Egy nap véletlenül olyan kódra bukkantam, amellyel egy felhasználó a RAM teljesítményét próbálta figyelni a virtuális gépében. Nem adom meg ezt a kódot (ott van egy „lábtörlő”), és csak a legszükségesebbet hagyom meg. Szóval a macska a stúdióban van!
#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;
}
Egyszerű – foglaljon le memóriát, és írjon bele egy gigabájtot. És mit mutat ez a teszt?
$ ./memtest
4.06504 GB / s
Körülbelül 4 GB/s.
Mit?!?!
Hogyan?!?!?
Ez Core i7 (bár nem a legújabb), DDR4, a processzor szinte nincs betöltve - MIÉRT?!?!
A válasz, mint mindig, szokatlanul közönséges.
Az új operátor (egyébként a malloc függvényhez hasonlóan) valójában nem foglal le memóriát. Ezzel a hívással az allokátor megnézi a szabad helyek listáját a memóriatárban, és ha nincs ilyen, meghívja az sbrk() függvényt, hogy növelje az adatszegmenst, majd visszaadja a programnak az új hely címére való hivatkozást. kiosztott.
A probléma az, hogy a kijelölt terület teljesen virtuális. Nincsenek valódi memóriaoldalak lefoglalva.
És amikor ebből a lefoglalt szegmensből az egyes oldalakhoz az első hozzáférés megtörténik, az MMU oldalhibát „lövet”, ami után az elért virtuális oldalhoz valódi oldalt rendel.
Ezért valójában nem a busz és a RAM modulok teljesítményét teszteljük, hanem az operációs rendszer MMU és VMM teljesítményét. A RAM valós teljesítményének teszteléséhez pedig csak egyszer kell inicializálnunk a lefoglalt területeket. Például így:
#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;
}
Azaz egyszerűen inicializáljuk a lefoglalt puffereket az alapértelmezett értékkel (char 0).
Ellenőrizzük:
$ ./memtest
28.5714 GB / s
A másik dolog.
A történet morálja – ha nagy pufferekre van szüksége a gyors működéshez, ne felejtse el inicializálni őket.
Forrás: will.com