Lite mer om dåliga tester

En dag kom jag av misstag över kod som en användare försökte övervaka RAM-prestanda i sin virtuella maskin. Jag kommer inte att ge den här koden (det finns en "fotduk" där) och jag lämnar bara det viktigaste. 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 - allokera minne och skriv in en gigabyte i det. Och vad visar detta test?

$ ./memtest
4.06504 GB / s

Cirka 4GB/s.

Vad?!?!

Hur?!?!?

Detta är Core i7 (om än inte den nyaste), DDR4, processorn är nästan inte laddad - VARFÖR?!?!

Svaret är som alltid ovanligt vanligt.

Den nya operatören (som malloc-funktionen förresten) allokerar faktiskt inte minne. Med detta anrop tittar allokatorn på listan över lediga platser i minnespoolen, och om det inte finns några, anropar sbrk() för att öka datasegmentet och returnerar sedan till programmet en referens till adressen från den nya platsen bara tilldelas.

Problemet är att det tilldelade området är helt virtuellt. Inga riktiga minnessidor tilldelas.

Och när den första åtkomsten till varje sida från detta tilldelade segment inträffar, "skjuter" MMU ett sidfel, varefter den virtuella sidan som nås tilldelas en riktig sida.

Därför testar vi faktiskt inte prestanda för buss- och RAM-modulerna, utan prestandan hos operativsystemets MMU och VMM. Och för att testa den verkliga prestandan hos RAM behöver vi bara initiera de tilldelade 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

En annan sak.

Moralen i historien - om du behöver stora buffertar för att fungera snabbt, glöm inte att initiera dem.

Källa: will.com

Lägg en kommentar