А чё, mmap правда тормозит?
Считается, что mmap медленнее, чем последовательное чтение.
Я чего-то не понял. Ты пытался сравнить вызов read.find с ручной реализацией find-а, но по памяти?
Скорее всего не mmap тормозит, а интерпретатор, а find быстрее отрабатывает естественно.
Скорее всего не mmap тормозит, а интерпретатор, а find быстрее отрабатывает естественно.
oops 

Нет.
mmap прикидывается строкой. В частности в нём есть метод find, написанный на С и не очень сильно отличается от str.find. Ещё он поддерживает так называемый buffer protocol (отдавая всем желающим сырой поинтер и длину поэтому модуль re (регулярные выражения) может искать по ммапнутым файлам точно так же, как по строкам.
Я сравнивал обе штуки — find и re.matchall, на ммапнутых файлах и на целиком зачитанных в строку файлах. Ммапнутые файлы тормозят, раза в три.
Я почитал релевантные сурцы, вроде ничего странного Питон не делает, значит, остаётся только предположить, что тормозят сами ммапнутые файлы, то есть ОСь. Вот, спрашиваю — это правда?
mmap прикидывается строкой. В частности в нём есть метод find, написанный на С и не очень сильно отличается от str.find. Ещё он поддерживает так называемый buffer protocol (отдавая всем желающим сырой поинтер и длину поэтому модуль re (регулярные выражения) может искать по ммапнутым файлам точно так же, как по строкам.
Я сравнивал обе штуки — find и re.matchall, на ммапнутых файлах и на целиком зачитанных в строку файлах. Ммапнутые файлы тормозят, раза в три.
Я почитал релевантные сурцы, вроде ничего странного Питон не делает, значит, остаётся только предположить, что тормозят сами ммапнутые файлы, то есть ОСь. Вот, спрашиваю — это правда?
а может
data = str(data)
чтобы исключить различия в find?
data = str(data)
чтобы исключить различия в find?
а может и правда. я всю жизнь большие файлики ммапил,
а мелкие-то можно и бумажными прочитать.
надо будет проверить ради интересу.
а мелкие-то можно и бумажными прочитать.
надо будет проверить ради интересу.
Как бы всё логично. mmap — page fault, возможно, с чтением, на каждые 4 килобайта. read — один системный вызов с последовательным чтением.
мне неочевидно. этот "один системный вызов" складывает внутри себя прочтённую страницу в ядерный кеш (в процессе находя какую-нибудь свободную страницу)
mmap — page fault, возможно, с чтением, на каждые 4 килобайта.да никто не мешает делать map-ahead как и read-ahead
тормозят сами ммапнутые файлы, то есть ОСь. Вот, спрашиваю — это правда?сомневаюсь. всякие тесты, находимые гуглом, говорят, что mmap быстрее или не медленнее. думаю, всё же python что-то делает не так:
[00:21] 2:/tmp$ perf report -i read.data -n
52.16% 2745552883 python2.7 libpython2.7.so.1.0 [.] fastsearch
18.25% 960673551 python2.7 [kernel.kallsyms] [k] copy_user_generic_string
11.50% 605541183 python2.7 [kernel.kallsyms] [k] clear_page_c
2.20% 115848328 python2.7 [kernel.kallsyms] [k] page_fault
1.61% 84824906 python2.7 [kernel.kallsyms] [k] find_get_page
1.44% 75801718 python2.7 [kernel.kallsyms] [k] file_read_actor
1.44% 75739981 python2.7 [kernel.kallsyms] [k] __rmqueue
1.19% 62656527 python2.7 [kernel.kallsyms] [k] filemap_fault
1.14% 59907998 python2.7 [kernel.kallsyms] [k] unmap_vmas
[00:17] 2:/tmp$ perf report -i mmap.data -n
93.98% 9228125820 python2.7 mmap.so [.] mmap_gfind
1.31% 128500821 python2.7 [kernel.kallsyms] [k] page_fault
0.77% 75924077 python2.7 [kernel.kallsyms] [k] find_get_page
0.77% 75903300 python2.7 [kernel.kallsyms] [k] unmap_vmas
0.61% 60000085 python2.7 ld-2.11.so [.] 0x0000000000bd48
0.43% 42359267 python2.7 [kernel.kallsyms] [k] filemap_fault
0.24% 23972596 python2.7 [kernel.kallsyms] [k] page_remove_rmap
Может быть, я уже спать хочу и не соображаю. :-) Но при чём здесь ядерный кеш, и в чём твой тезис, я не понял. :-)
Read же делается всего файла. Это один системный вызов, который читает весь файл сразу. В дальнейшем при обращении к данным из этого файла гарантированно не будет никаких page fault-ов (если только что-то не успеет уйти в swap, это не наш случай).
В случае с mmap page fault-ов будет как минимум не меньше (меньше нуля быть не может). Да, read-ahead наверняка есть. Может быть, даже map-ahead есть. Но заранее понятно, что mmap быстрее read-а быть не может. Хотя бы потому что mmap всё равно в итоге должен сделать 1 или несколько read-ов.
Read же делается всего файла. Это один системный вызов, который читает весь файл сразу. В дальнейшем при обращении к данным из этого файла гарантированно не будет никаких page fault-ов (если только что-то не успеет уйти в swap, это не наш случай).
В случае с mmap page fault-ов будет как минимум не меньше (меньше нуля быть не может). Да, read-ahead наверняка есть. Может быть, даже map-ahead есть. Но заранее понятно, что mmap быстрее read-а быть не может. Хотя бы потому что mmap всё равно в итоге должен сделать 1 или несколько read-ов.
Интересно. Результаты противоречат моим представлениям. Скинь ссылку на код тестов.
try
LC_ALL=C time echo pewpew
LC_ALL=C time echo pewpew
[00:40] 2:/tmp$ perf report -i read-faults.data -n
# Events: 487 cycles
#
# Overhead Samples Command Shared Object Symbol
# ........ .......... ......... ................. ......
#
98.15% 241068 python2.7 [kernel.kallsyms] [k] file_read_actor
1.85% 4542 python2.7 ld-2.11.so [.] 0x00000000000ba0
0.00% 2 python2.7 [kernel.kallsyms] [k] __clear_user
0.00% 1 python2.7 [kernel.kallsyms] [k] load_elf_binary
#
# (For a higher level overview, try: perf report --sort comm,dso)
#
[00:40] 2:/tmp$ perf report -i mmap-faults.data -n
# Events: 2K cycles
#
# Overhead Samples Command Shared Object Symbol
# ........ .......... ......... ................... ......
#
98.11% 240918 python2.7 mmap.so [.] mmap_gfind
1.76% 4313 python2.7 libpython2.7.so.1.0 [.] PyDict_New
0.12% 301 python2.7 ld-2.11.so [.] 0x00000000017d20
0.01% 13 python2.7 [kernel.kallsyms] [k] load_elf_binary
0.00% 2 python2.7 [kernel.kallsyms] [k] __clear_user
0.00% 1 python2.7 [kernel.kallsyms] [k] copy_user_generic_string
#
# (For a higher level overview, try: perf report --sort comm,dso)
#
:-p
Скинь ссылку на код тестов.Она же в первом посте.
После read'а ядро должно скопировать прочтённую страницу процессу (изначальная копия остаётся в кеше ядра). В случае mmap'а этого можно не делать. Из профилей это хорошо видно, кстати
Поэтому mmap легко может быть медленнее, тьфу блин, быстрее read'а при подходящих условиях
Поэтому mmap легко может быть Кстати, да, логично. Спасибо за объяснение.
Профили и про perf я ещё почитаю, как проснусь. А то и так какую-то фигню пишу. :-)
Профили и про perf я ещё почитаю, как проснусь. А то и так какую-то фигню пишу. :-)
ФЖ, шозанах, твой тест сравнивает наивный O(nm) поиск с чем там? Бойером-Муром? И ты удивляешься, что первый в 2-3 раза медленнее?
: static PyObject *
: mmap_gfind(mmap_object *self,
0.00 : 2433: 4c 89 dd mov %r11,%rbp
0.00 : 2436: 48 8d 34 3a lea (%rdx,%rdi,1%rsi
0.00 : 243a: 48 29 fd sub %rdi,%rbp
0.00 : 243d: 0f 1f 00 nopl (%rax)
: end_p = self->data + end;
:
: for (p = (reverse ? end_p - len : start_p);
: (p >= start_p) && (p + len <= end_p); p += sign) {
: Py_ssize_t i;
: for (i = 0; i < len && needle[i] == p[i]; ++i)
?:0
10.15 : 2440: 4d 85 db test %r11,%r11
0.00 : 2443: 7e 5b jle 24a0 <mmap_gfind+0x140>
?:0
0.66 : 2445: 0f b6 0a movzbl (%rdx%ecx
?:0
11.91 : 2448: 31 c0 xor %eax,%eax
?:0
0.84 : 244a: 38 0b cmp %cl%rbx)
?:0
8.57 : 244c: 74 0b je 2459 <mmap_gfind+0xf9>
8.40 : 244e: eb 17 jmp 2467 <mmap_gfind+0x107>
?:0
1.63 : 2450: 0f b6 0c 02 movzbl (%rdx,%rax,1%ecx
3.74 : 2454: 38 0c 03 cmp %cl%rbx,%rax,1)
3.60 : 2457: 75 09 jne 2462 <mmap_gfind+0x102>
2.33 : 2459: 48 83 c0 01 add $0x1,%rax
?:0
8.40 : 245d: 4c 39 d8 cmp %r11,%rax
?:0
0.75 : 2460: 7c ee jl 2450 <mmap_gfind+0xf0>
: /* nothing */;
: if (i == len) {
2.33 : 2462: 4c 39 d8 cmp %r11,%rax
0.26 : 2465: 74 40 je 24a7 <mmap_gfind+0x147>
: self->pos += num_bytes;
: return result;
: }
: fastsearch(const STRINGLIB_CHAR* s, Py_ssize_t n,
: const STRINGLIB_CHAR* p, Py_ssize_t m,
: Py_ssize_t maxcount, int mode)
: {
: unsigned long mask;
: Py_ssize_t skip, count = 0;
0.00 : 8eae9: 45 31 ed xor %r13d,%r13d
0.00 : 8eaec: eb 26 jmp 8eb14 <fastsearch+0x114>
0.00 : 8eaee: 66 90 xchg %ax,%ax
: i = i + m;
: else
: i = i + skip;
: } else {
: /* skip: check if next character is part of pattern */
: if (!STRINGLIB_BLOOM(mask, s[i+m]
?:0
7.28 : 8eaf0: 0f be 0c 37 movsbl (%rdi,%rsi,1%ecx
?:0
1.89 : 8eaf4: 4c 89 f3 mov %r14,%rbx
0.00 : 8eaf7: 83 e1 3f and $0x3f,%ecx
1.46 : 8eafa: 48 d3 eb shr %cl,%rbx
?:0
7.71 : 8eafd: 48 89 d9 mov %rbx,%rcx
?:0
4.37 : 8eb00: 83 e1 01 and $0x1,%ecx
?:0
6.55 : 8eb03: 48 0f 45 f5 cmovne %rbp,%rsi
: skip = mlast - i - 1;
: }
: /* process pattern[-1] outside the loop */
: STRINGLIB_BLOOM_ADD(mask, p[mlast]);
:
: for (i = 0; i <= w; i++) {
14.85 : 8eb07: 48 8d 6e 01 lea 0x1(%rsi%rbp
6.26 : 8eb0b: 49 39 eb cmp %rbp,%r11
8.01 : 8eb0e: 0f 8c cc 00 00 00 jl 8ebe0 <fastsearch+0x1e0>
: /* note: using mlast in the skip path slows things down on x86 */
: if (s[i+m-1] == p[m-1]) {
6.11 : 8eb14: 4a 8d 74 15 00 lea 0x0(%rbp,%r10,1%rsi
0.00 : 8eb19: 0f b6 4c 24 c0 movzbl -0x40(%rsp%ecx
0.00 : 8eb1e: 38 4c 37 ff cmp %cl,-0x1(%rdi,%rsi,1)
32.31 : 8eb22: 75 cc jne 8eaf0 <fastsearch+0xf0>
: mask |= (1UL << ch) & (STRINGLIB_BLOOM_WIDTH -1
: #define STRINGLIB_BLOOM(mask, ch) \
: mask & (1UL << ch) & (STRINGLIB_BLOOM_WIDTH -1
mmap можно делать с MAP_POPULATE тогда пэйдж-фолтов не будет,
но этот кусок кода написан хреново и мапит страницы по одной на каждую беря пару локов.
на микротестах получается что смапить одну страницу медленне чем её скопировать,
это всё без учёта чтения, считается что всё уже в пэйдж-кэше.
но этот кусок кода написан хреново и мапит страницы по одной на каждую беря пару локов.
на микротестах получается что смапить одну страницу медленне чем её скопировать,
это всё без учёта чтения, считается что всё уже в пэйдж-кэше.
Хм.
Ну видишь ли, я обнаружил проблему когда использовал регекспы вообще. И оно дико тормозило! Потом начал тестить, только потом заглянул в сурцы наконец...
Короче, сейчас я ещё немножко поэкспериментировал (добавив поиск регекспом и тупо зачитывание ммапленных данных в строку таки да, ммап почти не тормозит (ну, там, может, на пять процентов медленнее, и то опять это могут быть какие-то тонкие различия в реализации чего-нибудь). То есть в моём эксперименте тормозил его файнд.
Интересно, в чём была проблема в той программе, при виде которой я собственно начал задаваться вопросом. Она там искала всякое в выкачаном svn репозитории. С накладными расходами на открытие множества мелких файликов я поборолся путём использования ммапа только для >1 мегабайта (или 100к, уже не помню — в репозитории куча тяжёлых документов лежала, но вот насколько тяжёлых... и всё равно ощутимо тормозило. Может, ммапленные файлы не выгружаются сразу из памяти, и от этого какая-нибудь фигня происходит?
Ладно, неважно, будем считать, что честное имя ммапа восстановлено!
Ну видишь ли, я обнаружил проблему когда использовал регекспы вообще. И оно дико тормозило! Потом начал тестить, только потом заглянул в сурцы наконец...
Короче, сейчас я ещё немножко поэкспериментировал (добавив поиск регекспом и тупо зачитывание ммапленных данных в строку таки да, ммап почти не тормозит (ну, там, может, на пять процентов медленнее, и то опять это могут быть какие-то тонкие различия в реализации чего-нибудь). То есть в моём эксперименте тормозил его файнд.
Интересно, в чём была проблема в той программе, при виде которой я собственно начал задаваться вопросом. Она там искала всякое в выкачаном svn репозитории. С накладными расходами на открытие множества мелких файликов я поборолся путём использования ммапа только для >1 мегабайта (или 100к, уже не помню — в репозитории куча тяжёлых документов лежала, но вот насколько тяжёлых... и всё равно ощутимо тормозило. Может, ммапленные файлы не выгружаются сразу из памяти, и от этого какая-нибудь фигня происходит?
Ладно, неважно, будем считать, что честное имя ммапа восстановлено!
Оставить комментарий
bleyman
Надобно тут было нечто грепообразное написать на Питоне, ну я и подумал, а не использовать ли мне mmap!И с диким удивлением обнаружил, что на множестве маленьких файликов оно вообще всё тормозит нечеловечески, но и на стомегабайтном файле в три раза медленнее чем "open(fname, 'rb').read.find('abcde')". Ну, если такой строки там нет, то есть файл пришлось целиком проглядеть.
Причём числа подозрительно похожи и на унихе, и на винде!
Причём внутри этот самый питоновский mmap написан на чистой сишке и выглядит довольно эффективным — типа, find у него тупо ищёт строку вдоль некоего пойнтера, всем остальным занимается ОС! Ну, там, у str.find олгоритм поумней, типа Боер-Мур, правда, но у меня и регекспами тоже дико тормозило (они умеют ммапом питаться!)
Так и должно быть или всё-таки некая кривизна в Питоне виновата? То есть я не могу сказать что это меня дико удивляет после определённых размышлений, но до того я почему-то полагал, что польза mmap не только в удобстве, но и в том что он всё-таки быстрее даже если мне нужно тупо в файле какую-нибудь фигню поискать.
Кот: http://python.pastebin.com/pbmwtgGX