Czy lepiej jest używać std :: memcpy () czy std :: copy () ze względu na wydajność?


163

Czy lepiej jest używać, memcpyjak pokazano poniżej, czy lepiej jest używać std::copy()pod względem wydajności? Czemu?

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

Należy pamiętać, że charmoże być podpisane lub niepodpisane, w zależności od implementacji. Jeśli liczba bajtów może być> = 128, użyj unsigned chardla tablic bajtów. ( (int *)Obsada też byłaby bezpieczniejsza (unsigned int *).)
Dan Breslau

13
Dlaczego nie używasz std::vector<char>? Albo skoro mówisz bits, std::bitset?
GManNickG,

2
Właściwie, czy mógłbyś mi wyjaśnić, co to (int*) copyMe->bits[0]robi?
user3728501

4
Nie jestem pewien, dlaczego coś, co wydaje się takim bałaganem z tak niewielkim kontekstem, było na +81, ale hej. @ user3728501 Domyślam się, że początek bufora intdyktuje jego rozmiar, ale wydaje się, że jest to przepis na katastrofę zdefiniowaną w implementacji, jak wiele innych rzeczy tutaj.
underscore_d

2
W rzeczywistości to (int *)rzutowanie jest po prostu czystym niezdefiniowanym zachowaniem, a nie zdefiniowanym w implementacji. Próba wykonania punningu za pomocą rzutowania narusza ścisłe zasady aliasingu i dlatego jest całkowicie nieokreślona przez Standard. (Również w C ++, chociaż nie w C, nie możesz pisać kalambur za pomocą unionalbo.) Prawie jedynym wyjątkiem jest sytuacja, gdy konwertujesz na wariant char*, ale dodatek nie jest symetryczny.
underscore_d

Odpowiedzi:


207

Mam zamiar sprzeciwić się ogólnej mądrości, która std::copyspowoduje niewielką, prawie niezauważalną utratę wydajności. Właśnie wykonałem test i stwierdziłem, że to nieprawda: zauważyłem różnicę w wydajności. Jednak zwycięzcą był std::copy.

Napisałem implementację C ++ SHA-2. W moim teście haszuję 5 ciągów przy użyciu wszystkich czterech wersji SHA-2 (224, 256, 384, 512) i wykonuję pętlę 300 razy. Czasy mierzę za pomocą Boost.timer. Ten licznik pętli 300 wystarczy, aby całkowicie ustabilizować moje wyniki. Przeprowadzałem test 5 razy, na przemian z memcpywersją i std::copywersją. Mój kod korzysta z przechwytywania danych w jak największej liczbie fragmentów (wiele innych implementacji działa z char/ char *, podczas gdy ja operuję z T/ T *(gdzie Tjest największym typem w implementacji użytkownika, który ma prawidłowe zachowanie przepełnienia), więc szybki dostęp do pamięci na największe typy, jakie mogę, mają kluczowe znaczenie dla wydajności mojego algorytmu. Oto moje wyniki:

Czas (w sekundach) do ukończenia testów 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%

Całkowity średni wzrost szybkości std :: copy over memcpy: 2,99%

Mój kompilator to gcc 4.6.3 w Fedorze 16 x86_64. Moje flagi optymalizacji to -Ofast -march=native -funsafe-loop-optimizations.

Kod dla moich implementacji SHA-2.

Postanowiłem również przeprowadzić test na mojej implementacji MD5. Wyniki były znacznie mniej stabilne, więc zdecydowałem się zrobić 10 biegów. Jednak po kilku pierwszych próbach otrzymałem wyniki, które różniły się znacznie od jednego uruchomienia do drugiego, więc domyślam się, że miała miejsce jakaś aktywność systemu operacyjnego. Postanowiłem zacząć od nowa.

Te same ustawienia i flagi kompilatora. Jest tylko jedna wersja MD5 i jest szybsza niż SHA-2, więc zrobiłem 3000 pętli na podobnym zestawie 5 ciągów testowych.

Oto moje 10 ostatnich wyników:

Czas (w sekundach) do ukończenia testów 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%

Całkowity średni spadek prędkości std :: copy over memcpy: 0,11%

Kod mojej implementacji MD5

Te wyniki sugerują, że istnieje pewna optymalizacja, którą std :: copy wykorzystałem w moich testach SHA-2, std::copyktórej nie można było użyć w moich testach MD5. W testach SHA-2 obie tablice zostały utworzone w tej samej funkcji, która wywołała std::copy/ memcpy. W moich testach MD5 jedna z tablic została przekazana do funkcji jako parametr funkcji.

Zrobiłem trochę więcej testów, aby zobaczyć, co mogę zrobić, aby std::copyznowu przyspieszyć. Odpowiedź okazała się prosta: włącz optymalizację czasu łącza. Oto moje wyniki z włączonym LTO (opcja -flto w gcc):

Czas (w sekundach) do zakończenia wykonywania testów MD5 z opcją -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%

Całkowity średni wzrost szybkości std :: kopia nad memcpy: 0,72%

Podsumowując, nie wydaje się, aby korzystanie z niego miało wpływ na wydajność std::copy. W rzeczywistości wydaje się, że nastąpił wzrost wydajności.

Wyjaśnienie wyników

Dlaczego więc miałby std::copyzwiększyć wydajność?

Po pierwsze, nie spodziewałbym się, że będzie wolniej w przypadku jakiejkolwiek implementacji, o ile włączona jest optymalizacja inliningu. Wszystkie kompilatory działają agresywnie; jest to prawdopodobnie najważniejsza optymalizacja, ponieważ umożliwia wiele innych optymalizacji. std::copymoże (i podejrzewam, że wszystkie implementacje w świecie rzeczywistym tak robią) wykryć, że argumenty można w prosty sposób skopiować, a pamięć jest ułożona sekwencyjnie. Oznacza to, że w najgorszym przypadku, kiedy memcpyjest to legalne, nie std::copypowinno działać gorzej. Prosta implementacja std::copytego odracza, memcpypowinna spełniać kryteria twojego kompilatora „zawsze wstaw to przy optymalizacji pod kątem szybkości lub rozmiaru”.

Jednak std::copyzachowuje również więcej swoich informacji. Kiedy dzwonisz std::copy, funkcja zachowuje typy nienaruszone. memcpydziała na void *, który odrzuca prawie wszystkie przydatne informacje. Na przykład, jeśli przekażę tablicę std::uint64_t, kompilator lub implementator biblioteki może być w stanie skorzystać z 64-bitowego wyrównania z std::copy, ale może to być trudniejsze do zrobienia z memcpy. Wiele implementacji algorytmów, takich jak ten, działa najpierw na części niewyrównanej na początku zakresu, następnie na części wyrównanej, a następnie na części bez wyrównania na końcu. Jeśli wszystko jest na pewno wyrównane, kod staje się prostszy i szybszy, a predyktor rozgałęzień w procesorze będzie łatwiejszy do poprawienia.

Przedwczesna optymalizacja?

std::copyjest w interesującej pozycji. Spodziewam się, że nigdy nie będzie wolniejszy, memcpya czasem szybszy w przypadku każdego nowoczesnego kompilatora optymalizującego. Co więcej, wszystko, co możesz memcpy, możesz std::copy. memcpynie pozwala na nakładanie się zderzaków, podczas gdy std::copypodpory zachodzą na siebie w jednym kierunku (z std::copy_backwardzachodzeniem na drugi kierunek). memcpydziała tylko na wskaźnikach, std::copydziała na każdej iteratorów ( std::map, std::vector, std::deque, lub mój własny niestandardowy typ). Innymi słowy, powinieneś używać go tylko std::copywtedy, gdy chcesz skopiować fragmenty danych.


35
Chcę podkreślić, że nie oznacza std::copyto, że jest to 2,99% lub 0,72% lub -0,11% szybsze niż memcpyte czasy dla całego programu. Jednak generalnie uważam, że testy porównawcze w prawdziwym kodzie są bardziej przydatne niż testy porównawcze w fałszywym kodzie. Mój cały program miał tę zmianę w szybkości wykonywania. Rzeczywiste efekty tylko dwóch schematów kopiowania będą miały większe różnice niż pokazano tutaj, gdy są rozpatrywane osobno, ale to pokazuje, że mogą mieć mierzalne różnice w rzeczywistym kodzie.
David Stone

2
Nie zgadzam się z twoimi ustaleniami, ale wyniki są wynikami: /. Jednak jedno pytanie (wiem, że to było dawno temu i nie pamiętasz badań, więc po prostu skomentuj tak, jak myślisz), prawdopodobnie nie zajrzałeś do kodu asemblera;
ST3

2
Moim zdaniem memcpyi std::copyma różne implementacje, więc w niektórych przypadkach kompilator optymalizuje otaczający kod i rzeczywisty kod kopiowania pamięci jako jeden integralny fragment kodu. Innymi słowy, czasami jeden jest lepszy od drugiego, a nawet innymi słowy, decyzja, który z nich zastosuje, jest przedwczesną, a nawet głupią optymalizacją, bo w każdej sytuacji trzeba zrobić nowe badania i co więcej, programy są zwykle rozwijane, więc po niektóre drobne zmiany mogą zostać utracone przewaga funkcji nad innymi.
ST3

3
@ ST3: Wyobrażam sobie, że w najgorszym przypadku std::copyjest to trywialna funkcja wbudowana, która wywołuje tylko memcpywtedy, gdy jest to legalne. Podstawowe podszycie wyeliminowałoby wszelkie negatywne różnice w wydajności. Zaktualizuję post z wyjaśnieniem, dlaczego std :: copy może być szybsze.
David Stone

7
Bardzo pouczająca analiza. Re Całkowity średni spadek szybkości std :: copy nad memcpy: 0,11% , chociaż liczba jest poprawna, wyniki nie są statystycznie istotne. 95% przedział ufności dla różnicy średnich wynosi (-0,013 s, 0,025), co obejmuje zero. Jak zauważyłeś, były różnice w stosunku do innych źródeł i Twoich danych, prawdopodobnie powiedziałbyś, że wydajność jest taka sama. Dla porównania, pozostałe dwa wyniki są istotne statystycznie - szanse, że przez przypadek zobaczysz różnicę w czasach tej skrajności, wynoszą około 1 na 100 milionów (pierwszy) i 1 na 20 000 (ostatni).
TooTone

78

Wszystkie kompilatory, które znam, zastępują prosty std::copyelement a, memcpygdy jest to odpowiednie, a nawet lepiej, wektoryzują kopię, aby była jeszcze szybsza niż memcpy.

W każdym razie: profiluj i przekonaj się sam. Różne kompilatory będą robić różne rzeczy i jest całkiem możliwe, że nie zrobią dokładnie tego, o co prosisz.

Zobacz tę prezentację na temat optymalizacji kompilatora (pdf).

Oto, co robi GCC dla prostego std::copytypu POD.

#include <algorithm>

struct foo
{
  int x, y;    
};

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

Oto demontaż (tylko z -Ooptymalizacją), pokazujący wywołanie 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

Jeśli zmienisz podpis funkcji na

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

następnie memmovestaje się a memcpydla niewielkiej poprawy wydajności. Zauważ, że memcpysam będzie silnie wektoryzowany.


1
Jak mogę profilować. Jakiego narzędzia użyć (w systemie Windows i Linux)?
user576670

5
@Konrad, masz rację. Ale memmovenie powinien być szybszy - raczej powinien być wolniejszy, ponieważ musi uwzględniać możliwość nakładania się dwóch zakresów danych. Myślę, że std::copyzezwala na nakładanie się danych, więc musi zadzwonić memmove.
Charles Salvia

2
@Konrad: Gdyby memmove było zawsze szybsze niż memcpy, wówczas memcpy wywoływałoby memmove. To, do czego std :: copy faktycznie może zostać wysłane (jeśli cokolwiek), jest zdefiniowane przez implementację, więc nie warto wspominać o szczegółach bez wspominania o implementacji.
Fred Nurk,

1
Chociaż prosty program do odtworzenia tego zachowania, skompilowany z -O3 pod GCC, pokazuje mi plik memcpy. To prowadzi mnie do przekonania, że ​​GCC sprawdza, czy pamięć się pokrywa.
jweyrich

1
@Konrad: standard std::copypozwala na nakładanie się w jednym kierunku, ale nie w drugim. Początek wyjścia nie może leżeć w zakresie wejściowym, ale początek wejścia może leżeć w zakresie wyjściowym. Jest to trochę dziwne, ponieważ kolejność przypisań jest zdefiniowana, a wywołanie może być UB, nawet jeśli efekt tych przypisań w tej kolejności jest zdefiniowany. Ale przypuszczam, że ograniczenie umożliwia optymalizację wektoryzacji.
Steve Jessop,

24

Zawsze należy używać std::copy, ponieważ memcpyogranicza się tylko w stylu C struktur POD, a kompilator może zastąpić wywołań std::copyze memcpyjeśli cele są w rzeczywistości POD.

Ponadto std::copymoże być używany z wieloma typami iteratorów, a nie tylko ze wskaźnikami. std::copyjest bardziej elastyczny, aby nie tracić wydajności i jest wyraźnym zwycięzcą.


Dlaczego warto kopiować iteratory?
Atmocreations

3
Nie kopiujesz iteratorów, ale raczej zakres zdefiniowany przez dwa iteratory. Na przykład std::copy(container.begin(), container.end(), destination);skopiuje zawartość container(wszystko pomiędzy begini end) do bufora wskazanego przez destination. std::copynie wymaga oszustw takich jak &*container.begin()lub &container.back() + 1.
David Stone

16

Teoretycznie memcpymoże mieć niewielką , niezauważalną , nieskończenie małą przewagę wydajności, tylko dlatego, że nie ma takich samych wymagań jak std::copy. Ze strony podręcznika man memcpy:

Aby uniknąć przepełnienia, rozmiar tablic wskazywanych przez parametry docelowe i źródłowe powinien wynosić co najmniej liczbę bajtów i nie powinien się pokrywać (w przypadku nakładających się bloków pamięci bezpieczniejszym podejściem jest memmove).

Innymi słowy, memcpymoże zignorować możliwość nakładania się danych. (Przekazywanie nakładających się tablic do memcpyjest niezdefiniowanym zachowaniem). memcpyNie trzeba więc jawnie sprawdzać tego warunku, natomiast std::copymożna go używać, o ile OutputIteratorparametr nie znajduje się w zakresie źródłowym. Pamiętaj, że to nie to samo, co stwierdzenie, że zakres źródłowy i docelowy nie mogą się pokrywać.

Więc ponieważ std::copyma nieco inne wymagania, teoretycznie powinien być nieco (z ekstremalnym naciskiem na nieco ) wolniejszy, ponieważ prawdopodobnie sprawdzi nakładające się tablice C lub deleguje kopiowanie tablic C do memmove, który musi wykonać czek. Ale w praktyce Ty (i większość osób zajmujących się profilowaniem) prawdopodobnie nie zauważysz żadnej różnicy.

Oczywiście, jeśli nie pracujesz z POD , i tak nie możesz z niego korzystać memcpy.


7
To prawda std::copy<char>. Ale std::copy<int>można założyć, że jego dane wejściowe są wyrównane wewnętrznie. To będzie o wiele większa różnica, ponieważ wpływa na każdy element. Nakładanie się to jednorazowa kontrola.
MSalters

2
@MSalters, prawda, ale większość implementacji memcpy, które widziałem, sprawdza wyrównanie i próbuje kopiować słowa, a nie bajt po bajcie.
Charles Salvia

1
std :: copy () może również ignorować nakładanie się pamięci. Jeśli chcesz obsługiwać nakładanie się pamięci, musisz sam napisać logikę wywołującą std :: reverse_copy () w odpowiednich sytuacjach.
Cygon

2
Istnieje odwrotny argument: podczas przechodzenia przez memcpyinterfejs traci informacje o wyrównaniu. W związku z tym memcpymusi wykonywać sprawdzanie wyrównania w czasie wykonywania, aby obsłużyć niewyrównane początki i końce. Te czeki mogą być tanie, ale nie są darmowe. Natomiast std::copymożna uniknąć tych kontroli i wektoryzować. Ponadto kompilator może udowodnić, że tablice źródłowe i docelowe nie nakładają się i ponownie wektoryzują bez konieczności wybierania przez użytkownika między memcpyi memmove.
Maxim Egorushkin

11

Moja zasada jest prosta. Jeśli używasz C ++, preferuj biblioteki C ++, a nie C :)


40
C ++ został specjalnie zaprojektowany, aby umożliwić używanie bibliotek C. To nie był wypadek. Często lepiej jest używać std :: copy niż memcpy w C ++, ale nie ma to nic wspólnego z tym, który z nich jest C, a tego rodzaju argument jest zwykle niewłaściwym podejściem.
Fred Nurk

2
@FredNurk Zwykle chcesz uniknąć słabego obszaru C, w którym C ++ zapewnia bezpieczniejszą alternatywę.
Phil1970,

@ Phil1970 Nie jestem pewien, czy C ++ jest w tym przypadku dużo bezpieczniejsze. Nadal musimy przekazywać prawidłowe iteratory, które nie przekraczają itp. Myślę, że używanie std::end(c_arr)zamiast tego c_arr + i_hope_this_is_the_right_number_of elementsjest bezpieczniejsze? a co ważniejsze, jaśniejsze. I to byłby punkt, na który kładę nacisk w tym konkretnym przypadku: std::copy()jest bardziej idiomatyczny, łatwiejszy w utrzymaniu, jeśli typy iteratorów zmieniają się później, prowadzi do jaśniejszej składni itp.
podkreślenie_d

1
@underscore_d std::copyjest bezpieczniejsze, ponieważ poprawnie kopiuje przekazane dane, jeśli nie są one typami POD. memcpyszczęśliwie skopiuje std::stringobiekt do nowej reprezentacji bajt po bajcie.
Jens

3

Tylko niewielki dodatek: różnica prędkości między memcpy()i std::copy()może się znacznie różnić w zależności od tego, czy optymalizacje są włączone, czy wyłączone. Z g ++ 6.2.0 i bez optymalizacji memcpy()wyraźnie wygrywa:

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

Gdy optymalizacje są włączone ( -O3), wszystko znowu wygląda prawie tak samo:

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

Im większa tablica, tym mniej zauważalny jest efekt, ale nawet przy N=1000 memcpy()jest około dwa razy szybciej, gdy optymalizacje nie są włączone.

Kod źródłowy (wymaga testu porównawczego Google):

#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
Mierzenie wydajności z wyłączonymi optymalizacjami jest ... cóż ... prawie bezcelowe ... Jeśli interesuje Cię wydajność, nie będziesz kompilować bez optymalizacji.
bolov

3
@bolov Nie zawsze. W niektórych przypadkach ważne jest, aby mieć stosunkowo szybki program debugowany.
Żołądź,

2

Jeśli naprawdę potrzebujesz maksymalnej wydajności kopiowania (której możesz nie mieć), nie używaj żadnego z nich .

Istnieje wiele , że można zrobić, aby zoptymalizować kopiowania pamięci - nawet więcej, jeśli jesteś gotów do korzystania z wielu wątków / rdzeni dla niego. Zobacz na przykład:

Czego brakuje / nie jest optymalne w tej implementacji memcpy?

zarówno pytanie, jak i niektóre odpowiedzi mają sugerowane implementacje lub linki do implementacji.


4
tryb pedanta: ze zwykłym zastrzeżeniem, że „ nie używaj żadnego z nich ” oznacza , że udowodnisz, że masz bardzo specyficzną sytuację / wymaganie, w przypadku których żadna z funkcji Standardowych dostarczonych przez Twoją implementację nie jest wystarczająco szybka ; w przeciwnym razie, moim zwykłym zmartwieniem jest to, że ludzie, którzy nie udowodnili, że są omijani przez przedwczesną optymalizację kopiowania kodu zamiast zwykle bardziej przydatnych części swojego programu.
underscore_d

-2

Profilowanie pokazuje, że stwierdzenie: std::copy()jest zawsze tak szybkie, jak memcpy()lub szybciej jest fałszywe.

Mój system:

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

gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2

Kod (język: 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 () profile: main: 21: now: 1422969084: 04859 elapsed: 2650 us
std :: copy () profile: main: 27: now: 1422969084: 04862 elapsed: 2745 us
memcpy () elapsed 44 s std :: copy ( ) upłynęło 45 s

g ++ -O3 -o test_stdcopy test_stdcopy.cpp

memcpy () profile: main: 21: now: 1422969601: 04939 elapsed: 2385 us
std :: copy () profile: main: 28: now: 1422969601: 04941 elapsed: 2690 us
memcpy () elapsed 27 s std :: copy ( ) upłynęło 43 s

Red Alert zwrócił uwagę, że kod używa memcpy z tablicy do tablicy i std :: copy z tablicy do wektora. To może być powodem do szybszego memcpy.

Skoro jest

v.reserve (sizeof (arr1));

nie będzie różnicy w kopiowaniu do wektora lub tablicy.

Kod jest ustawiony tak, aby używał tablicy w obu przypadkach. memcpy jeszcze szybciej:

{
    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
źle, twoje profilowanie pokazuje, że kopiowanie do tablicy jest szybsze niż kopiowanie do wektora. Poza tematem.
Red Alert

Mogę się mylić, ale w twoim poprawionym przykładzie, z memcpy, czy nie kopiujesz arr2 do arr1, podczas gdy ze std :: copy kopiujesz arr1 do arr2? ... Co możesz zrobić, to zrobić wiele, naprzemiennie eksperymenty (raz partia memcpy, raz partia std :: copy, potem znowu z memcopy, itd., wiele razy.). Następnie użyłbym clock () zamiast time (), ponieważ kto wie, co może robić twój komputer oprócz tego programu. Ale tylko moje dwa centy ... :-)
paercebal

7
Czyli przejście std::copyz wektora do tablicy memcpytrwało prawie dwa razy dłużej? Te dane są wysoce podejrzane. Skompilowałem twój kod przy użyciu gcc z -O3, a wygenerowany zestaw jest taki sam dla obu pętli. Zatem każda różnica w czasie, którą obserwujesz na swoim komputerze, jest tylko przypadkowa.
Red Alert
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.