Engang faldt jeg tilfældigvis over en kode, som en bruger forsøgte at overvåge RAM-ydeevnen i sin virtuelle maskine med. Jeg vil ikke gengive koden (det er en "smøre") og vil kun tage det mest væsentlige med. Så, katten i studiet!
#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 er enkelt — vi allokerer hukommelse og skriver et gigabyte til det. Hvad viser denne test?
$ ./memtest
4.06504 GB/s
Cirka 4GB/s.
Hvad?!?!
Hvordan?!?!?
Dette er en Core i7 (selv om den ikke er den nyeste), DDR4, processoren er næsten ikke belastet - HVORFOR?!?!
Svaret er som altid usædvanligt almindeligt.
Operatoren new (ligesom funktionen malloc, forresten) allokerer reelt ikke hukommelse. Ved dette kald kigger allokatoren på listen over frie områder i hukommelsespuljen, og hvis der ikke er nogen, kalder den sbrk() for at øge datasegmentet og returnerer derefter et link til programmet på adressen fra det område, der netop er allokeret.
Problemet er, at det allokerede område er helt virtuelt. De reelle hukommelsessider er ikke allokeret.
Og når den første adgang til hver side fra dette allokerede segment sker, "skyder" MMU en sidefejl, hvorefter den virtuelle side, der tilgås, tildeles en reel.
Derfor tester vi faktisk ikke bussen og RAM-modulernes ydeevne, men ydeevnen for MMU og VMM i operativsystemet. For at teste den reelle ydeevne af RAM skal vi blot initialisere de allokerede områder én gang. For eksempel sådan:
#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 vil sige, vi skal blot initialisere de allokerede buffere med standardværdien (char 0).
Vi tjekker:
$ ./memtest
28.5714 GB/s
Det er en helt anden sag.
Moralen er - hvis du har brug for store buffere for at arbejde hurtigt, skal du ikke glemme at initialisere dem.
Kilde: habr.com
