Още малко за лошите тестове

Един ден случайно попаднах на код, че потребител се опитва да наблюдава производителността на RAM в неговата виртуална машина. Няма да дам този код (там има „кърпа за крака“) и ще оставя само най-важното. И така, котката е в студиото!

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

Просто е - разпределете памет и запишете един гигабайт в нея. И какво показва този тест?

$ ./memtest
4.06504 GB / s

Приблизително 4GB/s.

Какво?!?!

Как?!?!?

Това е Core i7 (макар и не най-новият), DDR4, процесорът почти не се натоварва - ЗАЩО?!?!

Отговорът, както винаги, е необичайно обикновен.

Операторът new (като функцията malloc, между другото) всъщност не разпределя памет. С това извикване разпределителят преглежда списъка със свободни местоположения в пула с памет и ако няма такива, извиква sbrk(), за да увеличи сегмента от данни, и след това връща на програмата препратка към адреса от новото местоположение просто разпределени.

Проблемът е, че разпределената зона е изцяло виртуална. Не са разпределени реални страници с памет.

И когато се случи първият достъп до всяка страница от този разпределен сегмент, MMU „изстрелва“ грешка на страницата, след което виртуалната страница, до която се осъществява достъп, се присвоява на реална.

Следователно всъщност ние не тестваме производителността на шината и RAM модулите, а производителността на MMU и VMM на операционната система. И за да тестваме реалната производителност на RAM, просто трябва да инициализираме разпределените области веднъж. Например така:

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

Тоест ние просто инициализираме разпределените буфери със стойността по подразбиране (char 0).

проверете:

$ ./memtest
28.5714 GB / s

Още нещо.

Морал - ако имате нужда от големи буфери, за да работят бързо, не забравяйте да ги инициализирате.

Източник: www.habr.com

Добавяне на нов коментар