Még egy kicsit a rossz tesztekről

Egy nap belebotlottam egy kódba, amit egy felhasználó a virtuális gépe RAM teljesítményének figyelésére használt. Nem osztom meg a kódot (ez egy kicsit csalódás), csak a lényeget osztom 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ű: foglalj le memóriát és írj bele egy gigabájtot. Szóval mit mutat ez a teszt?

$ ./memtest
4.06504 GB / s

Körülbelül 4 GB/s.

Mi?!?!

Hogyan?!?!?

Ez egy Core i7 (még ha nem is a legújabb), DDR4, a processzor szinte nincs bepakolva - MIÉRT?!?!

A válasz, mint mindig, rendkívül hétköznapi.

Az új operátor (ahogy egyébként a malloc függvény is) valójában nem foglal le memóriát. Híváskor az allokátor megnézi a szabad memóriablokkok listáját a tárolóban, és ha nincsenek, akkor meghívja az sbrk() függvényt az adatszegmens növelésére, majd visszaad egy referenciát az újonnan lefoglalt blokk címére.

A probléma az, hogy a lefoglalt terület teljes mértékben virtuális. Nincsenek tényleges memórialapok lefoglalva.

És amikor az adott lefoglalt szegmensből az egyes oldalakhoz először férnek hozzá, az MMU „laphibát” jelez, amely után a hozzáfért virtuális oldalhoz egy valódi oldalt rendelnek hozzá.

Tehát a valóságban nem a busz és a RAM modulok teljesítményét teszteljük, hanem az operációs rendszer MMU és VMM moduljainak teljesítményét. A RAM tényleges teljesítményének teszteléséhez egyszerűen 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;
}

Vagyis egyszerűen inicializáljuk a lefoglalt puffereket az alapértelmezett értékkel (0. karakter).

Ellenőrizzük:

$ ./memtest
28.5714 GB / s

Ez már egy másik kérdés.

A történet tanulsága, hogy ha nagy pufferekre van szükséged a gyors futtatáshoz, ne felejtsd el inicializálni őket.

Forrás: will.com

Vásároljon megbízható tárhelyet DDoS védelemmel, VPS VDS szerverekkel rendelkező webhelyekhez 🔥 Vásároljon megbízható weboldal tárhelyet DDoS védelemmel, VPS VDS szerverekkel | ProHoster