Še malo o slabem testiranju

Nekega dne sem po naključju naletel na kodo, da je uporabnik poskušal spremljati delovanje RAM-a v svojem virtualnem računalniku. Te kode ne bom dal (tam je "krpica") in bom pustil samo najnujnejše. Torej, mačka je v studiu!

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

Preprosto je – dodelite pomnilnik in vanj zapišite en gigabajt. In kaj ta test pokaže?

$ ./memtest
4.06504 GB / s

Približno 4 GB/s.

Kaj?!?!

Kako?!?!?

To je Core i7 (čeprav ne najnovejši), DDR4, procesor skoraj ni naložen - ZAKAJ?!?!

Odgovor je, kot vedno, nenavadno običajen.

Novi operater (mimogrede, tako kot funkcija malloc) dejansko ne dodeli pomnilnika. S tem klicem dodeljevalec pogleda seznam prostih lokacij v pomnilniškem področju in, če jih ni, pokliče sbrk(), da poveča segment podatkov, nato pa programu vrne sklic na naslov z nove lokacije pravkar dodeljena.

Težava je v tem, da je dodeljeno območje povsem virtualno. Prave pomnilniške strani niso dodeljene.

In ko pride do prvega dostopa do vsake strani iz tega dodeljenega segmenta, MMU "izstreli" napako strani, po kateri se virtualni strani, do katere se dostopa, dodeli prava.

Zato pravzaprav ne preizkušamo delovanja modulov vodila in RAM-a, temveč delovanje MMU in VMM operacijskega sistema. Da bi preizkusili resnično delovanje RAM-a, moramo le enkrat inicializirati dodeljena območja. Na primer takole:

#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 pomeni, da preprosto inicializiramo dodeljene medpomnilnike s privzeto vrednostjo (char 0).

Preverjamo:

$ ./memtest
28.5714 GB / s

Druga stvar.

Narava zgodbe – če potrebujete velike medpomnilnike za hitro delovanje, jih ne pozabite inicializirati.

Vir: www.habr.com

Dodaj komentar