Jednoho dne jsem náhodou narazil na kód, kterým se uživatel pokoušel sledovat výkon RAM ve svém virtuálním počítači. Tento kód neposkytnu (je tam „nánožník“) a nechám jen to nejnutnější. Takže kočka je ve studiu!
#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;
}
Je to jednoduché – přidělte paměť a zapište do ní jeden gigabajt. A co tento test ukazuje?
$ ./memtest
4.06504 GB / s
Přibližně 4 GB/s.
Co?!?!
Jak?!?!?
Jedná se o Core i7 (i když ne nejnovější), DDR4, procesor téměř nezatížený - PROČ?!?!
Odpověď je jako vždy neobvykle obyčejná.
Operátor new (mimochodem jako funkce malloc) ve skutečnosti paměť nepřiděluje. Při tomto volání se alokátor podívá na seznam volných míst ve fondu paměti, a pokud tam žádná nejsou, zavolá sbrk() ke zvětšení datového segmentu a poté vrátí programu odkaz na adresu z nového umístění právě přidělené.
Problém je, že přidělená oblast je zcela virtuální. Nejsou přiděleny žádné stránky skutečné paměti.
A když dojde k prvnímu přístupu ke každé stránce z tohoto přiděleného segmentu, MMU „vystřelí“ chybu stránky, po které je virtuální stránce, ke které se přistupuje, přiřazena skutečná stránka.
Ve skutečnosti tedy netestujeme výkon sběrnice a modulů RAM, ale výkon MMU a VMM operačního systému. A abychom mohli otestovat skutečný výkon RAM, stačí jednou inicializovat přidělené oblasti. Například takto:
#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;
}
To znamená, že jednoduše inicializujeme přidělené buffery s výchozí hodnotou (char 0).
Zkontrolujeme:
$ ./memtest
28.5714 GB / s
Další věc.
Morálka příběhu – pokud potřebujete velké buffery k rychlé práci, nezapomeňte je inicializovat.
Zdroj: www.habr.com