205 votes

C ++ memcpy () vs std :: copy ()

Est-il préférable d'utiliser memcpy comme indiqué ci-dessous ou d'utiliser std :: copy () en termes de performances? Pourquoi?

 char *bits = NULL;
...

bits = new (std::nothrow) char[((int *) copyMe->bits)[0]];
if (bits == NULL)
{
    cout << "ERROR Not enough memory.\n";
    exit(1);
}

memcpy (bits, copyMe->bits, ((int *) copyMe->bits)[0]);
 

253voto

David Stone Points 3822

Je vais aller à l'encontre de la sagesse générale ici que std::copie aura une légère, presque imperceptible, la perte de performances. Je viens de faire un test et a constaté que pour être faux: je n'ai pas remarqué une différence de performance. Cependant, le gagnant a été std::copy.

J'ai écrit en C++ de l'algorithme SHA-2 mise en œuvre. Dans mon test, j'ai de hachage 5 chaînes à l'aide des quatre SHA-2 versions (224, 256, 384, 512), et je boucle de 300 fois. Je mesure le temps d'utilisation de Boost.minuterie. Que 300 compteur de boucle est assez complètement stabiliser mes résultats. J'ai couru à l'essai 5 fois chacun, en alternant entre les memcpy et la version std::version de la copie. Mon code prend avantage de l'accaparement des données en autant de morceaux que possible (de nombreuses autres implémentations de fonctionner avec des char / char *, tandis que je fonctionne avec T / T * (où T est le plus grand type de l'utilisateur mise en œuvre est correcte dépassement de comportement), si vite accès à la mémoire sur le plus grand des types que je peux est au cœur de la performance de mon algorithme. Voici mes résultats:

Temps (en secondes) pour terminer l'exécution de l'algorithme SHA-2 tests

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%

Total de l'augmentation moyenne de la vitesse de std::copie de memcpy: 2.99%

Mon compilateur est gcc 4.6.3 sur Fedora 16 x86_64. Mon optimisation des drapeaux -Ofast -march=native -funsafe-loop-optimizations.

Code pour ma SHA-2 implémentations.

J'ai décidé de lancer un test sur mon MD5 mise en œuvre. Les résultats ont été beaucoup moins stable, j'ai donc décidé de faire 10 séries. Cependant, après mes premières tentatives, j'ai obtenu des résultats qui varient fortement d'une exécution à l'autre, donc je suppose que il y avait une sorte de système d'exploitation, l'activité en cours. J'ai décidé de recommencer.

Même les paramètres du compilateur et des drapeaux. Il n'y a qu'une seule version de MD5, et il est plus rapide que l'algorithme SHA-2, donc je n'ai 3000 boucles sur un ensemble similaire de 5 chaînes de test.

Ce sont mes 10 derniers résultats:

Temps (en secondes) pour terminer l'exécution de MD5 tests

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%

Total de la valeur moyenne de la diminution de la vitesse de std::copie de memcpy: 0.11%

Code pour mon MD5 mise en œuvre

Ces résultats suggèrent qu'il y a de l'optimisation de la std::copy utilisé dans ma SHA-2 tests que std::copy ne pouvait pas l'utiliser dans mon MD5 tests. Dans le SHA-2 tests, les deux tableaux ont été créés dans la même fonction qui a appelé std::copy / memcpy. Dans mon MD5 tests, l'un des tableaux a été transmise à la fonction en tant que paramètre de la fonction.

J'ai un peu plus de tests pour voir ce que je pouvais faire pour que les std::copie plus rapide encore. La réponse s'est avéré être simple: tourner sur le lien ci-optimisation du temps. Ce sont mes résultats avec LTO activée (option -flto dans gcc):

Temps (en secondes) pour terminer l'exécution de MD5 tests avec -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%

Total de l'augmentation moyenne de la vitesse de std::copie de memcpy: 0.72%

En résumé, il ne semble pas être une perte de performance pour l'utilisation de std::copy. En fait, il semble y avoir un gain de performance.

85voto

Peter Alexander Points 31990

Tous les compilateurs je sais que pour remplacer un simple std::copy avec un memcpy lorsque c'est approprié, ou encore mieux, de la vectorisation de la copie, de sorte qu'il serait même plus rapide qu'un memcpy.

Dans tous les cas: profil et trouver par vous-même. Différents compilateurs faire des choses différentes, et c'est tout à fait possible de ne pas faire exactement ce que vous demandez.

Voir cette présentation sur les optimisations du compilateur (pdf).

Voici ce que GCC ne pour un simple std::copy d'un type POD.

#include <algorithm>

struct foo
{
  int x, y;    
};

void bar(foo* a, foo* b, size_t n)
{
  std::copy(a, a + n, b);
}

Voici le démontage ( -O optimisation), montrant l'appel à memmove:

bar(foo*, foo*, unsigned long):
    salq    $3, %rdx
    sarq    $3, %rdx
    testq   %rdx, %rdx
    je  .L5
    subq    $8, %rsp
    movq    %rsi, %rax
    salq    $3, %rdx
    movq    %rdi, %rsi
    movq    %rax, %rdi
    call    memmove
    addq    $8, %rsp
.L5:
    rep
    ret

Si vous modifiez la signature de la fonction de

void bar(foo* __restrict a, foo* __restrict b, size_t n)

puis l' memmove devient un memcpy pour une légère amélioration de la performance. Notez que memcpy lui-même sera fortement vectorisées.

28voto

Puppy Points 90818

Utilisez toujours std::copy car memcpy est limité aux seules structures POD de style C, et le compilateur remplacera probablement les appels à std::copy par memcpy si les cibles sont en réalité POD.

De plus, std::copy peut être utilisé avec de nombreux types d'itérateurs, pas seulement des pointeurs. std::copy est plus flexible sans perte de performance et en sort gagnant.

16voto

Charles Salvia Points 28661

En théorie, memcpy pourrait avoir une légère, imperceptible, infinitésimale, l'avantage de performance, seulement parce qu'il n'a pas les mêmes exigences qu' std::copy. À partir de la page de man de memcpy:

Pour éviter les débordements, la taille de la les tableaux de pointe à la fois la destination et les paramètres de la source, doit être d'au moins num octets, et ne doit pas chevauchement (chevauchement de mémoire blocs, memmove est une approche plus sûre).

En d'autres termes, memcpy peut ignorer la possibilité d'un chevauchement de données. (En passant de chevauchement des tableaux d' memcpy est un comportement indéfini.) Donc, memcpy n'a pas besoin explicitement vérifier cette condition, alors que std::copy peut être utilisé aussi longtemps que l' OutputIterator paramètre n'est pas dans la plage source. Notez que ce n'est pas la même chose que de dire que la source de la plage et de la plage de destination ne peuvent pas se chevaucher.

Donc, depuis std::copy a quelque peu différentes exigences, en théorie, il devrait être légèrement (avec un extrême l'accent sur les légèrement) plus lent, car il sera probablement vérifier le chevauchement de C-tableaux, ou d'autre délégué de la copie de C-tableaux d' memmove, qui a besoin pour effectuer la vérification. Mais dans la pratique, vous (et la plupart des profileurs) ne sera probablement même pas de déceler de différence.

Bien sûr, si vous ne travaillez pas avec des Gousses, vous ne pouvez pas utiliser memcpy de toute façon.

11voto

UmmaGumma Points 3154

Ma règle est simple. Si vous utilisez C ++, préférez les bibliothèques C ++ et non C :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X