2015-01-08 3 views
6

Я пишу код, анализирующий файлы журнала, с предостережением, что эти файлы сжаты и должны быть несжаты на лету. Этот код представляет собой несколько высокочувствительный фрагмент кода, поэтому я пытаюсь использовать различные методы, чтобы найти правильный. У меня по существу столько оперативной памяти, сколько потребуется программе, независимо от того, сколько потоков я использую.mmap vs. malloc: странная производительность

Я нашел метод, который, кажется, работает достаточно хорошо, и я пытаюсь понять, почему он предлагает лучшую производительность.

Оба метода имеют поток считывателя, который считывает из процесса gzip с каналами и записывает в большой буфер. Этот буфер затем анализируется при анализе следующей строки журнала, возвращая то, что по существу является структурой указателей на то, где в буфере находятся разные поля.

Код находится в D, но он очень похож на C или C++.

Общая переменная:

shared(bool) _stream_empty = false;; 
shared(ulong) upper_bound = 0; 
shared(ulong) curr_index = 0; 

Синтаксический код:

//Lazily parse the buffer 
void construct_next_elem() { 

    while(1) { 
     // Spin to stop us from getting ahead of the reader thread 
     buffer_empty = curr_index >= upper_bound -1 && 
         _stream_empty; 
     if(curr_index >= upper_bound && !_stream_empty) { 
      continue; 
     } 
     // Parsing logic ..... 
    } 
} 

Метод 1: Malloc буфер достаточно большой, чтобы вместить распакованный файл вперед.

char[] buffer;     // Same as vector<char> in C++ 
buffer.length = buffer_length; // Same as vector reserve in C++ or malloc 

Метод 2: Использование анонимной карты памяти в качестве буфера

MmFile buffer; 
buffer = new MmFile(null, 
        MmFile.Mode.readWrite, // PROT_READ || PROT_WRITE 
        buffer_length, 
        null);     // MAP_ANON || MAP_PRIVATE 

Считыватель резьба:

ulong buffer_length = get_gzip_length(file_path); 
pipe = pipeProcess(["gunzip", "-c", file_path], 
            Redirect.stdout); 
stream = pipe.stdout(); 

static void stream_data() { 
    while(!l.stream.eof()) { 

     // Splice is a reference inside the buffer 
     char[] splice = buffer[upper_bound..upper_bound + READ_SIZE]; 
     ulong read = stream.rawRead(splice).length; 
     upper_bound += read; 
    } 
    // Clean up 
} 

void start_stream() { 
     auto t = task!stream_data(); 
     t.executeInNewThread(); 
     construct_next_elem(); 
} 

Я получаю значительно более высокую производительность из метода 1, даже на по порядку величины

User time (seconds): 112.22 
System time (seconds): 38.56 
Percent of CPU this job got: 151% 
Elapsed (wall clock) time (h:mm:ss or m:ss): 1:39.40 
Average shared text size (kbytes): 0 
Average unshared data size (kbytes): 0 
Average stack size (kbytes): 0 
Average total size (kbytes): 0 
Maximum resident set size (kbytes): 3784992 
Average resident set size (kbytes): 0 
Major (requiring I/O) page faults: 0 
Minor (reclaiming a frame) page faults: 5463 
Voluntary context switches: 90707 
Involuntary context switches: 2838 
Swaps: 0 
File system inputs: 0 
File system outputs: 0 
Socket messages sent: 0 
Socket messages received: 0 
Signals delivered: 0 
Page size (bytes): 4096 
Exit status: 0 

против

User time (seconds): 275.92 
System time (seconds): 73.92 
Percent of CPU this job got: 117% 
Elapsed (wall clock) time (h:mm:ss or m:ss): 4:58.73 
Average shared text size (kbytes): 0 
Average unshared data size (kbytes): 0 
Average stack size (kbytes): 0 
Average total size (kbytes): 0 
Maximum resident set size (kbytes): 3777336 
Average resident set size (kbytes): 0 
Major (requiring I/O) page faults: 0 
Minor (reclaiming a frame) page faults: 944779 
Voluntary context switches: 89305 
Involuntary context switches: 9836 
Swaps: 0 
File system inputs: 0 
File system outputs: 0 
Socket messages sent: 0 
Socket messages received: 0 
Signals delivered: 0 
Page size (bytes): 4096 
Exit status: 0 

Как способом больше ошибок страниц с помощью метода 2.

Может кто-то помочь мне пролить свет на то, почему существует такое суровое снижение производительности с помощью ММАПА?

Если кто-нибудь знает о каких-либо лучших способах решения этой проблемы, я бы с радостью его услышал.

EDIT -----

Изменен метод 2, чтобы сделать:

 char * buffer = cast(char*)mmap(cast(void*)null, 
          buffer_length, 
          PROT_READ | PROT_WRITE, 
          MAP_ANON | MAP_PRIVATE, 
          -1, 
          0); 

теперь получает увеличение 3x производительности по сравнению с использованием простой MmFile. Я пытаюсь понять, что может привести к такой резкой разнице в производительности, что это, по сути, только обертка вокруг mmap.

Perf номера для только с использованием прямым символом * ММАПА против Mmfile, способом гораздо меньше разломов страницы:

User time (seconds): 109.99 
System time (seconds): 36.11 
Percent of CPU this job got: 151% 
Elapsed (wall clock) time (h:mm:ss or m:ss): 1:36.20 
Average shared text size (kbytes): 0 
Average unshared data size (kbytes): 0 
Average stack size (kbytes): 0 
Average total size (kbytes): 0 
Maximum resident set size (kbytes): 3777896 
Average resident set size (kbytes): 0 
Major (requiring I/O) page faults: 0 
Minor (reclaiming a frame) page faults: 2771 
Voluntary context switches: 90827 
Involuntary context switches: 2999 
Swaps: 0 
File system inputs: 0 
File system outputs: 0 
Socket messages sent: 0 
Socket messages received: 0 
Signals delivered: 0 
Page size (bytes): 4096 
Exit status: 0 
+1

Ну, ничего неожиданного. 'mmap()' заставляет файл «сбой» в chunk by schunk. Объем ввода-вывода в обоих случаях одинаковый, но он выполняется в менее желательные моменты в случае mmap(). – wildplasser

+0

и mmap использует opIndex (перегруженный метод), вместо этого вы можете вывести void [] для прямого доступа, выполнив 'buffer = mmap [];' –

+0

wildplasser. Я напрямую пишу в mmap из трубы, прежде чем обращаться к нему. Итак, я жду на страницах, чтобы загрузить, прежде чем писать им, а также иметь их впереди с malloc? Я напрямую пишу в mmap из трубы, прежде чем я прочитаю их, поэтому я бы предположил, что эти страницы будут кэшироваться некоторое время. – sprw121

ответ

0

Вы получаете PageFaults и притормаживание, потому что ММАП по умолчанию только загрузки страницы, как только вы попробуйте получить к нему доступ.

Читайте об этом знает, что вы читаете последовательно, поэтому он забирает страницы раньше времени, прежде чем вы их попросите.

Обратите внимание на вызов madvise - он предназначен для подачи сигнала в ядро, как вы собираетесь обращаться к файлу mmap, и позволяете устанавливать различные стратегии для разных частей памяти mmap - для Например, у вас есть индексный блок, который вы хотите сохранить в памяти [MADV_WILLNEED], но доступ к контенту осуществляется случайным образом и по требованию [MADV_RANDOM], или вы выполняете цикл, хотя память в последовательном сканировании [MADV_SEQUENTIAL]

Однако ОС полностью бесплатно игнорировать стратегию, которую вы устанавливаете, поэтому YMMW

+0

Вы, вероятно, хотите [madvise (2)] (http://man7.org/linux/man-pages/man2/madvise.2.html) не 'posix_fadvise' –

+0

... спасибо за исправление, да используйте' madvise '- Я должен был посмотреть на исходный код перед тем, как написать ответ :) – Soren

Смежные вопросы