En dag råkade jag se en kod där en användare försökte övervaka RAM-prestanda i sin virtuella maskin. Jag kommer inte att återge koden (det är en "list"), utan lämnar bara det mest väsentliga. Så, katten i studion!
#include <sys/time.h>
#include <string.h>
#include <iostream>
#define CNT 1024
#define SIZE (1024*1024)
int main() {
struct timeval start;
struct timeval end;
long millis;
double gbs;
char ** buffers;
buffers = new char*[CNT];
for (int i=0;i<CNT;i++) {
buffers[i] = new char[SIZE];
}
gettimeofday(&start, NULL);
for (int i=0;i<CNT;i++) {
memset(buffers[i], 0, SIZE);
}
gettimeofday(&end, NULL);
millis = (end.tv_sec - start.tv_sec) * 1000 +
(end.tv_usec - start.tv_usec) / 1000;
gbs = 1000.0 / millis;
std::cout << gbs << " GB/sn";
for (int i=0;i<CNT;i++) {
delete buffers[i];
}
delete buffers;
return 0;
}Det är enkelt - vi allokerar minne och skriver en gigabyte i det. Vad visar detta test?
$ ./memtest
4.06504 GB/s
Ungefär 4 GB/s.
Vad?!?!
Hur?!?!?
Detta är en Core i7 (även om den inte är den senaste), DDR4, processorn är nästan inte belastad - VARFÖR?!?!
Svaret är, som alltid, oerhört ordinärt.
Operatören new (precis som funktionen malloc, för övrigt) allokerar faktiskt inte minne. Vid denna anrop granskar allokeraren listan över lediga områden i minnespoolen, och om det inte finns några, anropar den sbrk() för att öka datasegmentet och returnerar sedan en referens till programmet till adressen i det nyligen allokerade området.
Problemet är att det allokerade området är helt virtuellt. Verkliga minnessidor har inte allokerats.
Och när det första åtkomsten till varje sida i detta allokerade segment sker, "skjuter" MMU ett page fault, efter vilket en verklig sidallokeras till den virtuella sidan som accesseras.
Så i själva verket testar vi inte prestandan hos bussen och RAM-modulerna, utan prestandan hos MMU och VMM i operativsystemet. För att testa den verkliga prestandan hos RAM-minnet, behöver vi bara initiera de allokerade områdena en gång. Till exempel så här:
#include <sys/time.h>
#include <string.h>
#include <iostream>
#define CNT 1024
#define SIZE (1024*1024)
int main() {
struct timeval start;
struct timeval end;
long millis;
double gbs;
char ** buffers;
buffers = new char*[CNT];
for (int i=0;i<CNT;i++) {
// FIXED HERE!!!
buffers[i] = new char[SIZE](); // Add brackets, &$# !!!
}
gettimeofday(&start, NULL);
for (int i=0;i<CNT;i++) {
memset(buffers[i], 0, SIZE);
}
gettimeofday(&end, NULL);
millis = (end.tv_sec - start.tv_sec) * 1000 +
(end.tv_usec - start.tv_usec) / 1000;
gbs = 1000.0 / millis;
std::cout << gbs << " GB/sn";
for (int i=0;i<CNT;i++) {
delete buffers[i];
}
delete buffers;
return 0;
}Det vill säga, vi initierar helt enkelt de allokerade buffrarna med standardvärdet (char 0).
Kontrollerar:
$ ./memtest
28.5714 GB/s
Det är en annan sak.
Moral - om du behöver stora buffrar för att arbeta snabbt, glöm inte att initiera dem.
Källa: habr.com
