Lite mer om felaktig testning

En dag rÄkade jag se en kod dÀr en anvÀndare försökte övervaka RAM-prestanda i sin virtuella maskin. Jag kommer inte att Äterge koden (det Àr en "list"), utan lÀmnar bara det mest vÀsentliga. SÄ, katten i studion!

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

Det Àr enkelt - vi allokerar minne och skriver en gigabyte i det. Vad visar detta test?

$ ./memtest
4.06504 GB/s

UngefÀr 4 GB/s.

Vad?!?!

Hur?!?!?

Detta Ă€r en Core i7 (Ă€ven om den inte Ă€r den senaste), DDR4, processorn Ă€r nĂ€stan inte belastad - VARFÖR?!?!

Svaret Àr, som alltid, oerhört ordinÀrt.

Operatören new (precis som funktionen malloc, för övrigt) allokerar faktiskt inte minne. Vid denna anrop granskar allokeraren listan över lediga omrÄden i minnespoolen, och om det inte finns nÄgra, anropar den sbrk() för att öka datasegmentet och returnerar sedan en referens till programmet till adressen i det nyligen allokerade omrÄdet.

Problemet Àr att det allokerade omrÄdet Àr helt virtuellt. Verkliga minnessidor har inte allokerats.

Och nÀr det första Ätkomsten till varje sida i detta allokerade segment sker, "skjuter" MMU ett page fault, efter vilket en verklig sidallokeras till den virtuella sidan som accesseras.

SÄ i sjÀlva verket testar vi inte prestandan hos bussen och RAM-modulerna, utan prestandan hos MMU och VMM i operativsystemet. För att testa den verkliga prestandan hos RAM-minnet, behöver vi bara initiera de allokerade omrÄdena en gÄng. Till exempel sÄ hÀr:

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

Det vill sÀga, vi initierar helt enkelt de allokerade buffrarna med standardvÀrdet (char 0).

Kontrollerar:

$ ./memtest
28.5714 GB/s

Det Àr en annan sak.

Moral - om du behöver stora buffrar för att arbeta snabbt, glöm inte att initiera dem.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster