Аднойчы мне выпадкова патрапіў на вочы код, якім карыстач спрабаваў маніторыць прадукцыйнасць 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 Гб / с
Прыкладна 4GB/s.
Што?!?!
Як?!?!?
Гэта Core i7 (хай і не самы новы), DDR4, працэсар амаль не загружаны ЧАМУ?!?!
Адказ, як заўсёды, незвычайна звычайны.
Аператар new (як і функцыя malloc, дарэчы) насамрэч не вылучае памяць. Пры гэтым выкліку алакатар глядзіць спіс вольных участкаў у пуле памяці, і калі іх няма, выклікае sbrk() каб павялічыць сегмент дадзеных, і затым вяртае праграме спасылку на адрас з новага толькі што вылучанага ўчастку.
Праблема ў тым, што выдзелены ўчастак цалкам віртуальны. Рэальныя старонкі памяці не выдзелены.
І калі адбываецца першы зварот да кожнай старонкі з гэтага вылучанага сегмента, MMU "выстрэльвае" page fault, пасля чаго віртуальнай старонцы, да якой робіцца доступу, прызначаецца рэальная.
Таму насамрэч мы тэстуем не прадукцыйнасць шыны і модуляў RAM, а прадукцыйнасць 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 Гб / с
Іншая справа.
Мараль - калі вам патрэбныя вялікія буферы каб хутка-хутка працаваць, не забывайце іх ініцыялізаваць.
Крыніца: habr.com