Մյուսը սխալ թեստավորման մասին

Մի անգամ պատահաբար աչքիցս անցավ կոդ, որով օգտատերն փորձում էր մոնիտորել RAM-ի կատարողականությունը իր վիրտուալ մեքենայում։ Այս կոդը չեմ ներկայացնի (դ allí «պորտյանկա»), միայն ամենակարևորը թողնում եմ։ Ուստի, կատուն ուսումնարանում!

#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-ն «ագղի» page fault, այնուհետև իրական հասցեն հատկացվում է վիրտուալ էջին, որի վրա հասանելիություն է լինում։

Ուստի իրականում մենք թեստավորում ենք ոչ թե 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

Այն այլ բան է։

morals — եթե ձեզ պահանջվում են մեծ բուֆերներ արագ աշխատելու համար, մի մոռացեք դրանք նախաձեռնման։

Ընտանիք: habr.com

Գնել հուսալի հյուրընկալում DDoS պաշտպանությամբ, VPS VDS սերվերներով 🔥 Գնել հուսալի հյուրընկալում DDoS պաշտպանությամբ, VPS VDS սերվերներով | ProHoster