为什么在我的情况下 For 循环比 Map、Reduce 和 List 理解更快

alp*_*iii 4 python performance for-loop mapreduce list-comprehension

我写了一个简单的脚本来测试速度,这就是我发现的。实际上 for 循环在我的情况下是最快的。这真的让我感到惊讶,请查看下面的内容(正在计算平方和)。那是因为它在内存中保存了列表还是有意为之?任何人都可以解释这一点。

from functools import reduce
import datetime


def time_it(func, numbers, *args):
    start_t = datetime.datetime.now()
    for i in range(numbers):
        func(args[0])
    print (datetime.datetime.now()-start_t)

def square_sum1(numbers):
    return reduce(lambda sum, next: sum+next**2, numbers, 0)


def square_sum2(numbers):
    a = 0
    for i in numbers:
        i = i**2
        a += i
    return a

def square_sum3(numbers):
    sqrt = lambda x: x**2
    return sum(map(sqrt, numbers))

def square_sum4(numbers):
    return(sum([i**2 for i in numbers]))


time_it(square_sum1, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
time_it(square_sum2, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
time_it(square_sum3, 100000, [1, 2, 5, 3, 1, 2, 5, 3])
time_it(square_sum4, 100000, [1, 2, 5, 3, 1, 2, 5, 3])

0:00:00.302000 #Reduce
0:00:00.144000 #For loop
0:00:00.318000 #Map
0:00:00.290000 #List comprehension`
Run Code Online (Sandbox Code Playgroud)

更新- 当我尝试更长的循环时,就会有结果。

time_it(square_sum1, 100, range(1000))
time_it(square_sum2, 100, range(1000))
time_it(square_sum3, 100, range(1000))
time_it(square_sum4, 100, range(1000))

0:00:00.068992
0:00:00.062955
0:00:00.069022
0:00:00.057446
Run Code Online (Sandbox Code Playgroud)

PM *_*ing 6

Python函数调用的开销使其相对较慢,因此使用简单表达式的代码总是比将该表达式包装在函数中的代码快;它是普通def函数还是lambda. 出于这个原因,如果您可以使用循环中的普通表达式或推导式或生成器表达式来完成等效的工作,则最好避免map或者reduce如果您打算向它们传递 Python 函数for


有几个小的优化可以加速您的某些功能。不要做不必要的任务。例如,

def square_sum2a(numbers):
    a = 0
    for i in numbers:
        a += i ** 2
    return a
Run Code Online (Sandbox Code Playgroud)

此外,i * ii ** 2乘法快得多,因为乘法比求幂快。

正如我在评论中提到的,传递sum生成器比传递列表理解更有效,尤其是在循环很大的情况下;它可能不会对长度为 8 的小列表产生影响,但对于大列表会非常明显。

sum(i*i for i in numbers)
Run Code Online (Sandbox Code Playgroud)

顺便说一句,您不应该使用sumnext作为变量名,因为这会掩盖具有相同名称的内置函数。在这里不会有任何伤害,但它仍然不是一个好主意,并且它使您的代码在具有比 SO 语法高亮器更全面的语法高亮显示的编辑器中看起来很奇怪。


这是使用该timeit模块的代码的新版本。它重复 3 次,每次 10,000 次循环并对结果进行排序。正如timeit 文档中所解释的,在一系列重复中要查看的重要数字是最小的。

在典型情况下,最低值给出了机器运行给定代码片段的速度的下限;结果向量中的较高值通常不是由 Python 速度的变化引起的,而是由干扰计时精度的其他进程引起的。所以min()结果的 可能是您应该感兴趣的唯一数字。

from timeit import Timer
from functools import reduce

def square_sum1(numbers):
    return reduce(lambda total, u: total + u**2, numbers, 0)

def square_sum1a(numbers):
    return reduce(lambda total, u: total + u*u, numbers, 0)

def square_sum2(numbers):
    a = 0
    for i in numbers:
        i = i**2
        a += i
    return a

def square_sum2a(numbers):
    a = 0
    for i in numbers:
        a += i * i
    return a

def square_sum3(numbers):
    sqr = lambda x: x**2
    return sum(map(sqr, numbers))

def square_sum3a(numbers):
    sqr = lambda x: x*x
    return sum(map(sqr, numbers))

def square_sum4(numbers):
    return(sum([i**2 for i in numbers]))

def square_sum4a(numbers):
    return(sum(i*i for i in numbers))

funcs = (
    square_sum1,
    square_sum1a,
    square_sum2,
    square_sum2a,
    square_sum3,
    square_sum3a,
    square_sum4,
    square_sum4a,
)

data = [1, 2, 5, 3, 1, 2, 5, 3]

def time_test(loops, reps):
    ''' Print timing stats for all the functions '''
    timings = []
    for func in funcs:
        fname = func.__name__
        setup = 'from __main__ import data, ' + fname
        cmd = fname + '(data)'
        t = Timer(cmd, setup)
        result = t.repeat(reps, loops)
        result.sort()
        timings.append((result, fname))

    timings.sort()
    for result, fname in timings:
        print('{0:14} {1}'.format(fname, result))

loops, reps = 10000, 3
time_test(loops, reps)
Run Code Online (Sandbox Code Playgroud)

输出

square_sum2a   [0.03815755599862314, 0.03817843700016965, 0.038571521999983815]
square_sum4a   [0.06384095800240175, 0.06462285799716483, 0.06579178199899616]
square_sum3a   [0.07395686000018031, 0.07405958899835241, 0.07463337299850537]
square_sum1a   [0.07867341000019223, 0.0788448769999377, 0.07908406700153137]
square_sum2    [0.08781023399933474, 0.08803317899946705, 0.08846573399932822]
square_sum4    [0.10260082300010254, 0.10360279499946046, 0.10415067900248687]
square_sum3    [0.12363515399920288, 0.12434166299863136, 0.1273790529994585]
square_sum1    [0.1276186039976892, 0.13786184099808452, 0.16315817699796753]
Run Code Online (Sandbox Code Playgroud)

结果是在 Linux 上运行 Python 3.6.0 的旧单核 32 位 2GHz 机器上获得的。