Een beetje meer over foutieve tests

Op een dag viel me per ongeluk een code op waarin een gebruiker probeerde de RAM-prestaties van zijn virtuele machine te monitoren. Ik zal deze code niet geven (het is een "staartje") en alleen het belangrijkste benoemen. Dus, de kat in de studio!

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

Het is heel eenvoudig: we wijzen geheugen toe en schrijven er ƩƩn gigabyte in. En wat laat deze test zien?

$ ./memtest
4.06504 GB/s

Ongeveer 4GB/s.

Wat?!?!

Hoe?!?!?

Dit is een Core i7 (ook al is het niet de nieuwste), DDR4, de processor is bijna niet belast — WAAROM?!?!

Het antwoord is, zoals altijd, buitengewoon gewoon.

De operator new (zoals ook de functie malloc, trouwens) wijst in werkelijkheid geen geheugen toe. Bij deze oproep kijkt de allocator naar de lijst met vrije blokken in de geheugenvijver, en als die er niet zijn, roept hij sbrk() aan om het datasegment te vergroten, en retourneert dan een verwijzing naar het adres van het nieuw net toegewezen blok aan het programma.

Het probleem is dat het toegewezen blok geheel virtueel is. De werkelijke geheugenpagina’s zijn niet toegewezen.

En wanneer de eerste toegang tot elke pagina van dit toegewezen segment plaatsvindt, 'schiet' de MMU een page fault, waarna aan de virtuele pagina die wordt benaderd een reƫel geheugen wordt toegewezen.

Daarom testen we eigenlijk niet de prestaties van de bus en de RAM-modules, maar de prestaties van de MMU en de VMM van het besturingssysteem. Om de werkelijke prestaties van het RAM-geheugen te testen, hoeven we gewoon de toegewezen blokken een keer te initialiseren. Bijvoorbeeld zo:

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

Dat betekent dat we de toe te wijzen buffers gewoon initialiseren met een standaardwaarde (char 0).

We controleren:

$ ./memtest
28.5714 GB/s

Dat is een ander verhaal.

De moraal is: als je grote buffers nodig hebt om snel en efficiƫnt te werken, vergeet dan niet ze te initialiseren.

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers šŸ”„ Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster