2012-04-19 14 views
8

De 192 GB de RAM instalados en mi computadora, tengo 188GB RAM por encima de 4GB (en la dirección de hardware 0x100000000) reservados por el kernel Linux en el arranque (mem = 4G memmap = 188G $ 4G). Los módulos del kernel de adquisición de datos acumulan datos en esta gran área utilizada como un buffer en anillo usando DMA. Una aplicación de espacio de usuario mmap almacena este búfer de anillo en el espacio de usuario, luego copia los bloques del búfer de anillo en la ubicación actual para procesarlos una vez que estén listos.Pobre rendimiento memcpy en el espacio de usuario para memoria física mmap'ed en Linux

Copiando estos bloques de 16MB del área mmap'ed utilizando memcpy no funciona como esperaba. Parece que el rendimiento depende del tamaño de la memoria reservada en el momento del arranque (y más tarde mmap'ed en el espacio del usuario). http://www.wurmsdobler.org/files/resmem.zip contiene el código fuente para un módulo de núcleo que hace implementa la operación de archivo mmap:

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; 
} 

y una aplicación de prueba, que hace en esencia (con los controles eliminados):

#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; 

he llevado memcpy a cabo pruebas de un bloque de datos de 16 MB para los diferentes tamaños de memoria RAM reservada (resmem_length) en Ubuntu 10.04.4, Linux 2.6.32, en un SuperMicro 1026GT-TF-FM109:

|  |   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) 

Mis observaciones son:

  1. De la primera a la segunda pasada, establecimiento de memoria de mmap'ed a malloc'ed parece beneficiar a los contenidos ya que podrían ser cacheados en alguna parte.

  2. Existe una degradación significativa del rendimiento de> 64 GB, que se puede notar cuando se utiliza una memcpy.

Me gustaría entender por qué eso es así. Tal vez alguien en el pensamiento de grupo desarrolladores del núcleo Linux: 64 GB debería ser suficiente para cualquiera (hace esto suena?)

Saludos cordiales, peter

Respuesta

1

Su CPU probablemente no tiene suficiente memoria caché para tratar con él eficientemente. Use memoria más baja o obtenga una CPU con una memoria caché más grande.

+0

Hola Ignacio, puede que tengas razón. La computadora está equipada con dos Intel Xeon E5620 2.4GHz quad core con 12MB L3 caché cada uno y 1066MHz de velocidad de memoria. – PeterW

+0

Mi visión simplista es que para la primera operación de lectura, los contenidos en la RAM se almacenarían en caché, y la segunda solicitud se serviría directamente desde el caché, siempre que la cantidad se ajuste a la caché. Hubiera pensado que la cantidad de datos transferidos tendría un efecto en memcpy, en mi caso <12MB, pero no en el tamaño total de la memoria instalada o en la memoria RAM de los datos. – PeterW

+0

Pruebas adicionales han demostrado que la misma degradación del rendimiento se muestra para bloques de datos más pequeños, p. 1MB. Solo parece depender de la cantidad de memoria reservada en el momento del arranque, es decir, no más de 64 GB. – PeterW

2

Según los comentarios de SuperMicro, la degradación del rendimiento se debe a NUMA, acceso a la memoria no uniforme. El SuperMicro 1026GT-TF-FM109 utiliza la placa base X8DTG-DF con un chipset Intel 5520 Tylersburg en su corazón, conectado a dos CPUs Intel Xeon E5620, cada una de las cuales tiene 96GB de RAM conectados.

Si bloqueo mi aplicación a CPU0, puedo observar diferentes velocidades de memcpy dependiendo de qué área de memoria estaba reservada y consecuentemente mmap'ed. Si el área de memoria reservada está fuera de la CPU, a continuación, mmap luchas por algún tiempo para hacer su trabajo, y cualquier subsiguiente establecimiento de memoria hacia y desde el área de "a distancia" consume más tiempo (datos de bloque de tamaño = 16 MB):

resmem=64G$4G (inside CPU0 realm): 3949MB/s 
resmem=64G$96G (outside CPU0 realm): 82MB/s 
resmem=64G$128G (outside CPU0 realm): 3948MB/s 
resmem=92G$4G (inside CPU0 realm): 3966MB/s    
resmem=92G$100G (outside CPU0 realm): 57MB/s 

Tiene casi sentido. Solo el tercer caso, 64G $ 128, lo que significa que los 64GB superiores también producen buenos resultados. Esto contradice de alguna manera la teoría.

Saludos, Peter

+0

Si cada dominio NUMA tiene 96 GiB de RAM, entonces la prueba "64g $ 128G" sería la mitad en el primer dominio NUMA y la mitad en el segundo dominio NUMA. Es probable que la última prueba ("92G $ 100G") sea excesivamente peligrosa: el firmware normalmente reserva RAM para varias cosas y la RAM en la parte superior se puede usar para cosas como áreas de almacenamiento de estado de SMM y RAM no utilizable que el software aleatorio puede basura. – Brendan

Cuestiones relacionadas