Производительность Linux / Perl Mmap - PullRequest
10 голосов
/ 27 июня 2009

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

В качестве простого теста я просто отображаю файл (используя модуль perl Sys :: Mmap, используя подпрограмму «mmap», который, как я считаю, отображается непосредственно в лежащую в основе функцию C), и засыпаю процесс. При этом код тратит больше минуты, прежде чем возвращается из вызова mmap, несмотря на то, что этот тест ничего не делает - даже не читает - из файла mmap.

Догадываясь, я думал, что, возможно, linux требовал, чтобы весь файл читался при первом mmap'е, поэтому после того, как файл был отображен в первом процессе (пока он спал), я вызвал простой тест в другом процессе, который пытался прочитать первые несколько мегабайт файла.

Удивительно, но второй процесс также тратит много времени, прежде чем вернуться из вызова mmap, примерно в то же время, что и mmap'инг файла в первый раз.

Я убедился, что MAP_SHARED используется и что процесс, который отобразил файл в первый раз, все еще активен (что он не завершен и что mmap не был отображен).

Я ожидал, что mmapped-файл позволит мне предоставить нескольким рабочим процессам эффективный произвольный доступ к большому файлу, но если каждый вызов mmap требует сначала прочитать весь файл, это немного сложнее. Я не проверял использование долго выполняющихся процессов, чтобы увидеть, быстрый ли доступ после первой задержки, но я ожидал, что с помощью MAP_SHARED и другого отдельного процесса будет достаточно.

Моя теория состояла в том, что mmap будет возвращать более или менее немедленно, и что linux будет загружать блоки более или менее по требованию, но поведение, которое я наблюдаю, является противоположным, указывая на то, что при каждом вызове требуется чтение всего файла. в mmap.

Есть идеи, что я делаю не так, или если я совершенно не понял, как должен работать mmap?

Ответы [ 9 ]

16 голосов
/ 27 июня 2009

ок, нашел проблему. Как и предполагалось, ни Linux, ни Perl не были виноваты. Чтобы открыть и получить доступ к файлу, я делаю что-то вроде этого:

#!/usr/bin/perl
# Create 1 GB file if you do not have one:
# dd if=/dev/urandom of=test.bin bs=1048576 count=1000
use strict; use warnings;
use Sys::Mmap;

open (my $fh, "<test.bin")
    || die "open: $!";

my $t = time;
print STDERR "mmapping.. ";
mmap (my $mh, 0, PROT_READ, MAP_SHARED, $fh)
    || die "mmap: $!";
my $str = unpack ("A1024", substr ($mh, 0, 1024));
print STDERR " ", time-$t, " seconds\nsleeping..";

sleep (60*60);

Если вы тестируете этот код, нет никаких задержек, подобных тем, которые я обнаружил в моем исходном коде, и после создания минимального примера (всегда делайте это, верно!) Причина внезапно стала очевидной.

Ошибка заключалась в том, что я в своем коде рассматривал скаляр $mh как ручку, что-то легкое и легко перемещаемое (читай: проход по значению). Оказывается, это на самом деле длинная строка в ГБ, определенно не то, что вы хотите перемещать без создания явной ссылки (perl lingua для значения «указатель» / дескриптор). Поэтому, если вам нужно сохранить в хеш-коде или аналогичном коде, убедитесь, что вы сохранили \$mh и разыменовываете его, когда вам нужно использовать его как ${$hash->{mh}}, обычно в качестве первого параметра в substr или аналогичном.

8 голосов
/ 27 июня 2009

Если у вас относительно свежая версия Perl, вам не следует использовать Sys :: Mmap. Вы должны использовать слой PerlIO mmap .

Можете ли вы опубликовать код, который вы используете?

3 голосов
/ 28 июня 2009

В 32-битных системах адресное пространство для mmap() s довольно ограничено (и варьируется от ОС к ОС). Помните об этом, если вы используете файлы объемом в несколько гигабайт и тестируете только на 64-битной системе. (я бы предпочел написать это в комментарии, но у меня пока недостаточно очков репутации)

1 голос
/ 29 июня 2009

Одной вещью, которая может помочь производительности, является использование 'madvise (2)'. наверное легче всего сделано через Inline :: C. «madvise» позволяет вам сообщить ядру, каким будет ваш шаблон доступа (например, последовательный, случайный и т. д.).

0 голосов
/ 11 декабря 2012

Ваш доступ к этому файлу должен быть случайным, чтобы оправдать полный mmap. Если ваше использование не распределяется равномерно, вам, вероятно, лучше поискать, прочитать в недавно выделенную область и обработать, освободить, промыть и повторить. И работать с кусками, кратными 4k, скажем, 64k или около того.

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

0 голосов
/ 30 июня 2009

Если я могу подключить свой собственный модуль: я бы посоветовал использовать File :: Map вместо Sys :: Mmap . Это намного проще в использовании и менее подвержено сбоям, чем Sys :: Mmap.

0 голосов
/ 29 июня 2009

Хорошо, вот еще одно обновление. Использование атрибута Sys :: Mmap или PerlIO ": mmap" прекрасно работает в perl, но только до 2 ГБ файлов (магическое ограничение 32 бита). Если размер файла превышает 2 ГБ, появляются следующие проблемы:

Используя Sys :: Mmap и substr для доступа к файлу, кажется, что substr принимает только 32-битное int для параметра position, даже в системах, где perl поддерживает 64-битную. Об этом написано как минимум одно сообщение об ошибке:

# 62646: Максимальная длина строки с подстрокой

Используя open(my $fh, "<:mmap", "bigfile.bin"), когда размер файла превышает 2 ГБ, кажется, что perl либо зависнет, либо настаивает на чтении всего файла при первом чтении (не уверен, что, я никогда не запускал его достаточно долго, чтобы увидеть, если он завершено), что приводит к очень медленной работе.

Я не нашел обходного пути ни к одному из них, и в настоящее время я застрял с медленными операциями с файлами (без mmap) для работы с этими файлами. Если я не найду обходного пути, мне, возможно, придется реализовать обработку на C или другом языке более высокого уровня, который лучше поддерживает mmap для больших файлов.

0 голосов
/ 28 июня 2009

См. Wide Finder для производительности perl с mmap. Но есть одна большая ловушка. Если ваш набор данных будет на классическом HD, и вы будете читать из нескольких процессов, вы можете легко попасть в произвольный доступ, и ваш IO упадет до недопустимых значений (20 ~ 40 раз).

0 голосов
/ 27 июня 2009

Звучит удивительно. Почему бы не попробовать чистую версию C?

Или попробуйте свой код в другой версии OS / perl.

...