Dlaczego szybkość memcpy () dramatycznie spada co 4 KB?

Przetestowałem prędkośćmemcpy() zauważając prędkość spada dramatycznie na i * 4KB. Wynik jest następujący: oś Y to prędkość (MB / s), a oś X to rozmiar bufora dlamemcpy(), zwiększając z 1 KB do 2 MB. Podfigura 2 i podfigura 3 szczegółowo opisują część 1KB-150KB i 1KB-32KB.

Środowisko:

Procesor: Intel (R) Xeon (R) CPU E5620 @ 2,40 GHz

OS: 2.6.35-22-generic # 33-Ubuntu

Flagi kompilatora GCC: -O3 -msse4 -DINTEL_SSE4 -Wall -std = c99

Myślę, że musi to być związane z pamięciami podręcznymi, ale nie mogę znaleźć powodu z następujących przypadków nieprzyjaznych pamięci podręcznej:

Dlaczego mój program jest powolny podczas zapętlania dokładnie 8192 elementów?

Dlaczego transpozycja macierzy 512x512 jest znacznie wolniejsza niż transpozycja macierzy 513x513?

Ponieważ pogorszenie wydajności tych dwóch przypadków jest spowodowane przez nieprzyjazne pętle, które odczytują rozproszone bajty w pamięci podręcznej, marnując resztę przestrzeni linii pamięci podręcznej.

Oto mój kod:

void memcpy_speed(unsigned long buf_size, unsigned long iters){
    struct timeval start,  end;
    unsigned char * pbuff_1;
    unsigned char * pbuff_2;

    pbuff_1 = malloc(buf_size);
    pbuff_2 = malloc(buf_size);

    gettimeofday(&start, NULL);
    for(int i = 0; i < iters; ++i){
        memcpy(pbuff_2, pbuff_1, buf_size);
    }   
    gettimeofday(&end, NULL);
    printf("%5.3f\n", ((buf_size*iters)/(1.024*1.024))/((end.tv_sec - \
    start.tv_sec)*1000*1000+(end.tv_usec - start.tv_usec)));
    free(pbuff_1);
    free(pbuff_2);
}
AKTUALIZACJA

Biorąc pod uwagę sugestie @usr, @ChrisW i @Leeor, powtórzyłem test dokładniej, a poniższy wykres pokazuje wyniki. Rozmiar bufora wynosi od 26KB do 38KB, a ja testowałem go co drugi 64B (26KB, 26KB + 64B, 26KB + 128B, ......, 38KB). Każdy test pętli 100 000 razy w ciągu około 0,15 sekundy. Ciekawostką jest to, że spadek występuje nie tylko dokładnie na granicy 4KB, ale pojawia się również w 4 * i + 2 KB, ze znacznie mniejszą amplitudą spadania.

PS

@Leeor zaproponował sposób na wypełnienie tej kropki, dodając między nimi bufor zastępczy o wielkości 2 KBpbuff_1 ipbuff_2. To działa, ale nie jestem pewien co do wyjaśnienia Leeora.

questionAnswers(3)

yourAnswerToTheQuestion