Um dia, acidentalmente, chamei a atenção um código que um usuário estava tentando monitorar o desempenho da RAM em sua máquina virtual. Não darei esse código (existe um "calçado") e deixo apenas o mais essencial. Então, o gato está no estúdio!#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/s\n";
for (int i=0;i<CNT;i++) {
delete buffers[i];
}
delete buffers;
return 0;
}
Tudo é simples - alocamos memória e escrevemos um gigabyte nela. E o que esse teste mostra?$ ./memtest
4,06504 GB / sAproximadamente 4 GB / s.O que?!?!Quão?!?!?Este é o Core i7 (embora não seja o mais novo), DDR4, o processador quase não está carregado - POR QUE?!?!A resposta, como sempre, é extraordinariamente comum.O novo operador (como a função malloc, a propósito) não aloca realmente memória. Com essa chamada, o alocador examina a lista de seções livres no pool de memória e, se não houver nenhuma, chama sbrk () para aumentar o segmento de dados e retorna ao programa um link para o endereço da seção recém-alocada.O problema é que a área selecionada é completamente virtual. Páginas de memória real não estão alocadas.E quando o primeiro acesso a cada página desse segmento selecionado ocorre, a MMU "dispara" a falha da página, após o que a página virtual à qual o acesso é feito é atribuída à real.Portanto, na verdade, estamos testando não o desempenho dos módulos de barramento e RAM, mas o desempenho do MMU e VMM do sistema operacional. E, para testar o desempenho real da RAM, precisamos apenas inicializar as seções alocadas uma vez. Por exemplo, assim:#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/s\n";
for (int i=0;i<CNT;i++) {
delete buffers[i];
}
delete buffers;
return 0;
}
Ou seja, simplesmente inicializamos os buffers alocados com o valor padrão (caractere 0).Verificação:$ ./memtest
28.5714 GB / sOutra coisa.Moral - se você precisar de buffers grandes para trabalhar com rapidez e rapidez, não esqueça de inicializá-los.