Una mica més sobre les males proves

Un dia em vaig trobar accidentalment amb el codi que un usuari intentava controlar el rendiment de la memòria RAM a la seva màquina virtual. No donaré aquest codi (allà hi ha un "tavalló") i només deixaré el més essencial. Així doncs, el gat és a l'estudi!

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

És senzill: assigneu memòria i escriviu-hi un gigabyte. I què demostra aquesta prova?

$ ./memtest
4.06504 GB / s

Aproximadament 4 GB/s.

Què?!?!

Com?!?!?

Això és Core i7 (encara que no és el més nou), DDR4, el processador gairebé no està carregat - PER QUÈ?!?!

La resposta, com sempre, és inusualment normal.

El nou operador (com la funció malloc, per cert) no assigna memòria. Amb aquesta trucada, l'assignador mira la llista d'ubicacions lliures a l'agrupació de memòria i, si no n'hi ha cap, crida a sbrk() per augmentar el segment de dades, i després torna al programa una referència a l'adreça des de la nova ubicació. assignat.

El problema és que l'àrea assignada és totalment virtual. No s'assignen pàgines de memòria real.

I quan es produeix el primer accés a cada pàgina des d'aquest segment assignat, l'MMU "dispara" una fallada de pàgina, després de la qual s'assigna una de real a la pàgina virtual a la qual s'accedeix.

Per tant, de fet, no estem provant el rendiment dels mòduls de bus i RAM, sinó el rendiment de la MMU i VMM del sistema operatiu. I per provar el rendiment real de la memòria RAM, només cal que inicialitzem les àrees assignades una vegada. Per exemple com aquest:

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

És a dir, simplement inicialitzem els buffers assignats amb el valor per defecte (car 0).

Comprovem:

$ ./memtest
28.5714 GB / s

Una altra cosa.

Moral de la història: si necessiteu buffers grans per funcionar ràpidament, no oblideu inicialitzar-los.

Font: www.habr.com

Compreu allotjament fiable per a llocs amb protecció DDoS, servidors VPS VDS 🔥 Compra allotjament web fiable amb protecció DDoS, servidors VPS VDS | ProHoster