Boost :: bind causa un sovraccarico?
Domanda
Attualmente sto lavorando su software di rete.Ha una classe principale, server
che ovviamente rappresenta un'istanza del server.
Un'istanza server
può inviare richieste e l'utente è informato della risposta da parte di un callback.
Il codice è come:
class server
{
public:
typedef boost::function<void (int duration)> callback_func;
void send_request(endpoint& ep, callback_func cb);
};
.
Ora diciamo che, come utente che voglio il callback per sapere sull'istanza che lo ha chiamato, posso fare la seguente cosa:
void mycallback(const server& sv, int duration) { ... }
server sv;
sv.send_request("localhost", boost::bind(&mycallback, boost::ref(sv), _1));
.
Ma mi chiedo: c'è un overhead lo fa?Le chiamate a mycallback
saranno più lenti rispetto all'utilizzo di una chiamata "normale"?
Grazie.
Foot Nota: Potrei ovviamente cambiare il mio typedef
a: typedef boost::function<void (const server& sv, int duration)> callback_func;
e se boost::bind
causi un sovraccarico significativo, questo è probabilmente quello che farò alla fine.Vorrei solo sapere quali costi implica l'uso di boost::bind
.
Soluzione
Ovviamente causa un sovraccarico.Quello che fa è che crea un funtore che memorizza i parametri associati e ha un operator()
che chiami con gli argomenti rimanenti.Ora, è significativo?Non lo so, perché è solo una parola.Fai 10 milioni di richieste e misurale.Sei l'unico che può dire se questo overhead è significativo per te o no.
Inoltre, ho riscontrato un problema simile.Dato che non avevo davvero bisogno di delegati, potevo farla franca con i puntatori a funzione.Ma ho trovato un interessante benchmark che menziona anche alcune implementazioni alternative, tutte conprestazioni migliori rispetto a boost::function
.Ciò va a scapito della portabilità e in alcuni casi di brutti hack non standard nell'implementazione (ma in cambio di una prestazione davvero buona).
Altri suggerimenti
boost::bind
genera un oggetto funzionale che può essere ottimizzato se utilizzato come argomento per i modelli di funzione, ma boost::function
impedisce questa ottimizzazione, proprio come il passaggio di un puntatore a una funzione ne impedisce l'inlining.boost::function
stesso non deve introdurre più overhead di una funzione virtuale all o chiamare tramite un puntatore alla funzione.
PS.Sono d'accordo con Tamás Szelei: fai 10 milioni di richieste e misurale.
Rispetto a una normale chiamata di funzione, stai pagando due indirette per la chiamata di funzione, che è simile al sovraccarico di una chiamata di funzione virtuale (non devirtualizzata).
Il primo indiretto è dovuto alla cancellazione del tipo che avviene in boost :: function (questo è documentato nella documentazione boost::function
).Questo non può essere ottimizzato e avresti la stessa penalità anche con un puntatore a funzione nudo.
Il secondo riferimento indiretto deriva dalla chiamata alla funzione mycallback
tramite un puntatore a funzione.Un compilatore molto ben ottimizzato può capirlo e ottimizzarlo, ma un normale compilatore no.Puoi sbarazzarti di questo riferimento indiretto (in tutti i compilatori) se trasformi mycallback
in un oggetto funzione:
Invece di
void mycallback( .1. ) { .2. }
lo fai
struct mycallback {
void operator()( .1. ) const { .2. }
};
Tieni presente che boost :: bind causa una copia del funtore.Questo può essere molto significativo.Per il mio sistema operativo le notizie e le cancellazioni sono molto costose.Vedere la documentazione boost :: bind su ref () e cref ().Credo che le funzioni membro causino anche copie del funtore, ma non c'è documentazione al riguardo.