Egyszer véletlenül a szemem elé került egy kód, amellyel a felhasználó megpróbálta monitorozni a RAM teljesítményét a virtuális gépén. Ezt a kódot nem fogom bemutatni (ott »hosszú« van), és csak a legfontosabbat hagyom meg. Tehát, macska a stúdióban!
#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;
}Minden egyszerű — foglalunk memóriát, és írunk bele egy gigabájtnyi adatot. Mit mutat ez a teszt?
$ ./memtest
4.06504 GB/s
Körülbelül 4GB/s.
Mi?!?!
Hogyan?!?!?
Ez egy Core i7 (bár nem a legújabb), DDR4, a processzor szinte nincs terhelve — MIÉRT?!?!
A válasz, mint mindig, rendkívül banális.
A new operátor (és a malloc függvény is, mellesleg) valójában nem foglal memóriát. E hívás során az allokátor megnézi a memóriapoolban a szabad szegmensek listáját, és ha azok nincsenek, meghívja az sbrk()-t, hogy megnövelje az adat szegmenst, majd visszaadja a programnak a címre mutató hivatkozást az újonnan allokált szegmensből.
A probléma az, hogy az allokált szegmens teljesen virtuális. A tényleges memóriablokkok nincsenek allokálva.
És amikor az első hivatkozás történik az ezen allokált szegmensen belüli minden lapra, az MMU "ráüt" egy page fault-t, majd a virtuális lap, amelyhez hozzáférnek, egy valós lapot kap.
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. Ahhoz, hogy teszteljük a tényleges RAM teljesítményt, egyszerűen csak inicializálnunk kell az allokált szegmenseket. 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;
}Tehát, egyszerűen az allokált puffereket alapértelmezett értékkel inicializáljuk (char 0).
Ellenőrizzük:
$ ./memtest
28.5714 GB/s
Ez más tészta.
Moral — ha nagy pufferekre van szüksége a gyors munkához, ne felejtse el azokat inicializálni.
Forrás: habr.com
