Desempenho fraco do memcpy no espaço do usuário para a memória física do mmap em Linux

De 192GB de RAM instalada no meu computador, eu tenho 188GB de RAM acima de 4GB (no endereço de hardware 0x100000000) reservado pelo kernel do Linux no momento da inicialização (mem = 4G memmap = 188G $ 4G). Um módulo de aquisição de dados do kernel acumula dados nesta grande área usada como um buffer de anel usando DMA. Um aplicativo de espaço do usuário mapeia esse buffer de anel no espaço do usuário e copia blocos do buffer de anel no local atual para processamento quando estiverem prontos.

Copiar esses blocos de 16MB da área do mmap usando o memcpy não funciona como eu esperava. Parece que o desempenho depende do tamanho da memória reservada no momento da inicialização (e depois inserido no espaço do usuário).http://www.wurmsdobler.org/files/resmem.zip contém o código-fonte para um módulo do kernel que implementa a operação do arquivo mmap:

<code>module_param(resmem_hwaddr, ulong, S_IRUSR);
module_param(resmem_length, ulong, S_IRUSR);
//...
static int resmem_mmap(struct file *filp, struct vm_area_struct *vma) {
remap_pfn_range(vma, vma->vm_start,
    resmem_hwaddr >> PAGE_SHIFT,
    resmem_length, vma->vm_page_prot);
return 0;
}
</code>

e uma aplicação de teste, que faz em essência (com os cheques removidos):

<code>#define BLOCKSIZE ((size_t)16*1024*1024)
int resMemFd = ::open(RESMEM_DEV, O_RDWR | O_SYNC);
unsigned long resMemLength = 0;
::ioctl(resMemFd, RESMEM_IOC_LENGTH, &resMemLength);
void* resMemBase = ::mmap(0, resMemLength, PROT_READ | PROT_WRITE, MAP_SHARED, resMemFd, 4096);
char* source = ((char*)resMemBase) + RESMEM_HEADER_SIZE;    
char* destination = new char[BLOCKSIZE];
struct timeval start, end;
gettimeofday(&start, NULL);
memcpy(destination, source, BLOCKSIZE);
gettimeofday(&end, NULL);
float time = (end.tv_sec - start.tv_sec)*1000.0f + (end.tv_usec - start.tv_usec)/1000.0f;
    std::cout << "memcpy from mmap'ed to malloc'ed: " << time << "ms (" << BLOCKSIZE/1000.0f/time << "MB/s)" << std::endl;
</code>

Eu realizei testes de memória de um bloco de dados de 16MB para os diferentes tamanhos de RAM reservada (resmem_length) no Ubuntu 10.04.4, Linux 2.6.32, em um SuperMicro 1026GT-TF-FM109:

<code>|      |         1GB           |          4GB           |         16GB           |        64GB            |        128GB            |         188GB
|run 1 | 9.274ms (1809.06MB/s) | 11.503ms (1458.51MB/s) | 11.333ms (1480.39MB/s) |  9.326ms (1798.97MB/s) | 213.892ms (  78.43MB/s) | 206.476ms (  81.25MB/s)
|run 2 | 4.255ms (3942.94MB/s) |  4.249ms (3948.51MB/s) |  4.257ms (3941.09MB/s) |  4.298ms (3903.49MB/s) | 208.269ms (  80.55MB/s) | 200.627ms (  83.62MB/s)
</code>

Minhas observações são:

Da primeira para a segunda execução, memcpy de mmap'ed para malloc'ed parece beneficiar que o conteúdo já possa estar em cache em algum lugar.

Há uma degradação de desempenho significativa de> 64 GB, o que pode ser notado tanto ao usar um memcpy.

Eu gostaria de entender por que isso é assim. Talvez alguém no grupo de desenvolvedores do kernel do Linux tenha pensado: 64GB deve ser o suficiente para qualquer um (isso soa um pouco?)

Atenciosamente, Pedro

questionAnswers(2)

yourAnswerToTheQuestion