Немного разное время выполнения между python2 и python3 - PullRequest
3 голосов
/ 30 декабря 2010

Наконец, я написал простой генератор перестановок в python (реализация алгоритма «простых изменений», описанного Кнутом в «The Art ... 4»). Мне было любопытно о различиях во времени выполнения этого между python2 и python3. Вот моя функция:

def perms(s):
    s = tuple(s)
    N = len(s)
    if N <= 1:
        yield s[:]
        raise StopIteration()
    for x in perms(s[1:]):
        for i in range(0,N):
        yield x[:i] + (s[0],) + x[i:]

В версии python3 я только что изменил print x на print (x), так как print - это функция в py3. Я протестировал оба, используя модуль timeit.

Мои тесты:

$ echo "python2.6:" && ./testing.py && echo "python3:" && ./testing3.py

python2.6:

args time[ms]
1 0.003811
2 0.008268
3 0.015907
4 0.042646
5 0.166755
6 0.908796
7 6.117996
8 48.346996
9 433.928967
10 4379.904032

python3:

args time[ms]
1   0.00246778964996
2   0.00656183719635
3   0.01419159912
4   0.0406293644678
5   0.165960511097
6   0.923101452814
7   6.24257639835
8   53.0099868774
9   454.540967941
10  4585.83498001

Как видите, для числа аргументов меньше 6 Python 3 быстрее, но затем роли меняются местами, а python2.6 работает лучше. Поскольку я новичок в программировании на Python, мне интересно, почему это так? Или, может быть, мой скрипт более оптимизирован для python2?

Заранее спасибо за добрый ответ:)

Ответы [ 3 ]

4 голосов
/ 30 декабря 2010

Цитирование :

Общий результат обобщений 3.0 заключается в том, что Python 3.0 выполняет тест Pystone примерно на 10% медленнее, чем Python 2.5.Скорее всего, самой большой причиной является удаление специального регистра для маленьких целых чисел.Есть возможности для улучшения, но это произойдет после выхода 3.0!

>>> 4585.83498001/4379.904032
1.0470172283468882

Так что вы видите примерно 5% замедление.В цитируемом тексте говорится, что ожидается замедление на 10%.Так что я бы принял это как разумное замедление.

Тем не менее, ситуация улучшается, как можно видеть здесь и здесь .Так что попробуйте 3.1 или 3.2, если вас беспокоит замедление на 5%.

2 голосов
/ 30 декабря 2010

Это на самом деле очень интересный вопрос.

Я использовал следующий скрипт, который работает на Python 2.6, 2.7, 3.0, 3.1 и 3.2.

from __future__ import print_function
from timeit import Timer
from math import factorial

try:
    range = xrange
except:
    pass

def perms(s):
    s = tuple(s)
    N = len(s)
    if N <= 1:
        yield s[:]
        raise StopIteration()
    for x in perms(s[1:]):
        for i in range(0,N):
            yield x[:i] + (s[0],) + x[i:]

def testcase(s):
    for x in perms(s):
        pass

def test():
    for i in range(1,11):
        s = "".join(["%d" % x for x in range(i)])
        s = "testcase(\"%s\")" % s
        t = Timer(s,"from __main__ import testcase")
        factor = 100000
        factor = int(factor/factorial(i))
        factor = (factor>0) and factor or 1
        yield (i,(1000*min(t.repeat(5,factor))/factor))

if __name__=="__main__":
    print("args\ttime[ms]")
    for x in test():
        print("%i\t%f" % x)

Платформа Ubuntu 10.10, 64-разрядная, и все версии Python были скомпилированы из исходного кода. Я получаю следующие результаты:

case@quad:~$ py27 perms.py
args    time[ms]
1   0.002221
2   0.005072
3   0.010352
4   0.027648
5   0.111339
6   0.618658
7   4.207046
8   33.213019
9   294.044971
10  2976.780891

case@quad:~$ py32 perms.py
args    time[ms]
1   0.001725
2   0.004997
3   0.011208
4   0.032815
5   0.139474
6   0.761153
7   5.068729
8   39.760470
9   356.358051
10  3566.874027

После дополнительных экспериментов я отследил разницу в производительности для фрагмента: x[:i] + (s[0],) + x[i:] Если я просто вычислю один кортеж в начале цикла и верну его для каждого оператора yield, обе версии Python будут работать с одинаковой скоростью , (И перестановки неверны, но это не главное.)

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

case@quad:~$ py27 -m timeit -s "s=(1,2,3,4,5);x=(1,2,3,4,5,6,7,8)" "x[:3] + (s[0],) + x[3:]"
1000000 loops, best of 3: 0.549 usec per loop
case@quad:~$ py32 -m timeit -s "s=(1,2,3,4,5);x=(1,2,3,4,5,6,7,8)" "x[:3] + (s[0],) + x[3:]"
1000000 loops, best of 3: 0.687 usec per loop

Затем я использовал dis.dis () для просмотра байт-кода, сгенерированного обеими версиями.

case@quad:~/src/Python-3.0.1$ py32
Python 3.2b2 (r32b2:87398, Dec 21 2010, 21:39:59) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis
>>> s=(1,2,3,4,5)
>>> x=(1,2,3,4,5,6,7,8)
>>> def f(s,x):
...   return x[:3] + (s[0],) + x[3:]
... 
>>> dis.dis(f)
  2           0 LOAD_FAST                1 (x) 
              3 LOAD_CONST               0 (None) 
              6 LOAD_CONST               1 (3) 
              9 BUILD_SLICE              2 
             12 BINARY_SUBSCR        
             13 LOAD_FAST                0 (s) 
             16 LOAD_CONST               2 (0) 
             19 BINARY_SUBSCR        
             20 BUILD_TUPLE              1 
             23 BINARY_ADD           
             24 LOAD_FAST                1 (x) 
             27 LOAD_CONST               1 (3) 
             30 LOAD_CONST               0 (None) 
             33 BUILD_SLICE              2 
             36 BINARY_SUBSCR        
             37 BINARY_ADD           
             38 RETURN_VALUE         
>>> exit()
case@quad:~/src/Python-3.0.1$ py26
Python 2.6.6 (r266:84292, Oct 24 2010, 15:27:46) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis
>>> s=(1,2,3,4,5)
>>> x=(1,2,3,4,5,6,7,8)
>>> def f(s,x):
...   return x[:3] + (s[0],) + x[3:]
... 
>>> dis.dis(f)
  2           0 LOAD_FAST                1 (x)
              3 LOAD_CONST               1 (3)
              6 SLICE+2             
              7 LOAD_FAST                0 (s)
             10 LOAD_CONST               2 (0)
             13 BINARY_SUBSCR       
             14 BUILD_TUPLE              1
             17 BINARY_ADD          
             18 LOAD_FAST                1 (x)
             21 LOAD_CONST               1 (3)
             24 SLICE+1             
             25 BINARY_ADD          
             26 RETURN_VALUE        
>>> 

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

0 голосов
/ 30 декабря 2010

Я бы назвал эти цифры статистически незначимыми.

Слишком много факторов, чтобы эти вариации действительно имели какое-либо значение.

...