Un pouco máis sobre as malas probas

Un día atopeime accidentalmente con código que un usuario estaba tentando supervisar o rendemento da RAM na súa máquina virtual. Non vou dar este código (hai un "pano") e deixarei só o máis esencial. Entón, o gato está no estudo!

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

É sinxelo: asigna memoria e escribe un gigabyte nela. E que mostra esta proba?

$ ./memtest
4.06504 GB / s

Aproximadamente 4 GB/s.

Que?!?!

Como?!?!?

Este é o Core i7 (aínda que non é o máis novo), DDR4, o procesador case non está cargado - POR QUE?!?!

A resposta, coma sempre, é inusualmente común.

O novo operador (como a función malloc, por certo) non asigna memoria. Con esta chamada, o asignador mira a lista de localizacións libres no grupo de memoria e, se non hai ningunha, chama a sbrk() para aumentar o segmento de datos e, a continuación, devolve ao programa unha referencia ao enderezo desde a nova localización. asignado.

O problema é que a área asignada é totalmente virtual. Non se asignan páxinas de memoria reais.

E cando se produce o primeiro acceso a cada páxina desde este segmento asignado, a MMU "dispara" un fallo de páxina, despois de que a páxina virtual á que se accede atribúeselle unha real.

Polo tanto, de feito, non estamos probando o rendemento dos módulos de bus e RAM, senón o rendemento da MMU e VMM do sistema operativo. E para probar o rendemento real da RAM, só necesitamos inicializar as áreas asignadas unha vez. Por exemplo así:

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

É dicir, simplemente inicializamos os búfers asignados co valor predeterminado (car 0).

Comprobamos:

$ ./memtest
28.5714 GB / s

Outra cousa.

Moral da historia: se necesitas grandes búfers para funcionar rapidamente, non esquezas inicializalos.

Fonte: www.habr.com

Engadir un comentario