2012-04-19 21 views
8

Z 192 GB pamięci RAM zainstalowanej na moim komputerze, mam 188 GB pamięci RAM powyżej 4 GB (pod adresem sprzętowym 0x100000000) zarezerwowane przez jądro Linux podczas startu systemu (mem = 4G memmap = 188G 4G). Moduły jądra gromadzące dane gromadzą dane w tym dużym obszarze, używanym jako bufor pierścieniowy przy użyciu DMA. Aplikacja przestrzeni użytkownika mmap przekształca ten bufor pierścieniowy w przestrzeń użytkownika, a następnie kopiuje bloki z bufora pierścieniowego w bieżącej lokalizacji w celu przetworzenia, gdy będą gotowe.Słaba wydajność nagrywania w przestrzeni użytkownika dla mmap'ed pamięci fizycznej w systemie Linux

Kopiowanie tych bloków 16 MB z obszaru mmap'ed za pomocą memcpy nie działa zgodnie z oczekiwaniami. Wydaje się, że wydajność zależy od wielkości pamięci zarezerwowanej w czasie rozruchu (i późniejszej mmap'ed w przestrzeni użytkownika). http://www.wurmsdobler.org/files/resmem.zip zawiera kod źródłowy moduł jądra, która nie wykonuje operację pliku 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; 
} 

i stosowania testów, które wykonuje w zasadzie (z usuniętą kontrole):

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

I prowadzone z memcpy testów bloku danych 16 MB dla różnych rozmiarów przeznaczonym RAM (resmem_length) Ubuntu 10.04.4, Linux 2.6.32, na 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) 

Moje spostrzeżenia są następujące:

  1. Od pierwszego do drugiego biegu, memcpy z mmap-owany do malloc'ed wydaje się korzystne, że zawartość może już być przechowywane gdzieś.

  2. Występuje znaczny spadek wydajności od> 64 GB, co można zauważyć zarówno podczas korzystania z memcpy.

Chciałbym zrozumieć, dlaczego tak jest. Może ktoś w grupie programistów jądra Linux myśli: 64GB powinno wystarczyć każdemu (robi to pierścień dzwon?)

poważaniem, peter

Odpowiedz

1

Twój CPU prawdopodobnie nie ma wystarczającej ilości pamięci podręcznej, aby sobie z tym poradzić wydajnie. Użyj niższej pamięci lub uzyskaj procesor z większą pamięcią podręczną.

+0

Witam Ignacio, może mieć rację. Komputer jest wyposażony w dwa procesory Intel Xeon E5620 2.Czterordzeniowy procesor 4GHz z pamięcią podręczną 12MB L3 i prędkością 1066 MHz. – PeterW

+0

Mój uproszczony widok jest taki, że dla pierwszej operacji odczytu zawartość pamięci RAM byłaby buforowana, a drugie żądanie byłoby podawane bezpośrednio z pamięci podręcznej, o ile wielkość mieści się w pamięci podręcznej. Sądziłem, że ilość przesłanych danych będzie miała wpływ na memcpy, w moim przypadku <12 MB, ale nie na całkowity rozmiar pamięci zainstalowanej lub gdzie w pamięci RAM są dane. – PeterW

+0

Dalsze testy wykazały, że ta sama degradacja wydajności dotyczy mniejszych bloków danych, np. 1 MB. Wydaje mi się, że zależy to tylko od ilości pamięci zarezerwowanej podczas rozruchu, czyli nie więcej niż 64 GB. – PeterW

2

Na podstawie opinii od SuperMicro, obniżenie wydajności wynika z niejednorodnego dostępu do pamięci NUMA. SuperMicro 1026GT-TF-FM109 wykorzystuje płytę główną X8DTG-DF z jednym chipsetem Intel 5520 Tylersburg, połączoną z dwoma procesorami Intel Xeon E5620, z których każdy ma dołączoną 96 GB RAM.

Jeśli zablokuję aplikację na CPU0, mogę obserwować różne prędkości memcpy w zależności od tego, jaki obszar pamięci został zarezerwowany iw związku z tym mmap'ed. Jeśli zastrzeżony obszar pamięci jest off-CPU, następnie mmap zmagania na jakiś czas wykonywać swoje prace, a każda następna memcpy do iz „zdalnym” obszar pochłania więcej czasu (dane block size = 16MB):

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 

To prawie ma sens. Tylko trzeci przypadek, 64G $ 128, co oznacza najwyższy 64GB również przynosi dobre wyniki. To w pewien sposób przeczy teorii.

Pozdrawiam, Peter

+0

Jeśli każda domena NUMA ma 96 GiB pamięci RAM, wówczas test "64g $ 128G" będzie o połowę w pierwszej domenie NUMA, a połowa w drugiej domenie NUMA. Ostatni test ("92G $ 100G") może być nadmiernie niebezpieczny - oprogramowanie układowe zwykle rezerwuje trochę pamięci RAM na różne rzeczy, a pamięć RAM na górze może być używana do takich rzeczy, jak obszary zapisu stanu SMM, a nie darmowa/użyteczna pamięć RAM, którą losowe oprogramowanie może śmieci. – Brendan

Powiązane problemy