Un poco más sobre las malas pruebas

Un día encontré accidentalmente un código que indicaba que un usuario estaba intentando monitorear el rendimiento de la RAM en su máquina virtual. No daré este código (hay un "calcetín" allí) y dejaré solo lo más esencial. ¡Entonces el gato está en el estudio!

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

Es muy sencillo: asigne memoria y escriba en ella un gigabyte. ¿Y qué muestra esta prueba?

$ ./memprueba
4.06504 GB / s

Aproximadamente 4 GB/s.

¡¿¡¿Qué?!?!

¿¡¿¡¿Cómo?!?!?

Este es Core i7 (aunque no el más nuevo), DDR4, el procesador casi no está cargado - ¡¿POR QUÉ?!?!

La respuesta, como siempre, es inusualmente ordinaria.

El nuevo operador (como la función malloc, por cierto) en realidad no asigna memoria. Con esta llamada, el asignador mira la lista de ubicaciones libres en el grupo de memoria y, si no hay ninguna, llama a sbrk() para aumentar el segmento de datos y luego devuelve al programa una referencia a la dirección de la nueva ubicación recién asignado.

El problema es que el área asignada es totalmente virtual. No se asignan páginas de memoria real.

Y cuando se produce el primer acceso a cada página desde este segmento asignado, la MMU "dispara" un error de página, después de lo cual a la página virtual a la que se accede se le asigna una página real.

Por lo tanto, de hecho, no estamos probando el rendimiento del bus y los módulos RAM, sino el rendimiento de la MMU y VMM del sistema operativo. Y para probar el rendimiento real de la RAM, sólo necesitamos inicializar las áreas asignadas una vez. Por ejemplo 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;
}

Es decir, simplemente inicializamos los buffers asignados con el valor predeterminado (carácter 0).

Comprobamos:

$ ./memprueba
28.5714 GB / s

Otra cosa

Moraleja de la historia: si necesita grandes buffers para funcionar rápidamente, no olvide inicializarlos.

Fuente: habr.com

Añadir un comentario