Como posso ter certeza de que uma rotina está aproveitando o (N)RVO?
-
09-12-2019 - |
Pergunta
Gostaria de ter certeza de que minhas rotinas aproveitam (N)RVO sempre que possível.Além de analisar a desmontagem resultante, há algo que eu possa fazer ou verificar para ver se uma rotina está sendo compilada com (N)RVO?Neste ponto, estou mais interessado em MSVC e GCC.
Solução
Não, na verdade não.
No entanto, você pode seguir as orientações ao escrever seu código.
Otimização de valor de retorno sem nome
Isso é praticamente acionado toda vez que você retorna um valor temporário, mesmo no modo de depuração.
return MyObject(....);
Otimização do valor de retorno nomeado
Isso é praticamente acionado toda vez que a função sempre retorna o mesmo objeto temporário:
MyObject func() {
MyObject result;
if (...) { return result; }
result.push(0);
return result;
}
Você pode misturá-los, mas torna-se quase impossível para o compilador aplicar o RVO neste caso:
MyObject func() {
MyObject result;
if (...) { return MyObject(...); }
return result;
}
Aqui, é provável que um retorno se beneficie da RVO e o outro não.E eu apostaria que o primeiro seria otimizado porque você ficaria preso se criasse especulativamente result
no slot de retorno e de repente precisa pegar o if
filial.Observe que simplesmente reordenar as instruções funciona:
MyObject func() {
if (...) { return MyObject(...); }
MyObject result;
return result;
}
Portanto, a regra geral para a NRVO é que não deve haver return
declaração entre a declaração de result
e a return result;
declaração que retorna qualquer coisa além de result
em si.
Se você seguir isso, você acumulará as probabilidades a seu favor.E então é apenas uma questão de revisão de código.
E você também facilita a leitura do seu código, já que não declara variáveis antes de saber que realmente precisa delas!
Outras dicas
Você pode adicionar métodos de depuração ao destruidor:
struct A
{
~A() { cout << "destructor called"; }
};
A foo()
{
A a;
return a;
}
Se o destruidor for chamado, provavelmente o RVO não foi aplicado.
As maneiras possíveis em que posso pensar são:
Implementar um mecanismo de contagem de referências em sua classe que monitore o número de instâncias criadas por meio da classe, algo parecido com um
shared_ptr
faz, desta forma você pode detectar cópias extras de sua classe sendo criadas e removidas se a elisão de cópia não estiver acontecendo.Você poderia simplesmente colocar rastreamentos de depuração no construtor e destruidor de cópia para sua classe; se a elisão de cópia não estiver acontecendo, você verá muitos rastreamentos de depuração sucessivos do construtor e do destruidor de cópias.