在Python中,array.count()的数量级是否比list.count()慢几个?

时间:2018-05-12 11:56:55

标签: python arrays numpy count

目前我正在玩Python性能,试图加速我的程序(通常是计算启发式的程序)。我总是使用列表,尽量不要进入numpy数组。

但是最近我听说Python有8.7. array — Efficient arrays of numeric values所以我想我会试试那个。

我编写了一段代码来衡量array.count()list.count(),因为我在代码中的很多地方使用它:

from timeit import timeit
import array

a = array.array('i', range(10000))
l = [range(10000)]


def lst():
    return l.count(0)


def arr():
    return a.count(0)


print(timeit('lst()', "from __main__ import lst", number=100000))
print(timeit('arr()', "from __main__ import arr", number=100000))

使用array时,我期待性能略有提升。嗯,这就是发生的事情:

> python main.py
0.03699162653848456
74.46420751473268

因此,根据timeitlist.count()array.count()快2013倍。我绝对没想到。所以我通过SO,python docs等搜索过,我发现的唯一事情就是数组中的对象必须首先被包装到int - s中,所以这可能会减慢速度,但我是期望在创建array.array - 实例时发生这种情况,而不是在随机访问它时(我认为这是.count()所做的)。

那么抓到了哪里?

我做错了吗?

或许我不应该使用标准数组并直接进入numpy.array

1 个答案:

答案 0 :(得分:1)

  

哪里是捕获?

如上所述,初始测试并不比较苹果与苹果:

未提及,其中 range() 确实创建了RAM分配的数据结构,而 xrange() 类似于重新制定的对象(如下所示) generator - 永远不会与任何智能RAM分配的数据结构相比。

>>> L_above_InCACHE_computing = [ range( int( 1E24 ) ) ]    # list is created
>>> L_above_InCACHE_computing.count( 0 )                    # list has no 0 in
0
>>> L_above_InCACHE_computing.count( range( int( 1E24 ) )  )# list has this in
1

生成器的对象内在 .__len__() 吐出长度,仍然没有计数发生,是吗?(很高兴它没有,它不适合进入甚至~ 10^20 [TB]的RAM ... ,但它可以“生活”在py3 +中作为对象)

>>> print( L[0].__doc__ )
range(stop) -> range object
range(start, stop[, step]) -> range object

Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).

定量公平测试?需要更好的测试工程细节:

远远超过几十MB,以避免来自InCACHE计算工件的错误期望,这些工件永远不会扩展到实际问题大小:

>>> L_above_InCACHE_computing = [ range( int( 1E24 ) ) ]
>>> L_above_InCACHE_computing[0]
range(0, 999999999999999983222784)

>>> print( L_above_InCACHE_computing[0].__len__() )
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: Python int too large to convert to C ssize_t

进入RAM可行,但高于InCACHE-horizo​​n sizings:

# L_aRunABLE_above_InCACHE_computing = [ range( int( 1E9 ) ) ] # ~8+GB ->array
# would have no sense to test 
# a benchmark on an array.array().count( something ) within an InCACHE horizon
  

直接转到 numpy 数组?

绝对是一个明智的步骤来测试。矢量化内部性可能会令人惊讶,并且通常会做很多事情:o)

取决于你的其他代码,如果numpy-strength甚至可以提升你的代码库的其他部分。最后但同样重要的是,要注意过早的优化和扩展。如果可以在 [TIME] -domain中花费更多,则可以应对一些 [SPACE] - 域陷阱,但最危险的是丢失InCACHE-locality,其中没有权衡可能会有所帮助。因此,最好不要过早地锁定有希望的细节,但要以失去全球规模的绩效目标为代价。