Litt mer om dårlig testing

En dag kom jeg tilfeldigvis over kode som en bruker prøvde å overvåke RAM-ytelsen i den virtuelle maskinen sin. Jeg vil ikke gi denne koden (det er en "fotklut" der) og jeg vil bare la det viktigste være igjen. Så, katten er i 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;
}

Det er enkelt – alloker minne og skriv én gigabyte inn i det. Og hva viser denne testen?

$ ./memtest
4.06504 GB / s

Omtrent 4 GB/s.

Hva?!?!

Hvordan?!?!?

Dette er Core i7 (om enn ikke den nyeste), DDR4, prosessoren er nesten ikke lastet - HVORFOR?!?!

Svaret er, som alltid, uvanlig vanlig.

Den nye operatøren (som malloc-funksjonen, forresten) tildeler faktisk ikke minne. Med dette kallet ser allokatoren på listen over ledige plasseringer i minnepoolen, og hvis det ikke er noen, kaller sbrk() for å øke datasegmentet, og returnerer deretter til programmet en referanse til adressen fra den nye plasseringen bare tildelt.

Problemet er at det tildelte området er helt virtuelt. Ingen reelle minnesider er tildelt.

Og når den første tilgangen til hver side fra dette tildelte segmentet skjer, "skyter" MMU en sidefeil, hvoretter den virtuelle siden som åpnes blir tildelt en ekte en.

Derfor tester vi faktisk ikke ytelsen til buss- og RAM-modulene, men ytelsen til MMU og VMM til operativsystemet. Og for å teste den virkelige ytelsen til RAM, trenger vi bare å initialisere de tildelte områdene én gang. For eksempel slik:

#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 vil si at vi ganske enkelt initialiserer de tildelte bufferne med standardverdien (char 0).

Vi sjekker:

$ ./memtest
28.5714 GB / s

En annen ting.

Moralen i historien - hvis du trenger store buffere for å fungere raskt, ikke glem å initialisere dem.

Kilde: www.habr.com

Legg til en kommentar