test szybkości pamięci

0

Robię takie coś:

void copy(double *d, double *s, int n)
{
 for(int i = 0; i < n/4; i+=4)
 { 
    d[i+0] = s[i+0];
    d[i+1] = s[i+1];
    d[i+2] = s[i+2];
    d[i+3] = s[i+3];
 }
}

#define N 0x1000000; // 16 MB
double *p = new double[2*N]; // 16 x 16 MB

for(int i = 0; i < 2*N; i+=2)
 {
   p[i] = randf(); // jakieś losowe liczby - tak dla rozrywki...
   p[i+1] = randf(); 
 }

int t = timer(); // czas w ms
copy(p, p+N, N)

t = timer() - t;

// szybkość w MB/s
int mps = N * 0.016 / t; // 16 = 2x8B 

i co to właściwie mierzy?

Mi z tego wychodzi ponad 20000, czyli szybkość ponad 20 GB/s !!!
co jest raczej niemożliwe, bo DDR3 1600MHz ma przepustowość tylko 12GB/s;

w dual mode 24 teoretycznie, no ale to przecież podwójnie nigdy nie pójdzie...
może w wersji wielowątkowej jedynie... choć i w to wątpię.

0

gdzie pogubiliście te tagi dla kodu?

1

Lekcja na dziś: CPU Cache oraz read-ahead.
Wyobraź sobie że żyjemy w roku 2017. Masz w swoim procku jakieś 3 levele cache do których CPU wczyta dane, te do których się w tej chwili odnosisz oraz te "dookoła" (zakładając że może też będą ci potrzebne). To działa szczególnie dobrze kiedy danych jest mało i kiedy masz dostęp sekwencyjny, po kolei. U ciebie oba warunki są spełnione więc odczyt z pamięci to ty tutaj masz na dobrą sprawę raptem kilka a potem to już tylko czytanie z cache. A nawe i te twoje odczyty z pamięci też są pewnie robione "przy okazji" przez "read ahead".

Jeśli chcesz serio widzieć odczyty z RAM to:

  1. Zrób duży bufor danych, coś co nie wejdzie ci w żadne cache. Powiedzmy kilka GB.
  2. Czytaj z losowych indeksów tablicy, odległych o więcej niż rozmiar cache dla twojego CPU.

Zalecałbym w ogóle w ramach w nauki puscić to parę razy z różnymi rozmiarami bufora -> tak żeby wszedł w level 1,2,3 cache i żeby wyszedł poza. I porównać czas działania i zobaczyć jak kolosalne znaczenie mają takie szczegóły na wydajność.

0

Żartujesz sobie chyba.

Tam jest: 16 x 16 MB = 256MB = 1/4 GB;
widziałeś cache o takich rozmiarach... w pecetach?

0

Zwykle to się testuje badając wydajność w zależności od wielkości bloku pamięci.
Zacznij od małych bloków rzędu 2kB, a potem je stopniowo zwiększaj (np. 2x).
Dla każdej wielkości zrób kilka uruchomień testu i wylicz średnią i odchylenie standardowe.

https://pl.wikipedia.org/wiki/Niepewno%C5%9B%C4%87_pomiaru

1

@Pelikan a ty umiesz czytać ze zrozumieniem? o_O cache wcale nie musi być takie duze skoro czytasz sekwencyjnie! W trakcie jak ty cośtam sobie wyliczasz na kolejnych indeksach w tablicy CPU sprytnie doczytuje "w tle" kolejne bloki pamięci do cache za pomocą read-ahead, więc w swojej pętli nigdy nie operujesz na pamięci tylko na zawartości cache i dodatkowo nigdy nie czekasz na odczyt z pamięci (no tylko za pierwszym razem) bo dane zawsze są juz w cache.

0
Shalom napisał(a):

@Pelikan a ty umiesz czytać ze zrozumieniem? o_O cache wcale nie musi być takie duze skoro czytasz sekwencyjnie! W trakcie jak ty cośtam sobie wyliczasz na kolejnych indeksach w tablicy CPU sprytnie doczytuje "w tle" kolejne bloki pamięci do cache za pomocą read-ahead, więc w swojej pętli nigdy nie operujesz na pamięci tylko na zawartości cache i dodatkowo nigdy nie czekasz na odczyt z pamięci (no tylko za pierwszym razem) bo dane zawsze są juz w cache.

Przecież to jest test szybkości pamięci - tej podstawowej RAM,
zatem to ładowanie do cache zaniży jedynie wynik, a nie odwrotnie,
bowiem każda dana jest tu używana tylko raz, co jest zupełnie sprzeczne z logiką cache.

Szybkość RAM poprzez cache < szybkość samego ram,
ponieważ ładowanie cache to tylko niepotrzebnie tracony czas,
w przypadku gdy czytam/zapisuję tylko raz dany fragment ramu!

6

Ty sobie chyba robisz jaja teraz jakieś :D :D
Czy ty w ogóle rozumiesz jaka jest idea cache? Odczytujesz dane SEKWENCYJNIE. Czyli masz np. 1GB danych w pamięci i lecisz po nich bajt po bajcie!
Cache ładuje się partiami. Masz np. 6MB cache L3, więc przy odczycie 1 bajta CPU sprytnie może wczytać sobie z RAM od razu 6MB do cache L3 przez read-ahead bo zakłada że będziesz odnosił się danych "obok" tego czego teraz używasz. Co wiecej robi to generalnie "w tle", więc nie czekasz na wczytanie całej zawartości cache nawet, tylko na odczytanie małego fragmentu na ktorym pracujesz, a reszta czyta się w międzyczasie.
I teraz jak ty robisz swój genialny odczyt z drugiej komórki pamieci to nie ma już żadnego odczytu z pamieci bo dane są już w cache! Więc lecą z cache właśnie. No i tak się przesuwasz po tym cache a CPU wykrywa że nadal idziesz sekwencyjnie i sprytnie usuwa z cache "stare" dane które już przeskoczyłeś i ładuje sobie w tle dane "przyszłe", których będziesz za chwilę używać...

zatem to ładowanie do cache zaniży jedynie wynik, a nie odwrotnie,

Niczego nie zaniży bo CPU zawsze i tak dane będzie czytał do cache a potem do rejestrów procesora. Co więcej zwykle prawdą jest że uzywasz danych leżących blisko siebie w pamięci, a odczyty z cache są rzędy wielkości szybsze niż z pamięci, więc tylko w bardzo nietypowych sytuacjach wczytanie danych do cache coś spowolni. Wczytanie na raz całego cache jest niewiele wolniejsze niż wczytanie 1 bajtu z pamięci, bo to dostęp do pamięci generuje większość spowolnienia.

Szybkość RAM poprzez cache < szybkość samego ram

To chyba tylko w architekturze pelikana tak jest.

Zalecam doczytać bo teraz tylko się błaźnisz. Przeczytaj ze zrozumieniem (!) https://people.freebsd.org/~lstewart/articles/cpumemory.pdf a potem wróć. Serio.

0

Bredzisz niemiłosiernie!

Jest instrukcja wyłączająca zupełnie ten cache,
i właśnie po żeby go można było ominąć zupełnie, zwiększać tym samy szybkość,
przy takim sekwencyjnym dostępie właśnie, bo wówczas czas ładowania cache odpada!

t_ram + tcach > t_ram

cache przyspiesza typowe algorytmy, znaczy gdy operujesz na niedużym fragmencie ramu,
a ponadto każda zmienna jest używana wielokrotnie, a nie tylko raz!

Wtedy masz tak:
pierwszy dostęp: t_ram + t_cach;

ale za to potem już masz tylko samo: t_cach przy kolejnych
dostępach do tej samej danej na której wykonujesz operacje, co w sumie daje mniejszy czas, bowiem:

2t_ram + nt_cache < n*t_ram; z uwagi na: t_ram >> t_cache;

ale pod warunkiem... chyba dla: n > 2 dopiero, gdzie n to liczba operacji typu zapis/odczyt.

2

Ok, napisz teraz program, który wyłączy cache (odpowiedni bit w rejestrze cr0 itd) i zrobi taki benchmark.

Na oko będzie od 2 do 100 razy wolniejszy, ale przynajmniej czegoś się nauczysz.

1

@Pelikan szkoda mi marnować czasu na kłótnie z kimś kto nie ma zielonego pojęcia o czym mówi. Zasugerowałem ci na samym początku w jaki sposób zasymulować cache miss i czytać cały czas z RAMu -> poprzez dostępy do oddalonych od siebie (o więcej niż rozmiar cache) obszarów pamięci. Rozumiem że tamten pdf był za długi to może to chociaż przeczytasz:
http://igoro.com/archive/gallery-of-processor-cache-effects/
albo:
https://mechanical-sympathy.blogspot.fr/2012/08/memory-access-patterns-are-important.html
Serio, przeczytaj cokolwiek na ten temat bo bredzisz.

0
Rev napisał(a):

Ok, napisz teraz program, który wyłączy cache (odpowiedni bit w rejestrze cr0 itd) i zrobi taki benchmark.

Na oko będzie od 2 do 100 razy wolniejszy, ale przynajmniej czegoś się nauczysz.

Żartujesz sobie.

Masz takie fakty - stricte matematyczne!:

  1. dla każdej pierwszej operacji na pamięci jest konieczny dostęp do tejże pamięci, zatem czas minimalny wynosi: t_ram;
  2. dla każdej ostatniej operacji na pamięci jest konieczny dostęp do tej pamięci, zatem czas: t_ram
  3. jedynie wszelkie pośrednie operacje mogą być realizowane na cache, stąd masz: n*t_cache

w sumie minimum wynosi:
I. dla włączonego cache: 2t_ram + nt_cache
II. z wyłączonym cache: n
t_ram

Problem: kiedy będzie szybciej z włączonym cache?

Wystarczy rozwiązać równanie:
2t_ram + nt_cache < n*t_ram;

w naszym przypadku mamy: n =1, zatem:
2t_ram + t_cache < 2*t_ram !

to jest ewidentnym fałszem.

Zatem cache spowalnia, w tym przypadku, a nie przyspiesza.... co jest zresztą oczywiste.

1

Fakt będzie jak napiszesz program i nam pokażesz :).

0
Shalom napisał(a):

@Pelikan szkoda mi marnować czasu na kłótnie z kimś kto nie ma zielonego pojęcia o czym mówi. Zasugerowałem ci na samym początku w jaki sposób zasymulować cache miss i czytać cały czas z RAMu -> poprzez dostępy do oddalonych od siebie (o więcej niż rozmiar cache) obszarów pamięci. Rozumiem że tamten pdf był za długi to może to chociaż przeczytasz:
http://igoro.com/archive/gallery-of-processor-cache-effects/
albo:
https://mechanical-sympathy.blogspot.fr/2012/08/memory-access-patterns-are-important.html
Serio, przeczytaj cokolwiek na ten temat bo bredzisz.

Powiem tylko tak:
Nie bądź głąb - praw fizyki nie zmienisz... hihi!

Jeszcze raz tłumaczę - i ostatni raz:

wszelki odczyt z głównego ramu kosztuje: t_ram,
a z jednoczesnym zapisem cache, będzie tak:
t_max = t_ram + t_cache; gdyby to było robione sekwencyjnie

natomiast gdyby to było realizowane równolegle:
t_min = max(t_ram, t_cache) = t_ram;
bowiem ram musi być tak, czy inaczej czytany, więc cache nic tu nie pomoże - wręcz przeciwnie!

W praktyce jest jeszcze gorzej:
odczyt z ram do rejestru: t_ram,
natomiast z cache jest faktycznie tak:

  1. odczyt z ram
  2. zapis do cache
  3. i teraz: odczyt z cache do rejestru, co razem daje 2 operacje na cache!
1

Ale zdajesz sobie sprawę że sprzętowo pamięć nie jest czytana 'po jednej zmiennej' dla dubla 8 bajtów, tylko nie zalażnie ile ich potrzebujesz to stajesz kilkadziesiąt bajtów na raz np.. I cały myk z pamięcią cache polega na tym że ona zapisuje te kilka dziesiąt bajtów do bardzooo szybkiego rejestru, dziesiątki razy szybszego od ram'u. I zamiast n dostępów do ram masz n/8 dostępów do ram + n/10 jako czas dostępu do cache. poza absolutnie nie realny przypadkiem że n=1 w którym wydajność nie robi różnicy (i kilkoma szczególnymi) , cash przyspiesza wszystko.

//mam tu błąd :P
// dowód druga pętla wykona sie tylko kilka/kilkanaście procent szybciej.
for(i=0; i<n;i++)
{ tab1[i] += 3;}

for(i=0; i<n;i=+2)
{ tab2[i] += 3;}
1

Raz, Twoje równanie nie ma pokrycia z rzeczywistością.
Dwa, sam przecież na samym początku zauważyłeś, że wynik badania jest niespodziewany.
Trzy, biorąc pod uwagę twoje równanie i przemyślenia z nim związane, to powinieneś uzyskać wyniki świadczące o odchyle od normy w stonę niskiej prędości dostępu.

Wygląda to tak, jakbyś sam się ze sobą kłócił.

0
topik92 napisał(a):

Ale zdajesz sobie sprawę że sprzętowo pamięć nie jest czytana 'po jednej zmiennej' dla dubla 8 bajtów, tylko nie zalażnie ile ich potrzebujesz to stajesz kilkadziesiąt bajtów na raz np.. I cały myk z pamięcią cache polega na tym że ona zapisuje te kilka dziesiąt bajtów do bardzooo szybkiego rejestru, dziesiątki razy szybszego od ram'u. I zamiast n dostępów do ram masz n/8 dostępów do ram + n/10 jako czas dostępu do cache. poza absolutnie nie realny przypadkiem że n=1 w którym wydajność nie robi różnicy (i kilkoma szczególnymi) , cash przyspiesza wszystko.

Ojej! Ależ rewelaxyne rzeczy opowiadasz...

Zapomniałeś chyba jedynie o tym że:
test szybkości pamięci, to pomiar szybkości pamięci!

// dowód druga pętla wykona sie tylko kilka/kilkanaście procent szybciej.
for(i=0; i<n;i++)
{ tab1[i] += 3;}

for(i=0; i<n;i=+2)
{ tab2[i] += 3;}

ale chyba coś zgubiłeś 'przy okazji' - połowę danych... notabene.

Sprawdź sobie takie coś:

Test typu I:

for(int = 0; i < N; i++)
{
a[i] = b[i];
}

Test typu II:

for(int = 0; i < N/2; i+=2)
{
a[i] = b[i];
a[i+1] = b[i+1]
}

...

Test typu 8:
for(int = 0; i < N/8; i+=8)
{
a[i] = b[i];
a[i+1] = b[i+1]
a[i+2] = b[i+2]
a[i+3] = b[i+3]
a[i+4] = b[i+4]
a[i+5] = b[i+5]
a[i+6] = b[i+6]
a[i+7] = b[i+7]
}

...

0
spartanPAGE napisał(a):

Raz, Twoje równanie nie ma pokrycia z rzeczywistością.
Dwa, sam przecież na samym początku zauważyłeś, że wynik badania jest niespodziewany.
Trzy, biorąc pod uwagę twoje równanie i przemyślenia z nim związane, to powinieneś uzyskać wyniki świadczące o odchyle od normy w stonę niskiej prędości dostępu.

Wygląda to tak, jakbyś sam się ze sobą kłócił.

A to niby dlaczego?

Wyniki są w zasadzie poprawne...

Przy częstotliwości 800 MHz każdy ram typu DDR - już od DDR1! - powinien osiągać przepustowość:
1600MHz x 64bits = 1600 x 8 MB= 12800 MB/s

zatem ram typu: DDR2, DDR3 itp., raczej więcej powinien osiągać.
A w trybie dual jeszcze x 2 (przynajmniej hipotetycznie).

1

Nie masz pojęcia, jak działa pamięć. Nie chcesz przeczytać o tym, jak działa pamięć. Nie chcesz sprawdzić eksperymentalnie, jak działa pamięć. Co nie przeszkadza Ci się kłócić z ludźmi, którzy wiedzą.

Perełka? Kosz?

0

@Pelikan nie chce mi sie tutaj kopać z koniem bo ty po prostu zagadujesz (i to źle!) zamiast przeczytać jak to działa.
Nie wiem czemu zakładasz ze odczyt z pamieci jest z jakiegoś powodu liniowy ze względu na rozmiar danych. Nie jest. Ani trochę nie jest. Bo narzut na dostęp do pamięci jest wielokrotnie większy od czasu samego odczytu. Odczytanie z pamięci jednego bajtu a odczytanie 128 bajtów do linii cache będzie trwało tyle samo, w granicy błędu pomiaru, zgodnie z zasadą że 1000000000 ~= 1000000000+1.
A odczytywanie 128 kolejnych bajtów będzie już oczywiście wielokrotnie szybsze jeśli masz cache, bo ten długi czas oczekiwania na pamięć odczekasz tylko raz na początku a potem dostaniesz tylko szybkie odczyty z cache. No ale przecież ci wszyscy ludzie od HFT którzy specjalnie piszą algorytmy żeby mieścić się w cache albo wypełniać całe linie cache nie mają racji :D Autorzy Disruptora i LMAXa nie mają racji. Ludzie od mechanical sympathy nie mają racji. Rację ma @Pelikan!
Zresztą to jest farsa: wyszły ci wyniki z d**y i pytasz nas czemu. Mówimy ci czemu, bo nie rozumiesz co robisz, a ty się z nami teraz kłócisz. To po co w ogóle pytałeś skoro "wiesz lepiej"? :D

Jeszcze raz zalinkuje: https://mechanical-sympathy.blogspot.fr/2012/08/memory-access-patterns-are-important.html bo piszą tam dokładnie o tym co sie dzieje kiedy masz taki trywialny pattern chodzenia po pamięci:

It seems that for the linear walk case, memory latency does not exist

The main take away is the more predictable the pattern of access to memory, then the better the cache sub-systems are at hiding main-memory latency.

0
Althorion napisał(a):

Nie masz pojęcia, jak działa pamięć. Nie chcesz przeczytać o tym, jak działa pamięć. Nie chcesz sprawdzić eksperymentalnie, jak działa pamięć. Co nie przeszkadza Ci się kłócić z ludźmi, którzy wiedzą.

Perełka? Kosz?

Masz kody więc sprawdź sobie to sam i praktycznie...
no, a te pedefy możesz poczytać - dziecku na dobranockę... choć nie radzę -
pomiesza się dziecku w pałce i ogłupieje od tego, no ale wtedy będzie całkiem jak jego tatuś,
zatem uzyskacie, zapewne ten, no.. pełny konsensus rodzinny.. hihi!

0

Przenoszę do perełek, chociaż mam wątpliwości czy autor to po prostu idiota czy zwykły troll.

1 użytkowników online, w tym zalogowanych: 0, gości: 1