Ist es in Bezug auf die Leistung besser, std :: memcpy () oder std :: copy () zu verwenden?


163

Ist es besser, memcpywie unten gezeigt zu verwenden , oder ist es besser, std::copy()in Bezug auf die Leistung zu verwenden? Warum?

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]);

Beachten Sie, dass chardies je nach Implementierung signiert oder nicht signiert sein kann. Wenn die Anzahl der Bytes> = 128 sein kann, verwenden Sie sie unsigned charfür Ihre Byte-Arrays. (Die (int *)Besetzung wäre auch sicherer (unsigned int *).)
Dan Breslau

13
Warum benutzt du nicht std::vector<char>? Oder weil Sie sagen bits, std::bitset?
GManNickG

2
Könnten Sie mir bitte erklären, was das (int*) copyMe->bits[0]bedeutet?
user3728501

4
Ich bin mir nicht sicher, warum etwas, das wie ein Chaos mit so wenig wichtigem Kontext aussieht, bei +81 lag, aber hey. @ user3728501 Ich vermute, dass der Start des Puffers ein intDiktat seiner Größe enthält, aber das scheint ein Rezept für eine implementierungsdefinierte Katastrophe zu sein, wie so viele andere Dinge hier.
underscore_d

2
Tatsächlich ist diese (int *)Besetzung nur ein reines undefiniertes Verhalten, nicht implementierungsdefiniert. Der Versuch, Typ-Punning über eine Besetzung durchzuführen, verstößt gegen strenge Aliasing-Regeln und ist daher im Standard völlig undefiniert. (Auch in C ++, obwohl nicht C, können Sie kein Wortspiel über a unioneingeben.) Die einzige Ausnahme ist, wenn Sie in eine Variante von konvertieren char*, die Zulage jedoch nicht symmetrisch ist.
underscore_d

Antworten:


205

Ich werde hier gegen die allgemeine Weisheit verstoßen, std::copydie einen leichten, fast unmerklichen Leistungsverlust zur Folge haben wird. Ich habe gerade einen Test durchgeführt und festgestellt, dass dies nicht wahr ist: Ich habe einen Leistungsunterschied festgestellt. Der Gewinner war jedoch std::copy.

Ich habe eine C ++ SHA-2-Implementierung geschrieben. In meinem Test habe ich 5 Strings mit allen vier SHA-2-Versionen (224, 256, 384, 512) gehasht und 300-mal wiederholt. Ich messe Zeiten mit Boost.timer. Dieser 300-Schleifen-Zähler reicht aus, um meine Ergebnisse vollständig zu stabilisieren. Ich habe den Test jeweils 5 Mal ausgeführt und dabei zwischen der memcpyVersion und der std::copyVersion gewechselt . Mein Code nutzt die Möglichkeit, Daten in möglichst großen Blöcken abzurufen (viele andere Implementierungen arbeiten mit char/ char *, während ich mit T/ arbeite T *(wobei Tder größte Typ in der Implementierung des Benutzers das richtige Überlaufverhalten aufweist), sodass ein schneller Speicherzugriff auf die Die größten Typen, die ich kann, sind für die Leistung meines Algorithmus von zentraler Bedeutung. Dies sind meine Ergebnisse:

Zeit (in Sekunden), um den Lauf der SHA-2-Tests abzuschließen

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%

Durchschnittliche Geschwindigkeitssteigerung von std :: copy gegenüber memcpy: 2,99%

Mein Compiler ist gcc 4.6.3 unter Fedora 16 x86_64. Meine Optimierungsflags sind -Ofast -march=native -funsafe-loop-optimizations.

Code für meine SHA-2-Implementierungen.

Ich habe beschlossen, auch meine MD5-Implementierung zu testen. Die Ergebnisse waren viel weniger stabil, also entschied ich mich für 10 Läufe. Nach meinen ersten Versuchen erhielt ich jedoch Ergebnisse, die von Lauf zu Lauf sehr unterschiedlich waren. Ich vermute also, dass eine Art Betriebssystemaktivität im Gange war. Ich beschloss, von vorne zu beginnen.

Gleiche Compilereinstellungen und Flags. Es gibt nur eine Version von MD5 und diese ist schneller als SHA-2. Daher habe ich 3000 Schleifen mit einem ähnlichen Satz von 5 Testzeichenfolgen durchgeführt.

Dies sind meine letzten 10 Ergebnisse:

Zeit (in Sekunden), um die Ausführung der MD5-Tests abzuschließen

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%

Durchschnittliche Gesamtabnahme der Geschwindigkeit von std :: copy über memcpy: 0,11%

Code für meine MD5-Implementierung

Diese Ergebnisse legen nahe, dass es eine Optimierung gibt, die std :: copy in meinen SHA-2-Tests std::copyverwendet hat und die in meinen MD5-Tests nicht verwendet werden konnte. In den SHA-2-Tests wurden beide Arrays in derselben Funktion erstellt, die std::copy/ aufgerufen hat memcpy. In meinen MD5-Tests wurde eines der Arrays als Funktionsparameter an die Funktion übergeben.

Ich habe ein bisschen mehr getestet, um zu sehen, was ich tun kann, um std::copywieder schneller zu werden. Die Antwort stellte sich als einfach heraus: Aktivieren Sie die Optimierung der Verbindungszeit. Dies sind meine Ergebnisse bei aktiviertem LTO (Option -flto in gcc):

Zeit (in Sekunden), um die Ausführung der MD5-Tests mit -flto abzuschließen

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%

Durchschnittliche Geschwindigkeitssteigerung von std :: copy gegenüber memcpy: 0,72%

Zusammenfassend scheint es keine Leistungseinbußen für die Verwendung zu geben std::copy. Tatsächlich scheint es einen Leistungsgewinn zu geben.

Erklärung der Ergebnisse

Warum also std::copyeinen Leistungsschub geben?

Erstens würde ich nicht erwarten, dass es für eine Implementierung langsamer wird, solange die Optimierung des Inlining aktiviert ist. Alle Compiler inline aggressiv; Dies ist möglicherweise die wichtigste Optimierung, da sie so viele andere Optimierungen ermöglicht. std::copykann (und ich vermute, dass alle Implementierungen in der realen Welt dies tun) erkennen, dass die Argumente trivial kopierbar sind und dass der Speicher nacheinander angeordnet ist. Dies bedeutet, dass im schlimmsten Fall, wenn dies memcpylegal std::copyist, keine schlechtere Leistung erzielt werden sollte. Die triviale Implementierung std::copydass aufschiebt zu memcpysollten Sie Ihre Compiler Kriterien „immer inline dies , wenn für Geschwindigkeit oder Größe zu optimieren“ erfüllen.

Hält jedoch std::copyauch mehr von seinen Informationen. Wenn Sie aufrufen std::copy, behält die Funktion die Typen bei. memcpyarbeitet weiter void *, wodurch fast alle nützlichen Informationen verworfen werden. Wenn ich beispielsweise ein Array von übergebe std::uint64_t, kann der Compiler oder Bibliotheksimplementierer möglicherweise die 64-Bit-Ausrichtung mit nutzen std::copy, dies ist jedoch möglicherweise schwieriger memcpy. Viele Implementierungen solcher Algorithmen funktionieren, indem zuerst der nicht ausgerichtete Teil am Anfang des Bereichs, dann der ausgerichtete Teil und dann der nicht ausgerichtete Teil am Ende bearbeitet werden. Wenn garantiert ist, dass alles ausgerichtet ist, wird der Code einfacher und schneller und für den Verzweigungsprädiktor in Ihrem Prozessor einfacher zu korrigieren.

Vorzeitige Optimierung?

std::copyist in einer interessanten Position. Ich erwarte, dass es memcpymit keinem modernen Optimierungs-Compiler langsamer und manchmal schneller wird. Darüber hinaus können Sie alles, was memcpySie können std::copy. memcpyerlaubt keine Überlappung in den Puffern, wohingegen std::copyStützen in einer Richtung überlappen (mit std::copy_backwardfür die andere Überlappungsrichtung). memcpyfunktioniert nur auf Zeiger, std::copyarbeitet auf allen Iteratoren ( std::map, std::vector, std::deque, oder meine eigenen benutzerdefinierten Typ). Mit anderen Worten, Sie sollten nur verwenden, std::copywenn Sie Datenblöcke kopieren müssen.


35
Ich möchte betonen, dass dies nicht bedeutet, dass std::copy2,99% oder 0,72% oder -0,11% schneller sind als memcpydiese Zeiten, in denen das gesamte Programm ausgeführt wird. Im Allgemeinen bin ich jedoch der Meinung, dass Benchmarks in echtem Code nützlicher sind als Benchmarks in gefälschtem Code. Mein gesamtes Programm hat diese Änderung in der Ausführungsgeschwindigkeit bekommen. Die tatsächlichen Auswirkungen nur der beiden Kopierschemata weisen größere Unterschiede auf als hier gezeigt, wenn sie isoliert betrachtet werden. Dies zeigt jedoch, dass sie messbare Unterschiede im tatsächlichen Code aufweisen können.
David Stone

2
Ich möchte Ihren Ergebnissen nicht zustimmen, aber die Ergebnisse sind Ergebnisse: /. Bei einer Frage (ich weiß, dass es lange her ist und Sie sich nicht an Recherchen erinnern, also kommentieren Sie einfach so, wie Sie denken) haben Sie sich wahrscheinlich nicht mit Assembler-Code befasst.
ST3

2
Meiner Meinung nach memcpyund std::copyhat unterschiedliche Implementierungen, so dass der Compiler in einigen Fällen den umgebenden Code und den tatsächlichen Speicherkopiecode als einen integralen Code optimiert. Mit anderen Worten, manchmal ist eines besser als das andere, und mit anderen Worten, die Entscheidung, welche verwendet werden soll, ist verfrüht oder sogar dumm, weil in jeder Situation neue Forschung betrieben werden muss und darüber hinaus normalerweise Programme entwickelt werden, also danach Einige geringfügige Änderungen des Funktionsvorteils gegenüber anderen können verloren gehen.
ST3

3
@ ST3: Ich würde mir vorstellen, dass es sich im schlimmsten Fall std::copyum eine triviale Inline-Funktion handelt, die nur aufruft, memcpywenn sie legal ist. Grundlegendes Inlining würde negative Leistungsunterschiede beseitigen. Ich werde den Beitrag mit einer Erklärung aktualisieren, warum std :: copy möglicherweise schneller ist.
David Stone

7
Sehr informative Analyse. Re Durchschnittliche Gesamtabnahme der Geschwindigkeit von std :: copy über memcpy: 0,11% , obwohl die Anzahl korrekt ist, sind die Ergebnisse statistisch nicht signifikant. Ein 95% -Konfidenzintervall für die Mittelwertdifferenz beträgt (-0,013s, 0,025), einschließlich Null. Wie Sie betonten, gab es Abweichungen von anderen Quellen und bei Ihren Daten würden Sie wahrscheinlich sagen, dass die Leistung dieselbe ist. Als Referenz sind die beiden anderen Ergebnisse statistisch signifikant - die Wahrscheinlichkeit, dass Sie einen zufälligen Zeitunterschied sehen, liegt bei 1 zu 100 Millionen (zuerst) und 1 zu 20.000 (zuletzt).
TooTone

78

Alle Compiler, die ich kenne, ersetzen eine einfache std::copydurch eine, memcpywenn es angebracht ist, oder noch besser, die Kopie so zu vektorisieren, dass sie noch schneller als eine ist memcpy.

Auf jeden Fall: Profilieren und selbst herausfinden. Verschiedene Compiler werden unterschiedliche Dinge tun, und es ist durchaus möglich, dass sie nicht genau das tun, was Sie verlangen.

Siehe diese Präsentation zu Compiler-Optimierungen (pdf).

Hier ist, was GCC für einen einfachen std::copyPOD-Typ tut .

#include <algorithm>

struct foo
{
  int x, y;    
};

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

Hier ist die Demontage (nur mit -OOptimierung), die den Aufruf an Folgendes zeigt 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

Wenn Sie die Funktionssignatur in ändern

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

dann wird das memmovezu memcpyeiner leichten Leistungsverbesserung. Beachten Sie, dass memcpyselbst stark vektorisiert wird.


1
Wie kann ich ein Profiling durchführen? Welches Tool (unter Windows und Linux)?
user576670

5
@Konrad, du bist richtig. memmoveSollte aber nicht schneller sein, sondern etwas langsamer, da die Möglichkeit einer Überlappung der beiden Datenbereiche berücksichtigt werden muss. Ich denke, std::copyerlaubt überlappende Daten, und so muss es aufrufen memmove.
Charles Salvia

2
@Konrad: Wenn memmove immer schneller als memcpy wäre, würde memcpy memmove aufrufen. Was std :: copy tatsächlich versendet (wenn überhaupt), ist implementierungsdefiniert, daher ist es nicht sinnvoll, Einzelheiten zu erwähnen, ohne die Implementierung zu erwähnen.
Fred Nurk

1
Obwohl ein einfaches Programm zur Reproduktion dieses Verhaltens, das mit -O3 unter GCC kompiliert wurde, mir a zeigt memcpy. Es lässt mich glauben, dass GCC prüft, ob es eine Speicherüberlappung gibt.
Jweyrich

1
@Konrad: Standard std::copyerlaubt Überlappung in eine Richtung, aber nicht in die andere. Der Anfang der Ausgabe kann nicht innerhalb des Eingabebereichs liegen, aber der Anfang der Eingabe darf innerhalb des Ausgabebereichs liegen. Dies ist etwas seltsam, da die Reihenfolge der Zuweisungen definiert ist und ein Aufruf möglicherweise UB ist, obwohl die Auswirkung dieser Zuweisungen in dieser Reihenfolge definiert ist. Aber ich nehme an, die Einschränkung erlaubt Vektorisierungsoptimierungen.
Steve Jessop

23

Verwenden Sie immer std::copyda memcpyist begrenzt auf nur C-Stil POD Strukturen, und der Compiler wird wahrscheinlich Anrufe ersetzen , um std::copymit , memcpywenn die Ziele sind in der Tat POD.

Außerdem std::copykann es mit vielen Iteratortypen verwendet werden, nicht nur mit Zeigern. std::copyist flexibler ohne Leistungsverlust und ist der klare Gewinner.


Warum sollten Sie um Iteratoren herum kopieren wollen?
Atmocreations

3
Sie kopieren nicht die Iteratoren, sondern den von zwei Iteratoren definierten Bereich. Kopiert beispielsweise std::copy(container.begin(), container.end(), destination);den Inhalt von container(alles zwischen beginund end) in den durch angegebenen Puffer destination. std::copyerfordert keine Shenanigans wie &*container.begin()oder &container.back() + 1.
David Stone

16

Theoretisch memcpykönnte dies einen geringfügigen , nicht wahrnehmbaren , infinitesimalen Leistungsvorteil haben, nur weil es nicht die gleichen Anforderungen wie hat std::copy. Aus der Manpage von memcpy:

Um Überläufe zu vermeiden, muss die Größe der Arrays, auf die sowohl der Ziel- als auch der Quellparameter zeigen, mindestens num Byte betragen und darf sich nicht überlappen (bei überlappenden Speicherblöcken ist memmove ein sicherer Ansatz).

Mit anderen Worten, memcpykann die Möglichkeit überlappender Daten ignorieren. (Das Übergeben überlappender Arrays an memcpyist ein undefiniertes Verhalten.) Daher memcpymuss nicht explizit nach dieser Bedingung gesucht std::copywerden , während sie verwendet werden kann, solange sich der OutputIteratorParameter nicht im Quellbereich befindet. Beachten Sie, dass dies nicht gleichbedeutend ist mit der Aussage, dass sich Quell- und Zielbereich nicht überschneiden dürfen.

Also da std::copyhat etwas andere Anforderungen, in der Theorie sollte es sein , leicht (mit einem extremen Wert auf leicht ) langsamer, da es wahrscheinlich für überlappende C-Arrays überprüfen wird, oder delegiert das Kopieren von C-Arrays memmove, die das ausführen muss prüfen. In der Praxis werden Sie (und die meisten Profiler) wahrscheinlich nicht einmal einen Unterschied feststellen.

Wenn Sie nicht mit PODs arbeiten , können Sie diese natürlich memcpysowieso nicht verwenden .


7
Dies gilt für std::copy<char>. Aber std::copy<int>kann davon ausgehen , dass seine Eingänge sind int-ausgerichtet sind . Das wird einen weitaus größeren Unterschied machen, da es jedes Element betrifft. Überlappung ist eine einmalige Prüfung.
MSalters

2
@ MSalters, stimmt, aber die meisten Implementierungen von memcpyIch habe gesehen, wie die Ausrichtung überprüft wurde, und versucht, Wörter anstatt byteweise zu kopieren.
Charles Salvia

1
std :: copy () kann auch überlappende Speicher ignorieren. Wenn Sie überlappenden Speicher unterstützen möchten, müssen Sie die Logik selbst schreiben, um std :: reverse_copy () in den entsprechenden Situationen aufzurufen.
Cygon

2
Es gibt ein entgegengesetztes Argument: Beim Durchlaufen der memcpySchnittstelle gehen die Ausrichtungsinformationen verloren. Daher memcpymüssen zur Laufzeit Ausrichtungsprüfungen durchgeführt werden, um nicht ausgerichtete Anfänge und Enden zu verarbeiten. Diese Schecks mögen billig sein, sind aber nicht kostenlos. Während std::copykann diese Überprüfungen vermeiden und vektorisieren. Der Compiler kann auch beweisen, dass sich Quell- und Zielarrays nicht überlappen und erneut vektorisieren, ohne dass der Benutzer zwischen memcpyund wählen muss memmove.
Maxim Egorushkin

11

Meine Regel ist einfach. Wenn Sie C ++ verwenden, bevorzugen Sie C ++ - Bibliotheken und nicht C :)


40
C ++ wurde explizit entwickelt, um die Verwendung von C-Bibliotheken zu ermöglichen. Dies war kein Unfall. In C ++ ist es oft besser, std :: copy als memcpy zu verwenden, aber dies hat nichts damit zu tun, welches C ist, und diese Art von Argument ist normalerweise der falsche Ansatz.
Fred Nurk

2
@FredNurk Normalerweise möchten Sie einen schwachen Bereich von C vermeiden, in dem C ++ eine sicherere Alternative darstellt.
Phil1970

@ Phil1970 Ich bin mir nicht sicher, ob C ++ in diesem Fall viel sicherer ist. Wir müssen noch gültige Iteratoren übergeben, die nicht überlaufen usw. Ich denke, es ist sicherer, sie verwenden zu können, std::end(c_arr)anstatt sie zu verwenden c_arr + i_hope_this_is_the_right_number_of elements. und vielleicht noch wichtiger, klarer. Und das wäre der Punkt, den ich in diesem speziellen Fall hervorhole: std::copy()Ist idiomatischer, wartbarer, wenn sich die Typen der Iteratoren später ändern, führt zu einer klareren Syntax usw.
underscore_d

1
@underscore_d std::copyist sicherer, da die übergebenen Daten korrekt kopiert werden, falls es sich nicht um POD-Typen handelt. memcpykopiert gerne ein std::stringObjekt byteweise in eine neue Darstellung.
Jens

2

Nur eine kleine Ergänzung: Der Geschwindigkeitsunterschied zwischen memcpy()und std::copy()kann sehr unterschiedlich sein, je nachdem, ob Optimierungen aktiviert oder deaktiviert sind. Mit g ++ 6.2.0 und ohne Optimierungen memcpy()gewinnt eindeutig:

Benchmark             Time           CPU Iterations
---------------------------------------------------
bm_memcpy            17 ns         17 ns   40867738
bm_stdcopy           62 ns         62 ns   11176219
bm_stdcopy_n         72 ns         72 ns    9481749

Wenn Optimierungen aktiviert sind ( -O3), sieht alles wieder ziemlich gleich aus:

Benchmark             Time           CPU Iterations
---------------------------------------------------
bm_memcpy             3 ns          3 ns  274527617
bm_stdcopy            3 ns          3 ns  272663990
bm_stdcopy_n          3 ns          3 ns  274732792

Je größer das Array, desto weniger macht sich der Effekt bemerkbar, aber selbst bei N=1000 memcpy()ist er etwa doppelt so schnell, wenn keine Optimierungen aktiviert sind.

Quellcode (erfordert Google Benchmark):

#include <string.h>
#include <algorithm>
#include <vector>
#include <benchmark/benchmark.h>

constexpr int N = 10;

void bm_memcpy(benchmark::State& state)
{
  std::vector<int> a(N);
  std::vector<int> r(N);

  while (state.KeepRunning())
  {
    memcpy(r.data(), a.data(), N * sizeof(int));
  }
}

void bm_stdcopy(benchmark::State& state)
{
  std::vector<int> a(N);
  std::vector<int> r(N);

  while (state.KeepRunning())
  {
    std::copy(a.begin(), a.end(), r.begin());
  }
}

void bm_stdcopy_n(benchmark::State& state)
{
  std::vector<int> a(N);
  std::vector<int> r(N);

  while (state.KeepRunning())
  {
    std::copy_n(a.begin(), N, r.begin());
  }
}

BENCHMARK(bm_memcpy);
BENCHMARK(bm_stdcopy);
BENCHMARK(bm_stdcopy_n);

BENCHMARK_MAIN()

/* EOF */

18
Das Messen der Leistung bei deaktivierten Optimierungen ist ... na ja ... ziemlich sinnlos ... Wenn Sie an Leistung interessiert sind, werden Sie ohne Optimierungen nicht kompilieren.
Bolov

3
@bolov Nicht immer. In einigen Fällen ist es wichtig, ein relativ schnelles Debug-Programm zu haben.
Eichel

2

Wenn Sie wirklich maximale Kopierleistung benötigen (was Sie möglicherweise nicht tun), verwenden Sie keine von beiden .

Es kann viel getan werden, um das Kopieren des Speichers zu optimieren - noch mehr, wenn Sie bereit sind, mehrere Threads / Kerne dafür zu verwenden. Siehe zum Beispiel:

Was fehlt / ist nicht optimal in dieser memcpy-Implementierung?

Sowohl die Frage als auch einige der Antworten haben Implementierungen oder Links zu Implementierungen vorgeschlagen.


3
Pedant-Modus: Mit der üblichen Einschränkung, dass " keines von beiden verwenden " bedeutet, dass Sie nachgewiesen haben, dass Sie eine hochspezifische Situation / Anforderung haben, für die keine der von Ihrer Implementierung bereitgestellten Standardfunktionen schnell genug ist . Ansonsten ist meine übliche Sorge, dass Leute, die nicht bewiesen haben, dass sie davon abgelenkt werden, den Kopiercode vorzeitig zu optimieren, anstatt die normalerweise nützlicheren Teile ihres Programms.
underscore_d

-2

Die Profilerstellung zeigt diese Aussage: std::copy()ist immer so schnell wie memcpy()oder schneller ist falsch.

Mein System:

HP-Compaq-dx7500-Microtower 3.13.0-24-generic # 47-Ubuntu SMP Fr 2. Mai 23:30:00 UTC 2014 x86_64 x86_64 x86_64 GNU / Linux.

gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2

Der Code (Sprache: c ++):

    const uint32_t arr_size = (1080 * 720 * 3); //HD image in rgb24
    const uint32_t iterations = 100000;
    uint8_t arr1[arr_size];
    uint8_t arr2[arr_size];
    std::vector<uint8_t> v;

    main(){
        {
            DPROFILE;
            memcpy(arr1, arr2, sizeof(arr1));
            printf("memcpy()\n");
        }

        v.reserve(sizeof(arr1));
        {
            DPROFILE;
            std::copy(arr1, arr1 + sizeof(arr1), v.begin());
            printf("std::copy()\n");
        }

        {
            time_t t = time(NULL);
            for(uint32_t i = 0; i < iterations; ++i)
                memcpy(arr1, arr2, sizeof(arr1));
            printf("memcpy()    elapsed %d s\n", time(NULL) - t);
        }

        {
            time_t t = time(NULL);
            for(uint32_t i = 0; i < iterations; ++i)
                std::copy(arr1, arr1 + sizeof(arr1), v.begin());
            printf("std::copy() elapsed %d s\n", time(NULL) - t);
        }
    }

g ++ -O0 -o test_stdcopy test_stdcopy.cpp

memcpy () Profil: main: 21: now: 1422969084: 04859 abgelaufen: 2650 us
std :: copy () Profil: main: 27: now: 1422969084: 04862 abgelaufen: 2745 us
memcpy () abgelaufen 44 s std :: copy ( ) verstrichen 45 s

g ++ -O3 -o test_stdcopy test_stdcopy.cpp

memcpy () Profil: main: 21: now: 1422969601: 04939 abgelaufen: 2385 us
std :: copy () Profil: main: 28: now: 1422969601: 04941 abgelaufen: 2690 us
memcpy () abgelaufen 27 s std :: copy ( ) verstrichen 43 s

Red Alert wies darauf hin, dass der Code memcpy von Array zu Array und std :: copy von Array zu Vektor verwendet. Das könnte ein Grund für eine schnellere Erinnerung sein.

Da gibt es

v.reserve (sizeof (arr1));

Es darf keinen Unterschied in der Kopie zum Vektor oder Array geben.

Der Code ist so festgelegt, dass in beiden Fällen ein Array verwendet wird. memcpy noch schneller:

{
    time_t t = time(NULL);
    for(uint32_t i = 0; i < iterations; ++i)
        memcpy(arr1, arr2, sizeof(arr1));
    printf("memcpy()    elapsed %ld s\n", time(NULL) - t);
}

{
    time_t t = time(NULL);
    for(uint32_t i = 0; i < iterations; ++i)
        std::copy(arr1, arr1 + sizeof(arr1), arr2);
    printf("std::copy() elapsed %ld s\n", time(NULL) - t);
}

memcpy()    elapsed 44 s
std::copy() elapsed 48 s 

1
Falsch, Ihre Profilerstellung zeigt, dass das Kopieren in ein Array schneller ist als das Kopieren in einen Vektor. Off Topic.
Red Alert

Ich könnte mich irren, aber in Ihrem korrigierten Beispiel mit memcpy kopieren Sie nicht arr2 in arr1, während Sie mit std :: copy arr1 in arr2 kopieren? ... Sie könnten mehrere abwechselnd erstellen Experimente (einmal ein Stapel memcpy, einmal ein Stapel std :: copy, dann mehrmals mit memcopy usw. zurück). Dann würde ich clock () anstelle von time () verwenden, denn wer weiß, was Ihr PC zusätzlich zu diesem Programm tun könnte. Nur meine zwei Cent, obwohl ... :-)
paercebal

7
Der Wechsel std::copyvon einem Vektor zu einem Array dauert also memcpyfast doppelt so lange? Diese Daten sind sehr verdächtig. Ich habe Ihren Code mit gcc mit -O3 kompiliert, und die generierte Assembly ist für beide Schleifen gleich. Jeder Zeitunterschied, den Sie auf Ihrer Maschine beobachten, ist also nur zufällig.
Red Alert
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.