2015-04-23 62 views
2

我将改进我的代码片段的性能,该代码片段将经常以递归方式获取子数组。为什么Numpy.array比获取子列表的内建列表慢

所以我用numpy.array代替了内建列表。因为,据我所知,在获取子数组时,numpy.array不会复制原始列表。

但是当我改成numpy.array时,性能变差了。所以我想知道原因。谢谢!

以下是我的代码片段,通过使用不同的对象,我得到了执行时间:

import timeit 
stat = ''' 
import numpy 
def func(a): 
    a[len(a)-1] += 1 
    if len(a) == 1: 
     return a[0] 
    else: 
     return func(a[1:len(a)]) 
a1=[1,2,3,4,5,6,7,8,9,10] 
a2=numpy.array([1,2,3,4,5,6,7,8,9,10]) 
''' 
if __name__ == "__main__": 
    print "Execution time with build-in list: {0}".format(timeit.timeit('func(a1)', setup = stat, number = 1000)) 
    print "Execution time with Numpy array: {0}".format(timeit.timeit('func(a2)', setup = stat, number = 1000)) 

而且在我的64位MAC(Python的2.7.6 + NumPy的1.8.0rc1)输出:

Execution time with build-in list: 0.00507998466492 
Execution time with Numpy array: 0.0195469856262 
+0

它可能不会复制原始列表,但您仍然需要创建一个新的numpy数组,其中包含原始引用,这显然比创建新列表更加昂贵。你可以尝试用一个*很大的列表/数组来测试它。 – chepner

+0

以chepner为基础,随着数组大小的增加,时间如何变化?如果他的假设是正确的,那么列表运行时不会发生显着变化,但数组运行时会线性增加。 – WakkaDojo

+0

@chepner看来你是对的。我测试和numpy阵列比内置列表大阵列更快。但是你能解释为什么创建一个新的numpy数组引用比创建一个新列表显然更昂贵吗? – AngelIW

回答

0

感谢所有人的回答和对这个问题的评论,你已经显示出宝贵的信息给我做出这个答案。

答案是这样的。导致numpy数组在我的问题中表现不佳的原因是,访问单个项目并在numpy数组上分配内置类型比在内置列表上慢。实际上,获取numpy数组的子数组的性能比内置列表的性能增益确实存在。但是短阵列中的增益太小,例如在我的例子中len = 10的数组,所以小增益被丢失的是这行:a[len(a)-1] += 1其中我们访问了单个项目并在内置类型int之间转换。


下面的代码证明了原因:

import numpy 
from timeit import timeit 
stat = ''' 
import numpy 
a1 = range(4000) 
a2 = numpy.array(a1) 
i = 0 
''' 
if __name__ == "__main__": 
    test_times = 1000 
    print '1. {0:.8f}'.format(timeit('a1[i]', setup = stat, number = test_times)) 
    print '2. {0:.8f}'.format(timeit('a2[i]', setup = stat, number = test_times)) 
    print '3. {0:.8f}'.format(timeit('i += a1[i]; ++i', setup = stat, number = test_times)) 
    print '4. {0:.8f}'.format(timeit('i += a2[i]; ++i', setup = stat, number = test_times)) 
    print '5. {0:.8f}'.format(timeit('a = a1[i:len(a1)]; ++i', setup = stat, number = test_times)) 
    print '6. {0:.8f}'.format(timeit('a = a2[i:len(a2)]; ++i', setup = stat, number = test_times)) 

运行结果在我的Mac是以下几点:

1. 0.00005913 
2. 0.00017881 
3. 0.00008607 
4. 0.00084305 
5. 0.01492000 
6. 0.00053406 

我们可以从以上结果得到这些:

  1. 1对2:访问单个项目比内置列表时numpy的阵列较慢。
  2. 2比4:似乎需要数据转换,这花费额外的时间,当在numpy的阵列内建类型(INT)项的分配数据。对于内置列表,花费的时间非常少。
  3. 5比6:numpy的取子阵列内置列表进行比较时,真正得救的时间。
1

,如果你修改的代码的最后两行,你会得到相同的执行时间如下:

print "Execution time with build-in list: {0}".format(timeit.timeit(
    'func(a1)', setup = stat, number = 1000), 'gc.enable()') 
print "Execution time with Numpy array: {0}".format(timeit.timeit(
    'func(a2)', setup = stat, number = 1000), 'gc.enable()') 

在这两种情况下,我们都允许 timeit开启所谓的垃圾回收,即当内存不再使用时释放内存的过程。上述修改返回,例如:

Execution time with build-in list: 0.00580596923828 
Execution time with Numpy array: 0.00822710990906 

具有相同的数量级。根据文档 timeit“默认情况下,它暂时关闭垃圾收集时间。这种方法的优点是它使独立的时间更具可比性,这个缺点是垃圾收集可能是性能的重要组成部分被测量的功能“。

有一个很好的理解什么方法,即有或没有垃圾回收,应该使用什么时候,什么时候使用。另请注意,如果您应用时间模块中的time.time()块,您将获得更长的时间。

+0

你能解释这两个关于你的答案的问题吗? – AngelIW

+0

1.你能解释一下为什么GC可能会影响到消费的时间和他们花同时应用GC后的原因是什么? 2.为什么time.time()会获得更长的时间? – AngelIW