Šiek tiek daugiau apie blogus testus

Vieną dieną netyčia aptikau kodą, kurį vartotojas bandė stebėti RAM našumą savo virtualioje mašinoje. Neduosiu šio kodo (ten yra "kojytė") ir paliksiu tik būtiniausią. Taigi, katė yra studijoje!

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

Tai paprasta – paskirkite atmintį ir įrašykite vieną gigabaitą. Ir ką rodo šis testas?

$ ./memtest
4.06504 GB / s

Maždaug 4 GB/s.

Ką?!?!

Kaip?!?!?

Tai Core i7 (nors ir ne naujausias), DDR4, procesorius beveik nepakrautas – KODĖL?!?!

Atsakymas, kaip visada, yra neįprastai įprastas.

Naujasis operatorius (kaip, beje, funkcija malloc) iš tikrųjų neskiria atminties. Šiuo iškvietimu skirstytuvas peržiūri laisvų vietų sąrašą atminties telkinyje, o jei jų nėra, iškviečia sbrk(), kad padidintų duomenų segmentą, o tada grąžina programai nuorodą į adresą iš naujos vietos. paskirta.

Problema ta, kad paskirtas plotas yra visiškai virtualus. Tikri atminties puslapiai nepaskirti.

Ir kai įvyksta pirmoji prieiga prie kiekvieno puslapio iš šio skirto segmento, MMU „iššauna“ puslapio gedimą, po kurio pasiekiamam virtualiam puslapiui priskiriamas tikras.

Todėl iš tikrųjų mes testuojame ne magistralės ir RAM modulių, o operacinės sistemos MMU ir VMM našumą. O norint patikrinti tikrąjį RAM našumą, tereikia vieną kartą inicijuoti paskirtas sritis. Pavyzdžiui taip:

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

Tai yra, mes tiesiog inicijuojame paskirtus buferius numatytąja verte (char 0).

Mes patikriname:

$ ./memtest
28.5714 GB / s

Dar vienas dalykas.

Istorijos moralė – jei norint greitai veikti reikia didelių buferių, nepamirškite jų inicijuoti.

Šaltinis: www.habr.com

Pirkite patikimą prieglobą svetainėms su DDoS apsauga, VPS VDS serveriais 🔥 Įsigykite patikimą svetainių talpinimą su DDoS apsauga, VPS VDS serveriais | ProHoster