Às vezes, alega-se que o C ++ 11/14 pode proporcionar um aumento de desempenho, mesmo quando apenas compilando o código C ++ 98. A justificativa é geralmente na linha da semântica de movimentação, pois em alguns casos os construtores rvalue são gerados automaticamente ou agora fazem parte do STL. Agora, estou me perguntando se esses casos já foram realmente tratados pelo RVO ou otimizações de compilador semelhantes.
Minha pergunta então é se você poderia me dar um exemplo real de um pedaço de código C ++ 98 que, sem modificação, roda mais rápido usando um compilador que suporta os novos recursos de linguagem. Entendo que um compilador em conformidade padrão não é necessário para executar a cópia e, por esse motivo, a semântica do movimento pode gerar velocidade, mas eu gostaria de ver um caso menos patológico, se você desejar.
EDIT: Apenas para deixar claro, não estou perguntando se os novos compiladores são mais rápidos que os antigos, mas se existe um código pelo qual a adição de -std = c ++ 14 aos meus sinalizadores do compilador funcionaria mais rapidamente (evite cópias, mas se você pode inventar qualquer outra coisa além da semântica de movimentos, eu também estaria interessado)
fonte
std::move
e movimentação de construtores (o que exigiria modificações no código existente). A única coisa realmente relacionada à minha pergunta foi a frase "Você obtém vantagens imediatas de velocidade simplesmente recompilando", que não é apoiada por nenhum exemplo (ela menciona STL no mesmo slide, como fiz na minha pergunta, mas nada específico ) Eu estava pedindo alguns exemplos. Se estiver lendo os slides incorretamente, informe-me.Respostas:
Estou ciente de 5 categorias gerais em que a recompilação de um compilador C ++ 03, pois o C ++ 11 pode causar aumentos ilimitados de desempenho que praticamente não têm relação com a qualidade da implementação. Todas essas são variações da semântica de movimentação.
std::vector
realocarcada vez que o
foo
da memória intermédia é realocada em C ++ 03 que copiados cadavector
embar
.No C ++ 11, ele move os
bar::data
s, que são basicamente livres.Nesse caso, isso depende de otimizações dentro do
std
contêinervector
. Em todos os casos abaixo, o uso destd
contêineres é apenas porque eles são objetos C ++ que possuemmove
semântica eficiente no C ++ 11 "automaticamente" quando você atualiza seu compilador. Objetos que não o bloqueiam e que contêm umstd
contêiner também herdam o aprimoramento automáticomove
construtores .Falha no NRVO
Quando o NRVO (otimização do valor de retorno nomeado) falha, no C ++ 03 ele volta à cópia, no C ++ 11 volta ao movimento. As falhas do NRVO são fáceis:
ou até:
Temos três valores - o valor de retorno e dois valores diferentes dentro da função. O Elision permite que os valores dentro da função sejam 'mesclados' com o valor de retorno, mas não entre si. Ambos não podem ser mesclados com o valor de retorno sem mesclar um ao outro.
A questão básica é que a elisão do NRVO é frágil e o código com alterações fora do
return
site pode repentinamente ter reduções de desempenho maciças naquele local sem diagnóstico emitido. Na maioria dos casos de falha de NRVO, o C ++ 11 termina com amove
, enquanto o C ++ 03 termina com uma cópia.Retornando um Argumento de Função
Elision também é impossível aqui:
no C ++ 11 isso é barato: no C ++ 03 não há como evitar a cópia. Argumentos para funções não podem ser elididos com o valor retornado, porque a vida útil e a localização do parâmetro e o valor retornado são gerenciados pelo código de chamada.
No entanto, o C ++ 11 pode passar de um para o outro. (Em um exemplo de menos brinquedo, algo pode ser feito para o
set
).push_back
ouinsert
Finalmente, a elisão em contêineres não acontece: mas o C ++ 11 sobrecarrega o rvalue move operadores de inserção, o que salva cópias.
no C ++ 03, um temporário
whatever
é criado e, em seguida, copiado para o vetorv
. 2std::string
buffers são alocados, cada um com dados idênticos, e um é descartado.No C ++ 11, um temporário
whatever
é criado. Awhatever&&
push_back
sobrecargamove
é temporária no vetorv
. Umstd::string
buffer é alocado e movido para o vetor. Um vaziostd::string
é descartado.Tarefa
Roubado da resposta de @ Jarod42 abaixo.
A elisão não pode ocorrer com a atribuição, mas a mudança pode.
aqui
some_function
retorna um candidato para o qual fugir, mas como ele não é usado para construir um objeto diretamente, ele não pode ser escolhido. No C ++ 03, o acima resulta no conteúdo do temporário sendo copiadosome_value
. No C ++ 11, ele é movido parasome_value
, o que basicamente é gratuito.Para o efeito completo do exposto, você precisa de um compilador que sintetize construtores de movimento e atribuição para você.
O MSVC 2013 implementa os construtores de movimentação em
std
contêineres, mas não sintetiza os construtores de movimentação nos seus tipos.Portanto, tipos contendo
std::vector
s e similares não obtêm essas melhorias no MSVC2013, mas começarão a obtê-los no MSVC2015.clang e gcc já implementaram construtores de movimentação implícitos. O compilador de 2013 da Intel oferecerá suporte à geração implícita de construtores de movimentação, se você passar
-Qoption,cpp,--gen_move_operations
(eles não fazem isso por padrão em um esforço para serem compatíveis com o MSVC2013).fonte
std
contêineres da biblioteca serão atualizados com osmove
construtores "de graça" e (se você não o bloqueou) construções que usam os referidos objetos ( e os referidos objetos) começarão a obter a construção de movimentação livre em várias situações. Muitas dessas situações são cobertas pela elision no C ++ 03: nem todas.std
contêineres acima se deve principalmente ao fato de serem baratos demais para se mover para copiar o tipo que você recebe 'de graça' no C ++ 11 ao recompilar o C ++ 03. Avector::resize
é uma exceção: ele usamove
em C ++ 11.se você tiver algo como:
Você obteve uma cópia no C ++ 03, enquanto uma atribuição de movimentação no C ++ 11. então você tem otimização gratuita nesse caso.
fonte
foo().swap(v);