Vou contra a sabedoria geral aqui que std::copyterá uma perda de desempenho leve, quase imperceptível. Acabei de fazer um teste e achei que era falso: notei uma diferença de desempenho. No entanto, o vencedor foi std::copy.
Eu escrevi uma implementação C ++ SHA-2. No meu teste, fiz o hash de 5 strings usando todas as quatro versões do SHA-2 (224, 256, 384, 512) e faço loop 300 vezes. Eu medo os tempos usando o Boost.timer. Esse contador de 300 laços é suficiente para estabilizar completamente meus resultados. Eu executei o teste 5 vezes cada, alternando entre a memcpyversão e a std::copyversão. Meu código aproveita a captura de dados no maior número possível de partes (muitas outras implementações operam com char/ char *, enquanto eu opero com T/ T *(onde Té o maior tipo na implementação do usuário que tem o comportamento correto de estouro)), para um acesso rápido à memória no O maior número de tipos possíveis é essencial para o desempenho do meu algoritmo. Estes são os meus resultados:
Tempo (em segundos) para concluir a execução dos testes SHA-2
std::copy memcpy % increase
6.11 6.29 2.86%
6.09 6.28 3.03%
6.10 6.29 3.02%
6.08 6.27 3.03%
6.08 6.27 3.03%
Aumento médio total na velocidade de std :: copy over memcpy: 2.99%
Meu compilador é o gcc 4.6.3 no Fedora 16 x86_64. Meus sinalizadores de otimização são -Ofast -march=native -funsafe-loop-optimizations.
Código para minhas implementações SHA-2.
Decidi também executar um teste na minha implementação MD5. Os resultados foram muito menos estáveis, então decidi fazer 10 corridas. No entanto, após minhas primeiras tentativas, obtive resultados que variaram bastante de uma corrida para a outra, então acho que havia algum tipo de atividade do SO em andamento. Eu decidi começar de novo.
Mesmas configurações e sinalizadores do compilador. Existe apenas uma versão do MD5 e é mais rápida que o SHA-2, então eu fiz 3000 loops em um conjunto semelhante de 5 sequências de teste.
Estes são os meus 10 resultados finais:
Tempo (em segundos) para concluir a execução dos testes MD5
std::copy memcpy % difference
5.52 5.56 +0.72%
5.56 5.55 -0.18%
5.57 5.53 -0.72%
5.57 5.52 -0.91%
5.56 5.57 +0.18%
5.56 5.57 +0.18%
5.56 5.53 -0.54%
5.53 5.57 +0.72%
5.59 5.57 -0.36%
5.57 5.56 -0.18%
Redução média total na velocidade de std :: copy over memcpy: 0.11%
Código para minha implementação MD5
Esses resultados sugerem que há alguma otimização que std :: copy usada nos meus testes SHA-2 que std::copynão pôde ser usada nos meus testes MD5. Nos testes SHA-2, ambas as matrizes foram criadas na mesma função que chamou std::copy/ memcpy. Nos meus testes MD5, uma das matrizes foi passada para a função como um parâmetro de função.
Fiz um pouco mais de teste para ver o que eu poderia fazer para std::copyacelerar mais rapidamente. A resposta acabou sendo simples: ative a otimização do tempo do link. Estes são meus resultados com o LTO ativado (opção -flto no gcc):
Tempo (em segundos) para concluir a execução dos testes MD5 com -flto
std::copy memcpy % difference
5.54 5.57 +0.54%
5.50 5.53 +0.54%
5.54 5.58 +0.72%
5.50 5.57 +1.26%
5.54 5.58 +0.72%
5.54 5.57 +0.54%
5.54 5.56 +0.36%
5.54 5.58 +0.72%
5.51 5.58 +1.25%
5.54 5.57 +0.54%
Aumento médio total na velocidade de std :: copy over memcpy: 0,72%
Em resumo, não parece haver uma penalidade de desempenho pelo uso std::copy. De fato, parece haver um ganho de desempenho.
Explicação dos resultados
Então, por que pode std::copydar um impulso no desempenho?
Primeiro, eu não esperaria que fosse mais lento para qualquer implementação, desde que a otimização do inlining estivesse ativada. Todos os compiladores se alinham agressivamente; é possivelmente a otimização mais importante porque permite muitas outras otimizações. std::copyÉ possível (e suspeito que todas as implementações do mundo real) detectar que os argumentos são trivialmente copiáveis e que a memória é organizada em seqüência. Isso significa que, na pior das hipóteses, quando memcpyé legal, não std::copydeve ter desempenho pior. A implementação trivial std::copydisso adia memcpydeve atender aos critérios do seu compilador de "sempre alinhar isso ao otimizar velocidade ou tamanho".
No entanto, std::copytambém mantém mais informações. Quando você liga std::copy, a função mantém os tipos intactos. memcpyopera void *, que descarta quase todas as informações úteis. Por exemplo, se eu passar uma matriz de std::uint64_t, o compilador ou o implementador da biblioteca poderá tirar proveito do alinhamento de 64 bits com std::copy, mas pode ser mais difícil fazê-lo memcpy. Muitas implementações de algoritmos como esse funcionam primeiro trabalhando na parte não alinhada no início do intervalo, depois na parte alinhada e depois na parte não alinhada no final. Se tudo estiver garantido para estar alinhado, o código se tornará mais simples e rápido, e mais fácil para o preditor de ramificação do seu processador se corrigir.
Otimização prematura?
std::copyestá em uma posição interessante. Espero que nunca seja mais lento memcpye às vezes mais rápido com qualquer compilador de otimização moderno. Além disso, tudo o que você puder memcpy, você pode std::copy. memcpynão permite nenhuma sobreposição nos buffers, enquanto os std::copysuportes se sobrepõem em uma direção (com std::copy_backwardpara a outra direção de sobreposição). memcpysó funciona em ponteiros, std::copyfunciona em qualquer iteradores ( std::map, std::vector, std::deque, ou meu próprio tipo personalizado). Em outras palavras, você deve usar apenas std::copyquando precisar copiar blocos de dados.
charpode ser assinado ou não, dependendo da implementação. Se o número de bytes puder ser> = 128, useunsigned charpara suas matrizes de bytes. (O(int *)elenco seria mais seguro, como(unsigned int *), também.)