当前位置: 首页 > 知识库问答 >
问题:

为什么`np.sum(range(N))`非常慢?

蓬弘
2023-03-14

下面是我用来测试的脚本,在我所知道的情况下(主要来自视频),以及我在我的机器上得到的结果(python 3.8.10,numpy 1.19.5):

更新脚本:

import numpy as np
from timeit import timeit

N = 10_000_000
repetition = 10

def sum0(N = N):
    s = 0
    i = 0
    while i < N: # condition is checked in python
        s += i
        i += 1 # both additions are done in python
    return s

def sum1(N = N):
    s = 0
    for i in range(N): # increment in C
        s += i # addition in python
    return s

def sum2(N = N):
    return sum(range(N)) # everything in C

def sum3(N = N):
    return sum(list(range(N)))

def sum4(N = N):
    return np.sum(range(N)) # very slow np.array conversion

def sum5(N = N):
    # much faster np.array conversion
    return np.sum(np.fromiter(range(N),dtype = np.int)) 

def sum6(N = N):
    # possibly slow conversion to Py_long from np.int
    return sum(np.arange(N))

def sum7(N = N):
    # list returns a list of np.int-s
    return sum(list(np.arange(N)))

def sum7v2(N = N):
    # tolist conversion to python int seems faster than the implicit conversion
    # in sum(list()) (tolist returns a list of python int-s)
    return sum(np.arange(N).tolist())

def sum8(N = N):
    return np.sum(np.arange(N)) # everything in numpy (fortran libblas?)

def array_basic(N = N):
    return np.array(range(N))

def array_dtype(N = N):
    return np.array(range(N),dtype = np.int)

def array_iter(N = N):
    # np.sum's source code mentions to use fromiter to convert from generators
    return np.fromiter(range(N),dtype = np.int)

print(f"while loop:         {timeit(sum0, number = repetition)}")
print(f"for loop:           {timeit(sum1, number = repetition)}")
print(f"sum_range:          {timeit(sum2, number = repetition)}")
print(f"sum_rangelist:      {timeit(sum3, number = repetition)}")
print(f"npsum_range:        {timeit(sum4, number = repetition)}")
print(f"npsum_fromiterrange:{timeit(sum5, number = repetition)}")
print(f"sum_arange:         {timeit(sum6, number = repetition)}")
print(f"sum_list_arange:    {timeit(sum7, number = repetition)}")
print(f"sum_arange_tolist:  {timeit(sum7v2, number = repetition)}")
print(f"npsum_arange:       {timeit(sum8, number = repetition)}")
print(f"array_basic:        {timeit(array_basic, number = repetition)}")
print(f"array_dtype:        {timeit(array_dtype, number = repetition)}")
print(f"array_iter:         {timeit(array_iter,  number = repetition)}")

# Example output:
#
# while loop:         9.249794696999743
# for loop:           6.026467555000636
# sum_range:          1.4830789409988938
# sum_rangelist:      3.6745876889999636
# npsum_range:        16.216972655000063
# npsum_fromiterrange:3.47655400199983
# sum_arange:         16.656015603000924
# sum_list_arange:    19.500842117000502
# sum_arange_tolist:  4.004777374000696
# npsum_arange:       0.2332638230000157
# array_basic:        16.1631146109994
# array_dtype:        16.550737804000164
# array_iter:         3.9803170430004684

共有1个答案

澹台奇略
2023-03-14

让我们看看我能不能总结一下结果。

sum可以处理任何可迭代的值,反复请求下一个值并将其添加。range是一个生成器,它很乐意提供下一个值

# sum_range:          1.4830789409988938

从一个范围中列出一个列表需要时间:

# sum_rangelist:      3.6745876889999636
%%timeit x = list(range(N))
    ...: sum(x)
# npsum_range:        16.216972655000063
# npsum_fromiterrange:3.47655400199983

numpy数组上的迭代比列表慢,因为它必须“解框”每个元素。

# sum_arange:         16.656015603000924

同样,从数组中生成列表也很慢;类似于python级别的迭代。

# sum_list_arange:    19.500842117000502

arr.tolist()相对较快,可以在编译后的代码中创建纯python列表。所以速度类似于从范围列出一个列表。

# sum_arange_tolist:  4.004777374000696
# npsum_arange:       0.2332638230000157
# array_basic:        16.1631146109994
# array_dtype:        16.550737804000164
# array_iter:         3.9803170430004684
 类似资料:
  • 问题内容: java.util.Random源代码的第294行说 为什么是这样? 问题答案: 该描述并不完全准确,因为0不是2的幂。更好的说法是 当n是2的幂或2的幂的负数或零时。 如果n是2的幂,则二进制中的n是单个1,后跟零。-n为2的补数是倒数+ 1,因此位排成一行 要了解其工作原理,请将二进制补码视为逆+ 1。 因为当您添加一个得到两个的补码时,您会一直进行到一个。 如果n不是2的幂,则结

  • 问题内容: 在Java中,表达式为: 似乎等于: 尽管是有效的一元运算符,其优先级高于中的算术运算符。因此,编译器似乎假设该运算符不能为一元运算符,并解析该表达式。 但是,表达式: 即使存在以下唯一有效的解决方案,也不会编译: 和被指定为具有相同的优先级,那么为什么编译器为支持算术而解决看似模棱两可的问题,但为什么不这样做呢? 问题答案: 首先使用最大修改规则将文件标记化(转换为标记序列)-始终获

  • 我可以以大约每秒10,000次插入的速度将插入直接流式传输到BigQuery,但是当我试图使用Dataflow插入时,'tobqrow'步骤(如下所示)非常慢。每10分钟只有50排,这是4名工人。知道为什么吗?以下是相关代码:

  • 在Python3中,行是如何在不到一秒钟的时间内执行的,尽管它应该处理很多项?

  • 问题内容: 我遇到了这个表达式,我认为应该评估为True,但事实并非如此。 上面的语句按预期工作,但在以下情况下: 执行后,其结果为False。 我尝试搜索答案,但无法获得具体答案。谁能帮助我了解这种行为? 问题答案: 是一个运营商链,就像您 要做到这一点,您需要 和 都是如此。后者是错误的,因此是结果。添加括号不会再使运算符链接(某些运算符在括号中),这可以解释工作原理。 尝试: 再一次,一个很

  • 我注意到,如果在ListView中Listitem的TextView中使用android:singleLine=“true”,则滚动会非常缓慢。虽然我找到了另一种android:maxLines=“1”,但我很好奇为什么android:singleLine=“true”会让滚动变得很慢,即使很慢,为什么android仍在使用它?