Почему python для петель настолько нелинейный для больших входов?

Я сравнивал некоторые коды python, я заметил что-то странное. Я использовал следующую функцию, чтобы измерить, как быстро потребовалось перебирать пустой цикл:

def f(n):
    t1 = time.time()
    for i in range(n):
        pass
    print(time.time() - t1)

f(10**6) печатает около 0.035, f(10**7) около 0.35, f(10**8) около 3.5 и f(10**9) около 35. Но f(10**10)? Хорошо над 2000. Это конечно неожиданно. Зачем потребовалось бы в 60 раз больше времени для повторения в 10 раз больше элементов? Что с питоном для циклов, который вызывает это? Является ли это специфичным для python, или это происходит на многих языках?

Ответы

Ответ 1

Когда вы выходите выше 10^9, вы получаете из 32-битного целочисленного диапазона. Python3 затем прозрачно перемещает вас на произвольные целые числа точности, которые гораздо медленнее выделять и использовать.

В целом работа с такими большими номерами является одной из областей, где Python3 намного медленнее, чем Python2 (который, по крайней мере, имел бы быстрые 64-битные целые числа во многих системах). С другой стороны, это упрощает использование python с меньшими ошибками overflow.

Ответ 2

Некоторые точные тайминги, использующие timeit, показывают, что время фактически приблизительно увеличивается в соответствии с размером ввода, поэтому ваши тайминги выглядят довольно далеко:

In [2]: for n in [10**6,10**7,10**8,10**9,10**10]:
               % timeit f(n)
   ...:     
10 loops, best of 3: 22.8 ms per loop
1 loops, best of 3: 226 ms per loop # roughly ten times previous
1 loops, best of 3: 2.26 s per loop # roughly ten times previous
1 loops, best of 3: 23.3 s per loop # roughly ten times previous
1 loops, best of 3: 4min 18s per loop # roughly ten times previous

Используя xrange и python2, мы видим, что отношение примерно одинаковое, очевидно, что python2 намного быстрее в целом из-за того, что python3 int был заменен на long:

In [5]: for n in [10**6,10**7,10**8,10**9,10**10]:
               % timeit f(n)
   ...:     
100 loops, best of 3: 11.3 ms per loop
10 loops, best of 3: 113 ms per loop
1 loops, best of 3: 1.13 s per loop
1 loops, best of 3: 11.4 s per loop
1 loops, best of 3: 1min 56s per loop

Фактическое различие в времени выполнения, по-видимому, больше связано с размером окна длиной, а не напрямую связано с python 3. Разница незначительна при использовании unix, которая обрабатывает longs намного по-разному для окон, так что это проблема с конкретной платформой, если не больше, чем на python.