Lite mer om dÄliga tester

En dag stötte jag av misstag pÄ lite kod som en anvÀndare anvÀnde för att övervaka RAM-prestanda i sin virtuella maskin. Jag kommer inte att visa den hÀr koden (det Àr en enda röra) och lÀmnar bara de viktigaste delarna. SÄ, katten Àr 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 till det. Och vad visar det hĂ€r testet?

$ ./memtest
4.06504 GB / s

UngefÀr 4 GB/s.

Vad?!?!

Hur?!?!?

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

Svaret Àr, som alltid, utomordentligt ordinÀrt.

Den nya operatorn (och malloc-funktionen, förresten) allokerar faktiskt inte minne. NÀr den anropas tittar allokeraren pÄ listan över lediga omrÄden i minnespoolen, och om det inte finns nÄgra anropar den sbrk() för att utöka datasegmentet, och returnerar sedan en referens till adressen för det nyligen allokerade omrÄdet till programmet.

Problemet Àr att det allokerade omrÄdet Àr helt virtuellt. Inga riktiga minnessidor allokeras.

Och nÀr den första Ätkomsten till varje sida frÄn detta allokerade segment sker, "utlöser" MMU ett sidfel, varefter den virtuella sidan som nÄs tilldelas en riktig.

DÀrför testar vi i verkligheten inte prestandan hos buss- och RAM-modulerna, utan prestandan hos operativsystemets MMU och VMM. Och för att testa RAM-minnets verkliga prestanda 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 buffertarna med standardvÀrdet (char 0).

Vi kontrollerar:

$ ./memtest
28.5714 GB / s

Det Àr en annan sak.

Sensmoralen i historien: om du behöver stora buffertar för att arbeta riktigt snabbt, glöm inte att initiera dem.

KĂ€lla: will.com

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