2012-04-19 10 views
8

Di 192 GB di RAM installata sul mio computer, ho 188 GB di RAM superiore a 4 GB (all'indirizzo hardware 0x100000000) riservato dal kernel Linux all'avvio (mem = 4G memmap = 188g $ 4G). I moduli del kernel di acquisizione dati accumulano i dati in questa vasta area utilizzata come buffer circolare utilizzando DMA. Un'applicazione utente mmap memorizza questo buffer ad anello nello spazio utente, quindi copia i blocchi dal buffer circolare nella posizione corrente per l'elaborazione una volta che sono pronti.Scarse prestazioni memcpy nello spazio utente per la memoria fisica di mmap in Linux

La copia di questi blocchi da 16 MB dall'area di mmap'utilizzando memcpy non funziona come previsto. Sembra che le prestazioni dipendano dalla dimensione della memoria riservata al momento dell'avvio (e successivamente memorizzata nello spazio utente). http://www.wurmsdobler.org/files/resmem.zip contiene il codice sorgente di un modulo del kernel che fa implementa l'operazione sul file 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; 
} 

e un'applicazione di test, che fa in sostanza (con i controlli rimossi):

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

ho portato prove memcpy di un blocco di dati di 16 MB per le diverse dimensioni di RAM riservata (resmem_length) su Ubuntu 10.04.4, Linux 2.6.32, su 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) 

Le mie osservazioni sono:

  1. Dalla prima alla seconda corsa, memcpy da mmap'ed a malloc'ed sembra trarre beneficio che i contenuti potrebbero già essere memorizzate nella cache da qualche parte.

  2. C'è un significativo peggioramento delle prestazioni da> 64 GB, che può essere notato sia quando si utilizza una memcpy.

Mi piacerebbe capire perché lo sia. Forse qualcuno nel pensiero di gruppo sviluppatori del kernel Linux: 64GB dovrebbe essere sufficiente per chiunque (fa suonare un campanello?)

Cordiali saluti, peter

risposta

1

La CPU probabilmente non ha abbastanza di cache a che fare con esso in modo efficiente. Utilizzare la memoria inferiore o ottenere una CPU con una cache più grande.

+0

Ciao Ignacio, si può essere di destra. Il computer è dotato di due Intel Xeon E5620 2.Quad core 4GHz con 12 MB di cache L3 ciascuno e velocità di memoria 1066 MHz. – PeterW

+0

La mia visione semplicistica è che per la prima operazione di lettura il contenuto nella RAM verrebbe memorizzato nella cache, e la seconda richiesta verrebbe pubblicata direttamente dalla cache, a condizione che l'importo si adatti alla cache. Avrei pensato che la quantità di dati trasferiti avrebbe avuto un effetto sulla memcpy, nel mio caso <12 MB, ma non sulla dimensione totale della memoria adattata o sulla RAM dei dati. – PeterW

+0

Ulteriori test hanno dimostrato che lo stesso degrado delle prestazioni si mostra per blocchi di dati più piccoli, ad es. 1MB. Mi sembra che dipenda solo dalla quantità di memoria riservata all'avvio, cioè non più di 64 GB. – PeterW

2

Sulla base del feedback ricevuto da SuperMicro, il degrado delle prestazioni è dovuto a NUMA, accesso non uniforme alla memoria. Il SuperMicro 1026GT-TF-FM109 utilizza la scheda madre X8DTG-DF con un chipset Intel 5520 Tylersburg al centro, collegato a due CPU Intel Xeon E5620, ognuna delle quali ha 96 GB di RAM.

Se blocco la mia applicazione su CPU0, posso osservare diverse velocità di memcpy a seconda di quale area di memoria è stata prenotata e di conseguenza memorizzata. Se l'area di memoria riservata è off-CPU, quindi mmap lotte per qualche tempo per fare il suo lavoro, e ogni successiva memcpy da e verso la zona "remota" consuma più tempo (dati blocco size = 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 

Ha quasi senso. Solo il terzo caso, 64G $ 128, il che significa che i 64 GB più alti producono anche buoni risultati. Questo contraddice in qualche modo la teoria.

Saluti, Peter

+0

Se ciascun dominio NUMA ha 96 GiB di RAM, il test "64 g $ 128 G" sarà metà nel primo dominio NUMA e metà nel secondo dominio NUMA. L'ultimo test ("92G $ 100G") rischia di essere eccessivamente pericoloso - il firmware in genere riserva un po 'di RAM per varie cose e la RAM in alto può essere usata per cose come le aree di salvataggio dello stato SMM e non la RAM libera/utilizzabile che il software casuale può spazzatura. – Brendan

Problemi correlati