Trochu viac o zlom testovaní

Jedného dňa som náhodou narazil na kód, ktorým sa používateľ pokúšal monitorovať výkon RAM vo svojom virtuálnom stroji. Tento kód neuvediem (je tam „utierka“) a nechám len to najpodstatnejšie. Takže mačka je v štúdiu!

#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;
}

Je to jednoduché – prideľte pamäť a zapíšte do nej jeden gigabajt. A čo ukazuje tento test?

$ ./memtest
4.06504 GB / s

Približne 4 GB/s.

Čo?!?!

Ako?!?!?

Toto je Core i7 (aj keď nie najnovší), DDR4, procesor takmer nezaťažený - PREČO?!?!

Odpoveď je ako vždy nezvyčajne obyčajná.

Nový operátor (mimochodom ako funkcia malloc) v skutočnosti neprideľuje pamäť. Pri tomto volaní sa alokátor pozrie na zoznam voľných miest v pamäťovej oblasti, a ak žiadne nie sú, zavolá sbrk() na zvýšenie segmentu údajov a potom vráti programu odkaz na adresu z nového umiestnenia práve pridelené.

Problém je, že pridelená oblasť je úplne virtuálna. Nie sú pridelené žiadne stránky skutočnej pamäte.

A keď dôjde k prvému prístupu ku každej stránke z tohto prideleného segmentu, MMU „vystrelí“ chybu stránky, po ktorej sa virtuálnej stránke, ku ktorej sa pristupuje, priradí skutočná stránka.

Preto v skutočnosti netestujeme výkon zbernice a modulov RAM, ale výkon MMU a VMM operačného systému. A aby sme mohli otestovať skutočný výkon pamäte RAM, stačí raz inicializovať pridelené oblasti. Napríklad takto:

#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;
}

To znamená, že jednoducho inicializujeme pridelené vyrovnávacie pamäte s predvolenou hodnotou (char 0).

skontrolujte:

$ ./memtest
28.5714 GB / s

Ďalšia vec.

Morálka príbehu – ak potrebujete veľké vyrovnávacie pamäte na rýchlu prácu, nezabudnite ich inicializovať.

Zdroj: hab.com

Pridať komentár