-
Notifications
You must be signed in to change notification settings - Fork 81
Batching
FastClick is backward compatible with all vanilla element, and it should work out of the box with your own library. However Click may un-batch and re-batch packets along the path. This is not an issue for most slow path elements such as ICMP erros elements, where the development cost is not worth it. However, you probably want to have only batch-compatible elements in your fast path as this will be really faster.
Batch-compatible element should extend the BatchElement instead of the Element class. They also have to implement a version of push receiving a PacketBatch* argument instead of Packet* called push_batch. There is also a pull_batch function for pull elements.
The reason why batch element must provide a good old push fonction is that it may be not worth it to rebuild a batch before your element, and then unbatch-it because your element is betweem two vanilla elements. In this case the push version of your element will be used.
To let click compile with --disable-batch
, always enclose push_batch prototype
and implementation around #if HAVE_BATCH .. #endif
If your element must use batching, if only push_batch is implemented or your element always produces batches no matter the input, you will want to set batch_mode=BATCH_MODE_YES in the constructor, to let know the backward-compatibility manager that subsequent elements will receive batches, and previous element must send batch or let the backward compatibility manager rebuild a batch before passing it to your element. The default is BATCH_MODE_IFPOSSIBLE, telling that it should run in batch mode if it can, and vanilla element are fixed to BATCH_MODE_NO.
If you provide --enable-auto-batch
, the vanilla Elements will be set in mode
BATCH_MODE_IFPOSSIBLE, with a special push_batch function which will simply
call push() for each packets. However the push ports of the elements will
rebuild batches instead of letting them go through.
Without auto-batch, the batches will be un-batched before a vanilla Element and re-batched when hitting the next BatchElement. It is referenced as the "jump" mode as the batch "jump over" the vanilla Element. This is the behaviour described in the ANCS paper and still the default mode.
While you may implement push_batch and/or pull_batch for all elements, you should consider using class-based helpers or macros to avoid re-writing unnecessary code.
If your elements implements simple_action, and do not use push or pull directly (therefore it is purely functionnal) then it is as simple as changing the inheriting class :
class MyElement extends Element {
becomes :
class MyElement extends SimpleElement<MyElement> {
Actually, even if you're not into batching, CRTP templating will avoid one virtual call in non-batch mode and make your element faster. However, CRTP prevents further inheritance without some changes.
The CRTP is also available to accelerate a normal simple_action_batch :
class MyElement extends SimpleBatchElement<MyElement> {
...
PacketBatch simple_action_batch(PacketBatch* batch) {
//do something with batch and return it
}
Finally, if your element classifies packets, deciding an output (or drop) according to each packets, you can use the ClassifyElement helper. With this helper, one only needs to implement a int classify(Packet*);
function that returns an output index, that may be invalid (-1 or more than noutputs() will lead to dropping the packet). This will allow a very efficient implementation for both batch and non-batch mode, that do not add any virtual call. In batch mode, batches will be renconstructed before being sent.
class MyElement extends ClassifyElement<MyElement> {
...
int classify(Packet* p) {
if ( something ) {
return 0; //Send to output 0
} else
return -1; //Drop packet
}
These macros will help you process a batch.
A for loop that will iterate over a batch. Batch linking cannot be changed.
FOR_EACH_PACKET(batch,p)
//do something with p
A safer for-loop that allows modification of the batch (eg dropping packets)
FOR_EACH_PACKET_SAFE(batch,p)
//do something with p
Execute a function (possibly a lambda) for each packet. A new pointer may be returned by the function.
auto fnt = [](Packet* p){ return p-> uniqueify(); }:
EXECUTE_FOR_EACH_PACKET(fnt, batch);
In this version, fnt cannot return a null pointer. batch will become the result of the batch made out of all the packets returned by fnt.
Execute a function (possibly a lambda) for each packet. A new pointer, or a null pointer may be returned by the function.
auto fnt = [](Packet* p){ return p-> uniqueify(); }:
EXECUTE_FOR_EACH_PACKET_DROPPABLE(fnt, batch, [](Packet*){});
The last argument is a function to be executed when a packet is dropped.