Schlechte Speicherleistung im Benutzerbereich für mmap'ed physischen Speicher in Linux

Von 192 GB RAM, die auf meinem Computer installiert sind, habe ich 188 GB RAM über 4 GB (bei Hardware-Adresse 0x100000000), die vom Linux-Kernel beim Booten reserviert wurden (mem = 4G memmap = 188G $ 4G). Ein Datenerfassungskernmodul sammelt Daten in diesem großen Bereich, der als Ringpuffer unter Verwendung von DMA verwendet wird. Eine Anwendung für Benutzerbereiche ordnet diesen Ringpuffer dem Benutzerbereich zu und kopiert dann Blöcke aus dem Ringpuffer an der aktuellen Position zur Verarbeitung, sobald sie fertig sind.

Das Kopieren dieser 16-MB-Blöcke aus dem mmap-Bereich mit memcpy funktioniert nicht wie erwartet. Es scheint, dass die Leistung von der Größe des Speichers abhängt, der zum Startzeitpunkt reserviert (und später in den Benutzerbereich eingeordnet) wird.http://www.wurmsdobler.org/files/resmem.zip enthält den Quellcode für ein Kernelmodul, das die mmap-Dateioperation implementiert:

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

und eine Testanwendung, die im Wesentlichen (mit entfernten Häkchen) funktioniert:

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

Ich habe Memcpy-Tests eines 16-MB-Datenblocks für die verschiedenen Größen des reservierten Arbeitsspeichers (resmem_length) unter Ubuntu 10.04.4, Linux 2.6.32 und einem SuperMicro 1026GT-TF-FM109 durchgeführt:

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

Meine Beobachtungen sind:

Vom ersten bis zum zweiten Durchgang scheint das Speichern von Memcpy zwischen mmap'ed und malloc'ed davon zu profitieren, dass der Inhalt möglicherweise bereits irgendwo zwischengespeichert ist.

Es gibt eine signifikante Leistungsverschlechterung von> 64 GB, die sowohl bei Verwendung eines Memcpy festgestellt werden kann.

Ich würde gerne verstehen, warum das so ist. Vielleicht dachte jemand in der Linux-Kernel-Entwicklergruppe: 64 GB sollten für jeden reichen (klingelt das?)

Herzliche Grüße, Peter

Antworten auf die Frage(2)

Ihre Antwort auf die Frage