2009-06-27 4 views
9

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

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

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

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

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

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

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

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

ответ

15

Хорошо, нашел проблему. Как подозревали, ни 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 как ручку, что-то легкое и легко перемещается (читайте: pass by value). Оказывается, на самом деле это длинная строка в формате GB, окончательно не то, что вы хотите перемещать, не создавая явной ссылки (perl lingua для значения «указатель»/дескриптор).Поэтому, если вам нужно хранить в хеше или аналогичном, убедитесь, что вы храните \$mh, и разрешите его, когда вам нужно использовать его, как ${$hash->{mh}}, обычно как первый параметр в подстроке или аналогичный.

+3

+1 для последующего подробного объяснения. – RichieHindle

+3

Используйте форму 3 arg open(). –

0

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

Попробуйте код на другой версии ОС/perl.

+0

Я посмотрел интерфейс Perl OS, и он вызывает версию C более или менее напрямую, но если я не выясню это, я, вероятно, также проведу версию C. Что касается OS/perl-версии, то я тестировал две системы - x86_64. Один из них - Ubuntu 8.04.2 (linux 2.6.24-22, perl 5.8.8), а другой Ubuntu 9.04 (linux 2.6.28-13, perl 5.10.0). Такое же поведение.Вторая система была ноутбуком, и я могу окончательно подтвердить, что существует серьезный диск io, связанный с вызовом mmap из моих тестов. –

8

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

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

+0

Согласитесь, слой PerlIO mmap, вероятно, предпочтителен, так как он также позволит запустить один и тот же код с/без mmap'ing, просто добавив/удалив атрибут mmap. Независимо, я нашел проблему, разместил код, проблема решена. –

+0

Задайте эту проблему до 2 ГБ. Для больших файлов perl все еще есть проблемы, см. Мой другой ответ, связанный с этим. –

+0

Работает ли слой mmap PerlIO для доступа к куску/dev/mem для чтения/записи? – donaldh

3

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

+0

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

+0

Как я уже писал в своем другом ответе, даже на 64-битных системах все еще есть проблемы для более крупных файлов (> 2 ГБ). Однако ваш ответ правильный. Я уже 64-битный на всех своих машинах, даже на ноутбуке, поэтому для меня это не проблема. –

0

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

+0

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

+0

Если вы действительно нуждаетесь в случайном доступе к огромному файлу, лучшего решения нет. –

+0

Лучшим решением является очередь в очереди запросов на чтение, ограниченная коротким таймаутом, а затем считывание блоков с диска в оптимальном порядке, чтобы минимизировать время поиска. Я не уверен, что какие-либо файловые системы уже это делают, я не думаю, что ZFS. Он будет работать лучше всего при выполнении многих параллельных процессов, например. веб-сервер или другой API IO. –

1

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

0

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

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

#62646: Maximum string length with substr

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

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

+1

попробуйте использовать mmap из Sys :: Mmap непосредственно, чтобы создать скользящее окно в скаляре. –

+0

Спасибо, это, безусловно, обходной путь. Это потребовало бы отслеживания указателя в файл и отображения/развязки, когда это необходимо, что, вероятно, влияет на производительность. Но это, вероятно, еще быстрее, чем прямой файл ввода-вывода. –

+0

Проведенный бенчмаркинг, подтверждающий динамическую сопоставление/снятие карты с использованием размера сегмента 2 ГБ и предполагая, что сегментные коммутаторы довольно редки, скорость примерно на 30-40% выше, используя mmap с unmap/mapping, чем прямой файл ввода-вывода на 3 ГБ файл. В 2-Гбайт-файле разница меньше, но я подозреваю, что это связано с тем, что мой ноутбук кэширует большую часть файла во время произвольного доступа в любом случае. По крайней мере, у меня есть решение, которое работает, хотя и не так чисто, как я бы надеялся. Однако на этой стадии нет необходимости в дальнейшей оптимизации. –

0

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

+0

Вот предложение для новой очень полезной функции, основанной на моем наблюдении perl, описанном в этом потоке (файлы с отображением памяти, работающие до 2 ГБ); если пользователь сопоставляет файл размером более 2 ГБ, используйте сегментированный подход с «пользовательской» функцией чтения, которая автоматически отменяет/отображает по мере необходимости. По крайней мере, до тех пор, пока не будет исправлен «ошибка» 2 ГБ. –

0

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

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