Pergunta

Atualmente, estou trabalhando com software de rede.Ele tem uma classe principal, server, que obviamente representa uma instância do servidor.

Uma instância server pode enviar solicitações e o usuário é notificado da resposta por um retorno de chamada.

O código é assim:

class server
{
  public:
    typedef boost::function<void (int duration)> callback_func;

    void send_request(endpoint& ep, callback_func cb);
};

Agora, digamos que, como usuário, desejo que o callback saiba sobre a instância que o chamou, posso fazer o seguinte:

void mycallback(const server& sv, int duration) { ... }

server sv;
sv.send_request("localhost", boost::bind(&mycallback, boost::ref(sv), _1));

Mas eu me pergunto: há alguma sobrecarga fazendo isso?As chamadas para mycallback serão mais lentas do que usar uma chamada "normal"?

Obrigado.

Nota de rodapé: eu poderia, é claro, alterar meu typedef para: typedef boost::function<void (const server& sv, int duration)> callback_func; e se boost::bind causar qualquer sobrecarga significativa, é provavelmente o que farei no final.Gostaria apenas de saber quais custos implicam o uso de boost::bind.

Foi útil?

Solução

Claro que causa sobrecarga.O que ele faz é criar um functor que armazena os parâmetros associados e tem um operator() que você chama com os argumentos restantes.Agora, isso é significativo?Eu não sei, porque isso é apenas uma palavra.Faça 10 milhões de solicitações e meça.Você é o único que pode dizer se essa sobrecarga é significativa para você ou não.

Além disso, enfrentei um problema semelhante.Como eu realmente não precisava de delegados, poderia me safar com ponteiros de função.Mas eu encontrei um benchmark interessante que também menciona algumas implementações alternativas, todas commelhor desempenho do que boost::function.Isso tem o custo de portabilidade e, em alguns casos, hacks feios e não padrão na implementação (mas um desempenho realmente bom em troca).

Outras dicas

boost::bind gera um objeto funcional que pode ser otimizado se for usado como argumento para modelos de função, mas boost::function impede essa otimização, assim como passar um ponteiro para uma função impedirá seu inlining.o próprio boost::function não precisa introduzir mais sobrecarga do que uma função virtual all ou chamar por meio de um ponteiro para a função.

PS.Eu concordo com Tamás Szelei: faça 10 milhões de solicitações e meça.

Em comparação com uma chamada de função normal, você está pagando por duas indireções para a chamada de função, que é semelhante à sobrecarga de uma chamada de função virtual (não desvirtualizada).

A primeira indireção é devido ao apagamento de tipo que acontece em boost :: function (isto é documentado na documentação boost::function).Este realmente não pode ser otimizado e você teria a mesma penalidade com um ponteiro de função simples também.

A segunda indireção vem da chamada da função mycallback por meio de um ponteiro de função.Um compilador muito bem otimizado pode descobrir e otimizar isso, mas um compilador normal não.Você pode se livrar dessa indireção (em todos os compiladores) se transformar mycallback em um objeto de função:

Em vez de

void mycallback( .1. ) { .2. }

você faz

struct mycallback {
    void operator()( .1. ) const { .2. }
};

Esteja ciente de que boost :: bind causa uma cópia do functor.Isso pode ser muito significativo.Para meu sistema operacional, notícias e exclusões são muito caras.Veja a documentação boost :: bind sobre ref () e cref ().Acredito que funções-membro também causam cópias do functor, mas não há documentação sobre isso.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top