کمی بیشتر در مورد تست بد

یک روز به طور تصادفی با کدی برخورد کردم که یک کاربر سعی می کرد عملکرد 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

تقریباً 4 گیگابایت بر ثانیه

چی؟!؟!

چطور؟!؟!؟

این Core i7 است (البته نه جدیدترین)، DDR4، پردازنده تقریباً بارگذاری نشده است - چرا؟!؟!

پاسخ، مثل همیشه، غیرعادی معمولی است.

عملگر جدید (به هر حال، مانند تابع malloc) در واقع حافظه را تخصیص نمی دهد. با این فراخوانی، تخصیص دهنده به لیست مکان های رایگان در استخر حافظه نگاه می کند، و اگر هیچ کدام وجود نداشته باشد، sbrk() را برای افزایش بخش داده ها فراخوانی می کند و سپس یک مرجع به آدرس از مکان جدید را به برنامه برمی گرداند. اختصاص داده شده است.

مشکل اینجاست که منطقه اختصاص داده شده کاملا مجازی است. هیچ صفحه حافظه واقعی اختصاص داده نمی شود.

و هنگامی که اولین دسترسی به هر صفحه از این بخش تخصیص داده شده رخ می دهد، MMU یک خطای صفحه را "شیک" می کند، پس از آن صفحه مجازی مورد دسترسی یک صفحه واقعی اختصاص می یابد.

بنابراین، در واقع ما در حال تست عملکرد ماژول های باس و رم نیستیم، بلکه عملکرد MMU و VMM سیستم عامل را آزمایش می کنیم. و برای آزمایش عملکرد واقعی رم، فقط باید یک بار مناطق اختصاص داده شده را مقداردهی اولیه کنیم. به عنوان مثال مانند این:

#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

اضافه کردن نظر