Gostaria de obter algumas informações sobre como pensar corretamente sobre encerramentos de C ++ 11 e std::function
em termos de como eles são implementados e como a memória é tratada.
Embora eu não acredite em otimização prematura, tenho o hábito de considerar cuidadosamente o impacto de minhas escolhas no desempenho enquanto escrevo um novo código. Eu também faço uma boa quantidade de programação em tempo real, por exemplo, em microcontroladores e para sistemas de áudio, onde as pausas não determinísticas de alocação / desalocação de memória devem ser evitadas.
Portanto, gostaria de desenvolver um melhor entendimento de quando usar ou não lambdas C ++.
Meu entendimento atual é que um lambda sem encerramento capturado é exatamente como um retorno de chamada C. No entanto, quando o ambiente é capturado por valor ou por referência, um objeto anônimo é criado na pilha. Quando um fechamento de valor deve ser retornado de uma função, ele o envolve std::function
. O que acontece com a memória de fechamento neste caso? É copiado da pilha para a pilha? Ele é liberado sempre que o std::function
é liberado, ou seja, é contado por referência como um std::shared_ptr
?
Imagino que, em um sistema de tempo real, eu pudesse configurar uma cadeia de funções lambda, passando B como um argumento de continuação para A, de modo que um pipeline de processamento A->B
seja criado. Nesse caso, os fechamentos A e B seriam alocados uma vez. Embora eu não tenha certeza se eles seriam alocados na pilha ou no heap. No entanto, em geral, isso parece seguro para uso em um sistema de tempo real. Por outro lado, se B construir alguma função lambda C, que retorna, a memória para C seria alocada e desalocada repetidamente, o que não seria aceitável para uso em tempo real.
Em pseudo-código, um loop DSP, que acho que será seguro em tempo real. Quero realizar o processamento do bloco A e, em seguida, B, onde A chama seu argumento. Ambas as funções retornam std::function
objetos, então f
será um std::function
objeto, onde seu ambiente é armazenado no heap:
auto f = A(B); // A returns a function which calls B
// Memory for the function returned by A is on the heap?
// Note that A and B may maintain a state
// via mutable value-closure!
for (t=0; t<1000; t++) {
y = f(t)
}
E um que eu acho que pode ser ruim para usar em código em tempo real:
for (t=0; t<1000; t++) {
y = A(B)(t);
}
E um onde eu acho que a memória da pilha é provavelmente usada para o fechamento:
freq = 220;
A = 2;
for (t=0; t<1000; t++) {
y = [=](int t){ return sin(t*freq)*A; }
}
No último caso, o encerramento é construído a cada iteração do loop, mas, ao contrário do exemplo anterior, é barato porque é como uma chamada de função, nenhuma alocação de heap é feita. Além disso, eu me pergunto se um compilador poderia "suspender" o encerramento e fazer otimizações in-line.
Isso está correto? Obrigado.
operator()
. Não há "levantamento" a ser feito, lambdas não são nada de especial. Eles são apenas um atalho para um objeto de função local.