Ešte niečo o nesprávnom testovaní

Raz som náhodou natrafil na kód, ktorým sa používateľ snažil monitorovať výkon RAM vo svojej virtuálnej mašine. Tento kód sem nebudem uvádzať (je to „obrázok“) a nechám len to najdôležitejšie. Takže, mačka 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é — pridelenie pamäte a zapísanie do nej jedného gigabajtu. A čo tento test ukazuje?

$ .\/memtest
4.06504 GB\/s

Približne 4GB\/s.

Čo?!?!

Ako?!?!?

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

Odpoveď je, ako vždy, mimoriadne obyčajná.

Operátor new (tak ako aj funkcia malloc, mimochodom) v skutočnosti nepridelenie pamäte. Pri tomto volaní alokátor preskakuje zoznam voľných častí v pamäťovom zásobníku a ak tam nie sú, volá sbrk(), aby zväčšil segment dát, a potom vráti programu odkaz na adresu z práve prideleného bloku.

Problém je v tom, že pridelený blok je úplne virtuálny. Skutočné stránky pamäte nie sú pridelené.

A keď sa prvýkrát pristupuje k každej stránke z tohto prideleného segmentu, MMU „spustí“ page fault, po čom je virtuálnej stránke, ku ktorej sa pristupuje, priradená skutočná.

Preto v skutočnosti netestujeme výkon zbernice a modulov RAM, ale výkon MMU a VMM operačného systému. A na to, aby sme otestovali skutočný výkon operačnej pamäte, potrebujeme jednoducho raz inicializovať pridelené časti. 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é buffery predvolenou hodnotou (char 0).

Kontrolujeme:

$ .\/memtest
28.5714 GB\/s

To je iná vec.

Moraľ — ak potrebujete veľké buffery na rýchlu prácu, nezabúdajte ich inicializovať.

Zdroj: habr.com

Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster