2012-09-25 2 views
4

Я пытаюсь использовать PyPy в последнее время, и это на 25 раз быстрее для моего текущего проекта, и он работает очень хорошо. К сожалению, однако, записи файлов невероятно slow. Запись файлов примерно в 60 раз медленнее.Pypy slow при записи файла

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

В простом тесте, как это:

with file(path, 'w') as f: 
    f.writelines(['testing to write a file\n' for i in range(5000000)]) 

Я вижу 60x замедление PyPy, по сравнению с обычным Python. Это использует 64-разрядные версии 2.7.3 и PyPy 1.9, 32-бит и Python 2.7.2. Оба они находятся на одной ОС и машине, конечно (Windows 7).

Любая помощь будет оценена по достоинству. PyPy намного быстрее для того, что я делаю, но скорость записи файлов ограничена половиной мегабайта в секунду, это явно менее полезно.

+0

На linux эти скорости очень сопоставимы. PyPy для меня незначительно медленнее (20%) по причинам GC (есть фишка, чтобы исправить их). Какая-то странность в окнах? Не могли бы вы поместить такие вещи на bugs.pypy.org, а не здесь? stackoverflow не очень хорошая замена для отслеживания ошибок. – fijal

+0

https://bugs.pypy.org/[email protected]=item&@pagesize=50&@startwith=0 –

ответ

0

xrange это ответ на этот пример, так как он doesn't generate список, но генератор. 64-разрядный питон, вероятно, быстрее, чем 32-разрядный pypy при создании списка с 50 миллионами элементов.

Если у вас есть другой код, разместите фактический код, а не только тест.

+0

Это вообще не объясняет наблюдение, так как это * по крайней мере * как истина для CPython, как для PyPy. Возможно, PyPy даже выигрывает (по сравнению с CPython) от использования 'range', так как некоторые версии включают оптимизацию, где список фактически не материализуется, если это не требуется. (Также см. Ответ Мэтью Тревора и комментарии.) – delnan

2

Это медленнее, но не 60x медленнее на этой системе

TLDR; Используйте write('\n'.join(...)) вместо writelines(...)

$ pypy -m timeit -s "path='tst'" "with file(path, 'w') as f:f.writelines(['testing to write a file\n' for i in range(5000000)])" 
10 loops, best of 3: 1.15 sec per loop 

$ python -m timeit -s "path='tst'" "with file(path, 'w') as f:f.writelines(['testing to write a file\n' for i in range(5000000)])" 
10 loops, best of 3: 434 msec per loop 

xrange не делает никакой разницы

$ pypy -m timeit -s "path='tst'" "with file(path, 'w') as f:f.writelines(['testing to write a file\n' for i in xrange(5000000)])" 
10 loops, best of 3: 1.15 sec per loop 

Используя выражение генератор медленнее PyPy, но быстрее питона

$ pypy -m timeit -s "path='tst'" "with file(path, 'w') as f:f.writelines('testing to write a file\n' for i in xrange(5000000))" 
10 loops, best of 3: 1.62 sec per loop 
$ python -m timeit -s "path='tst'" "with file(path, 'w') as f:f.writelines('testing to write a file\n' for i in xrange(5000000))" 
10 loops, best of 3: 407 msec per loop 

движущемся создание данных за пределами теста усиливает разность (~ 4.2x)

$ pypy -m timeit -s "path='tst'; data=['testing to write a file\n' for i in range(5000000)]" "with file(path, 'w') as f:f.writelines(data)" 
10 loops, best of 3: 786 msec per loop 
$ python -m timeit -s "path='tst'; data=['testing to write a file\n' for i in range(5000000)]" "with file(path, 'w') as f:f.writelines(data)" 
10 loops, best of 3: 189 msec per loop 

Использование write() вместо writelines() гораздо быстрее, и для

$ pypy -m timeit -s "path='tst'; data='\n'.join('testing to write a file\n' for i in range(5000000))" "with file(path, 'w') as f:f.write(data)" 
10 loops, best of 3: 51.9 msec per loop 
$ python -m timeit -s "path='tst'; data='\n'.join('testing to write a file\n' for i in range(5000000))" "with file(path, 'w') as f:f.write(data)" 
10 loops, best of 3: 52.4 msec per loop 

$ uname -srvmpio 
Linux 3.2.0-26-generiC#41-Ubuntu SMP Thu Jun 14 17:49:24 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux 
$ python --version 
Python 2.7.3 
$ pypy --version 
Python 2.7.2 (1.8+dfsg-2, Feb 19 2012, 19:18:08) 
[PyPy 1.8.0 with GCC 4.6.2] 
+0

При записи данных в виде непрерывного фрагмента я получаю намного лучшую производительность в PyPy: разница составляет около 4x, а не 60x. Тем не менее, значительно медленнее. Полагаю, я просто останусь подальше от писем() и напишу все за один раз. –

0

Давайте сначала получить ваш метод аналогий прямой.

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

Следовательно, если вы планируете хранить все фиктивные данные в памяти, создайте его перед измерением времени.

Однако в вашем случае генерация данных «на лету», скорее всего, будет быстрее, чем ваш I/O будет когда-либо. Поэтому, используя генератор Python, в этом случае выражение генератора в сочетании с вызовом write вы избавитесь от этой системной ошибки.

Я не знаю, как writelines выполняет по сравнению с write. Однако, в соответствии с вашим writelines например:

with file(path, 'w') as f: 
    f.writelines('xxxx\n' for _ in xrange(10**6)) 

Запись больших объемов данных с write может быть быстрее:

with file(path, 'w') as f: 
    for chunk in ('x'*99999 for _ in xrange(10**3)): 
     f.write(chunk) 

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

+0

Мне хорошо известно, что в эталонное время было включено время, необходимое для создания фиктивных данных. Однако, поскольку writelines() написал так невероятно медленно в PyPy, это стало очень незначительным различием. Когда вы создаете список строк со списком и записываете их, PyPy тратит более 300 раз на запись файла, чем на генерацию данных. Вместо этого я попытаюсь записать куски и посмотреть, быстрее ли это. Благодаря! –

-1

Вы создаете два здесь, один с range и один со списком.

Список 1: один из вариантов заключается в замене списка, возвращающего range с генератором xrange. Другой - попробовать собственную оптимизацию PyPy под названием range-lists.

Вы можете включить эту функцию с помощью опции –objspace-std-withrangelist.

Список 2: вы создаете свой список результатов перед его записью. Это должно также быть генератором, поэтому превратить список понимание в выражение генератора:

f.writelines('testing to write a file\n' for i in range(5000000)) 

Пока выражение генератора является единственным аргументом, переданный функции, это даже не нужно удвоение на скобках ,

+0

rangelists включен по умолчанию, диапазон не выделяет всю память на pypy. что здесь все равно неважно. – fijal

+0

PyPy docs прямое состояние это не включено по умолчанию: http://doc.pypy.org/en/latest/config/objspace.std.withrangelist.html –

+0

Извините, вы правы, перечисление информации о переводе показывает это включен. –