Python的循环速度比单行理解快:为什么?

时间:2020-01-18 22:01:23

标签: python performance

这里有两个功能完全相同(将数字的阶乘相加)。 数组F包含0!... 9!

N = 1_000_000

F = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]

def facdigits1(n):
    total = 0
    for i in map(int, str(n)):
        total += F[i]
    return total

def facdigits2(n):
    return sum((F[i] for i in map(int, str(n))))

total = 0
for i in range(2, N):
    n = facdigits1(i)
    total += n
print(total)

完全出乎意料的(对我而言),facdigits2比facdigits1慢了约20%。我预料相反。知道这里发生了什么吗?

2 个答案:

答案 0 :(得分:2)

设置开销似乎更高。这是2**25 / n次用三种方法求和[1] * n的时间(以秒为单位)(因此,每n个行,因此每一行,求和的总数相同,即2**25)。请注意,这三者的启动速度都很慢,而循环的启动速度最快。不知道为什么循环在低于2.0之后上升到2.8(编辑:也许Python's small integers cache使得总和低于257的速度更快,但是其他两个解决方案似乎并没有因此受到影响)。

       n   loop   generator  pure
-----------------------------------
       1  11.3239  24.6003  12.5587
       2   6.8343  13.6080   5.6892
       4   4.4053   7.9283   3.0991
       8   3.2007   5.5467   1.8115
      16   2.5485   3.7965   1.1166
      32   2.2361   3.0682   0.7247
      64   2.0101   3.0527   0.5513
     128   2.0220   2.4149   0.4475
     256   1.8081   2.3848   0.3966
     512   2.0703   2.3418   0.4229
    1024   2.2276   2.2966   0.3706
    2048   2.2751   2.2856   0.3614
    4096   2.2761   2.3269   0.3533
    8192   2.3085   2.3623   0.3671
   16384   2.3395   2.2970   0.3552
   32768   2.4804   2.3583   0.4097
   65536   2.5998   2.3151   0.3572
  131072   2.7952   2.2654   0.3954
  262144   2.7445   2.4830   0.3599
  524288   2.7740   2.3462   0.3565
 1048576   2.7449   2.2845   0.3592
 2097152   2.8187   2.2246   0.3708
 4194304   2.8307   2.2691   0.3572
 8388608   2.8036   2.2731   0.3690
16777216   2.7704   2.3388   0.3649
33554432   2.8328   2.3018   0.3545

您的n≤6,因此我的测试同意发电机的速度较慢。就我而言,这是更极端的情况,大概是因为我没有做任何其他事情,例如将数字转换为字符串,查找列表索引以及对大数字求和。

代码:

from timeit import timeit

def sum_loop(numbers):
    total = 0
    for number in numbers:
        total += number
    return total

def sum_generator(numbers):
    return sum(number for number in numbers)

def sum_pure(numbers):
    return sum(numbers)

emax = 25
for e in range(emax + 1):
    n = 2**e
    number = 2**(emax - e)
    numbers = [1] * n
    print('%8d' % n, end='')
    for f in sum_loop, sum_generator, sum_pure:
        t = timeit(lambda: f(numbers), number=number)
        print('  %7.4f' % t, end='')
    print()

答案 1 :(得分:1)

问题本身隐藏在sum函数相对于生成器的性能降低中。总结一些答案,我得出的结论是,生成器不会一次将所有元素存储在内存中。这是我定义求和的方法:

def s1(size):
    total = 0
    for i in range(size):
        total += i
    return total

def s2(size):
    return sum(i for i in range(size))

def s3(size):
    return sum(range(size))

def s4(size):
    def gen(size):
        for i in range(size):
            yield i
    return sum(gen(size))

def s5(size):
    def gen(size):
        i=0
        while i<size:
            yield i
            i+=1
    return sum(gen(size))

from time import time
import numpy as np

time_avg=[]
size = 10000000
for i in range(10):
    times=[]
    for s in [s1,s2,s3,s4,s5]:
        t = time()
        s(size)
        times.append(round(time()-t, 3))
    time_avg.append(times)

print(np.sum(np.array(time_avg), axis=0)/len(time_avg))

输出

s1s2s3s4s5的时间是:

[0.9624 1.3263 0.6373 1.2426 1.8455]