Još malo o lošem testiranju

Jednog sam dana slučajno naišao na kod da je korisnik pokušavao pratiti performanse RAM-a u svom virtualnom računalu. Neću dati ovu šifru (tamo je "krpica") i ostavit ću samo najnužnije. Dakle, mačka je u studiju!

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

Jednostavno je - dodijelite memoriju i u nju upišite jedan gigabajt. I što ovaj test pokazuje?

$ ./memtest
4.06504 GB / s

Otprilike 4GB/s.

Što?!?!

Kako?!?!?

Ovo je Core i7 (iako ne najnoviji), DDR4, procesor skoro da nije opterećen - ZAŠTO?!?!

Odgovor je, kao i uvijek, neobično običan.

Novi operator (usput, poput funkcije malloc) zapravo ne dodjeljuje memoriju. S ovim pozivom, alokator gleda popis slobodnih lokacija u spremištu memorije, i ako ih nema, poziva sbrk() da poveća segment podataka, a zatim vraća programu referencu na adresu s nove lokacije upravo dodijeljen.

Problem je u tome što je dodijeljeno područje potpuno virtualno. Ne dodjeljuju se prave memorijske stranice.

A kada se dogodi prvi pristup svakoj stranici iz ovog dodijeljenog segmenta, MMU "opali" grešku stranice, nakon čega se virtualnoj stranici kojoj se pristupa dodjeljuje prava.

Stoga, zapravo, ne testiramo performanse sabirnice i RAM modula, već performanse MMU i VMM operativnog sustava. A kako bismo testirali stvarnu izvedbu RAM-a, samo trebamo jednom inicijalizirati dodijeljena područja. Na primjer ovako:

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

To jest, jednostavno inicijaliziramo dodijeljene međuspremnike sa zadanom vrijednošću (char 0).

Provjeravamo:

$ ./memtest
28.5714 GB / s

Druga stvar.

Pouka priče - ako trebate velike međuspremnike za brzi rad, ne zaboravite ih inicijalizirati.

Izvor: www.habr.com

Dodajte komentar