栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

各种numpy花式索引方法的性能,以及numba的性能

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

各种numpy花式索引方法的性能,以及numba的性能

您的总结并不完全正确,您已经使用不同大小的数组进行了测试,但是您没有做的一件事就是更改索引元素的数量。

我将其限制为纯索引,并省略了

take
(实际上是整数数组索引)
compress
extract
(因为它们实际上是布尔数组索引)。这些的唯一区别是恒定因素。对这些方法的常数因子
take
compress
将小于开销为numpy的功能
np.take
np.compress
否则的影响可以忽略不计的合理大小的数组。

让我用不同的数字表示它:

# ~ every 500th elementx = np.arange(0, 1000000, dtype=np.float64)idx = np.random.randint(0, 1000000, size=int(1000000/500))  # changed the ratio!bool_mask = np.zeros(x.shape, dtype=np.bool)bool_mask[idx] = True%timeit x[idx]# 51.6 µs ± 2.02 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)%timeit x[bool_mask]# 1.03 ms ± 37.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)# ~ every 50th elementidx = np.random.randint(0, 1000000, size=int(1000000/50))  # changed the ratio!bool_mask = np.zeros(x.shape, dtype=np.bool)bool_mask[idx] = True%timeit x[idx]# 1.46 ms ± 55.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)%timeit x[bool_mask]# 2.69 ms ± 154 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)# ~ every 5th elementidx = np.random.randint(0, 1000000, size=int(1000000/5))  # changed the ratio!bool_mask = np.zeros(x.shape, dtype=np.bool)bool_mask[idx] = True%timeit x[idx]# 14.9 ms ± 495 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)%timeit x[bool_mask]# 8.31 ms ± 181 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

那么这里发生了什么?很简单:整数数组索引只需要访问与索引数组中的值一样多的元素。这意味着如果匹配很少,那么如果索引很多,将会非常快但是很慢。但是,布尔数组索引始终需要遍历整个布尔数组并检查“真”值。这意味着该数组应该大致“恒定”。

但是,等等,对于布尔数组来说,它并不是真正恒定的,为什么整数数组索引需要比布尔数组索引花费更长的时间(最后一种情况),即使它必须处理的元素减少约5倍?

那就是它变得更加复杂的地方。在这种情况下,布尔数组

True
位于随机位置,这意味着它将经受 分支预测失败
。如果
True
并且
False
发生次数相等,但发生在随机位置,则发生这些事件的可能性更大。这就是为什么布尔数组索引越来越慢-
因为比
True
False
得到更加平等,从而更加“随机”。如果有更多
True
的,结果数组也会更大,这也会消耗更多的时间。

作为该分支预测事物的示例,请以以下示例为例(可能因不同的系统/编译器而异):

bool_mask = np.zeros(x.shape, dtype=np.bool)bool_mask[:1000000//2] = True   # first half True, second half False%timeit x[bool_mask]# 5.92 ms ± 118 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)bool_mask = np.zeros(x.shape, dtype=np.bool)bool_mask[::2] = True   # True and False alternating%timeit x[bool_mask]# 16.6 ms ± 361 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)bool_mask = np.zeros(x.shape, dtype=np.bool)bool_mask[::2] = Truenp.random.shuffle(bool_mask)  # shuffled%timeit x[bool_mask]# 18.2 ms ± 325 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

因此,即使布尔掩码包含相同数量的s

True
and的分布
False
也会严重影响运行时间
True
compress
-functions可以看到相同的效果。

对于整数数组索引(同样

np.take
),将看到另一个效果: cache locality
。您的情况下的索引是随机分布的,因此您的计算机必须执行很多“ RAM”到“处理器缓存”的加载,因为两个索引彼此之间不太可能相互接近。

比较一下:

idx = np.random.randint(0, 1000000, size=int(1000000/5))%timeit x[idx]# 15.6 ms ± 703 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)idx = np.random.randint(0, 1000000, size=int(1000000/5))idx = np.sort(idx)  # sort them%timeit x[idx]# 4.33 ms ± 366 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

通过对索引进行排序,下一个值将已经存在于高速缓存中的机会大大增加,这可能导致巨大的加速。如果您知道索引将被排序,那么这是一个非常重要的因素(例如,如果索引是由

np.where
它们创建的,则
np.where
对索引进行排序特别有效)。

因此,这并不取决于整数数组索引对于较小的数组而言较慢,而对于较大的数组而言则取决于更多因素。两者都有其用例,并且根据情况,它们可能(相当)快于另一种。


让我也谈一谈numba函数。首先是一些一般性的陈述:

  • cache
    不会有所不同,只是避免重新编译函数。在交互式环境中,这实际上是没有用的。但是,如果将功能打包在模块中会更快。
  • nogil
    本身不会提供任何速度提升。如果在不同的线程中调用它将更快,因为每个函数执行可以释放GIL,然后多个调用可以并行运行。

否则我不知道numba如何有效地实现这些功能,但是当您在numba中使用NumPy功能时,它可能会变慢或变快-
但即使速度更快,它也不会变快(也许对于小型阵列而言)。因为如果可以更快地进行开发,NumPy开发人员也会实施它。我的经验法则是:如果可以使用NumPy(向量化)进行操作,请不要理会numba。只有当您无法使用向量化NumPy函数或NumPy做到这一点时,它将使用过多的临时数组,然后numba才会发光!



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/652263.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号