Почему однострочный pyp (python) такой медленный?

Я пытаюсь преобразовать однострочники perl в pyp. Моя первая попытка была любезно дана мне как ответ на другой вопрос в качестве

pyp "mm | p if n==0 else (p[:-2] + [(int(x)%12) for x in p[-2:]]) | mm"

Однако это оказывается удивительно медленным. Если я создам тестовый файл, используя

for j in xrange(50000):
    print ",".join(str(i) for i in [random.choice(xrange(1000)) for i in xrange(8)])

а затем запустить

time (cat testmedium.txt |~/.local/bin/pyp "mm | p if n==0 else (p[:-2] + [(int(x)%12) for x in p[-2:]]) | mm" > /dev/null)

я получил

real    1m27.889s
user    1m26.941s
sys 0m0.688s

Однако эквивалент в Perl почти мгновенный.

time (cat testmedium.txt |perl -l -a -F',' -p -e'if ($. > 1) { $F[6] %=12; $F[7] %= 12;$_ = join(q{,}, @F[6,7]) }' > /dev/null)

real    0m0.196s
user    0m0.192s
sys 0m0.012s

Для больших тестовых файлов разница еще более существенна.


person marshall    schedule 05.05.2013    source источник
comment
Интерпретаторы PErl и python не работают одинаково. То, что быстро с одним, может быть худшим подходом в другом. Если бы вы сказали нам, чего вы пытаетесь достичь, мы, вероятно, могли бы предоставить быструю версию pythonic.   -  person Bakuriu    schedule 05.05.2013
comment
@Bakuriu Я думаю, что код pyp, должно быть, делает что-то странное, поскольку он также использует огромный объем памяти (728 МБ?), И я ожидал, что он будет обрабатывать строки практически на лету. Цель состоит в том, чтобы вводить числовые значения, разделенные запятыми, и выводить их в том же формате, за исключением того, что два числа в каждой строке заданы по модулю 12. В связанном вопросе есть еще несколько мелких деталей.   -  person marshall    schedule 05.05.2013
comment
Вы пытались профилировать что-то вроде pyp "mm | mm", чтобы проверить, тратит ли время сам pyp на использование каналов?   -  person Bakuriu    schedule 05.05.2013
comment
Мне также было бы любопытно, повлияло ли изменение (int(x)%12) на str(int(x)%12) на pyp.   -  person Amber    schedule 05.05.2013
comment
@Бакуриу Ты прав. time (~/.local/bin/pyp mm | mm ‹ testmedium.txt) тоже очень медленно!   -  person marshall    schedule 05.05.2013
comment
Хорошо, значит, сам pyp работает медленно. Это не совсем неожиданно, даже из чтения документации вы понимаете, что это делает много волшебства. :-)   -  person Lennart Regebro    schedule 05.05.2013
comment
Использование явных p.split(',') и ','.join(p) вместо mm приводит к изменению таймингов?   -  person Bakuriu    schedule 05.05.2013
comment
@Bakuriu ~/.local/bin/pyp p.split(',') | ','.join(p) ‹ testmedium.txt › /dev/null страдает от той же проблемы.   -  person marshall    schedule 06.05.2013
comment
Однако python -c "import sys;print '\n'.join(','.join(x.split(',')) for x in sys.stdin)" < test.txt > result.txt нет. Так что да, определенно что-то в pyp.   -  person Amber    schedule 06.05.2013


Ответы (2)


Этот код...

import sys

for index,line in enumerate(sys.stdin):
    if index == 0:
        print line
    else:
        values = line.split(',')
        values[-2:] = [str(int(x)%12) for x in values[-2:]]
        print ','.join(values)

у меня выполняется менее чем за секунду (используя тестовый файл, сгенерированный тем же методом, что и вы):

$ time (cat test.txt | python foo.py > /dev/null)

real    0m0.363s
user    0m0.339s
sys     0m0.032s

Так что, если вы столкнулись с проблемами, это, вероятно, неэффективность того, что пытается сделать pyp.

person Amber    schedule 05.05.2013
comment
Также создание нового values list, как и в версии pyp, не сильно меняет тайминги, так что это не проблема. - person Bakuriu; 05.05.2013
comment
К сожалению, я думаю, что это просто недостаток дизайна в pyp. В настоящее время он кажется неподходящим для обработки больших файлов. - person marshall; 15.05.2013

Это косвенный ответ на ваш вопрос @marshall.

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

Мне было интересно, может ли pypy предоставить более быструю версию pyp, поэтому я создал псевдоним для pyp:

alias 'pl=pypy /usr/bin/pyp'

Затем я запустил эту команду как с pyp, так и с pl.

lr | pl "'doc',p, p.replace('e','EEE')+'.xpg' | pp.reverse() | ''.join(p)" | pl "d|u"

где lr — это псевдоним для ls -R + ls -A просто для создания длинного рекурсивного списка для определения времени операции.

Результаты составили 8,04 секунды для pyp с использованием Python 2.7.6 и 4,46 секунды для псевдонима pl. Для гораздо большего набора каталогов это было 470 и 250 секунд. Во время этой операции Python работает на 100 % одного ядра, как и PyPy.

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

person John 9631    schedule 23.05.2014