Մի փոքր ավելին վատ փորձարկման մասին

Մի օր ես պատահաբար հանդիպեցի կոդ, որ օգտվողը փորձում էր վերահսկել 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 Գբ / վրկ

Մոտավորապես 4 ԳԲ/վ:

Ինչ?!?!

Ինչպես?!?!?

Սա Core i7 է (թեև ոչ ամենաթարմը), DDR4, պրոցեսորը գրեթե բեռնված չէ - ԻՆՉՈՒ?!?!

Պատասխանը, ինչպես միշտ, անսովոր սովորական է.

Նոր օպերատորը (ի դեպ, 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 Գբ / վրկ

Այլ բան է.

Պատմության բարոյականությունը. եթե ձեզ անհրաժեշտ են մեծ բուֆերներ արագ աշխատելու համար, մի մոռացեք դրանք սկզբնավորել:

Source: www.habr.com

Добавить комментарий