- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
由于 NumPy 不使用多核,我正在学习使用 NumExpr 加速 NumPy 代码,因为它对多线程有很好的支持。下面是我正在使用的示例:
# input array to work with
x = np.linspace(-1, 1, 1e7)
# a cubic polynomial expr
cubic_poly = 0.25*x**3 + 0.75*x**2 + 1.5*x - 2
%timeit -n 10 cubic_poly = 0.25*x**3 + 0.75*x**2 + 1.5*x - 2
# 657 ms ± 5.04 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
现在,我们可以使用 NumExpr 做同样的事情:
cubic_poly_str = "0.25*x**3 + 0.75*x**2 + 1.5*x - 2"
# set number of threads to 1 for fair comparison
ne.set_num_threads(1)
%timeit -n 10 ne.evaluate(cubic_poly_str)
# 60.5 ms ± 908 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
从时间上我们可以看出,即使我们使用与 NumPy 相同数量的线程(即 1),NumExpr
也快 10 倍以上
现在,让我们增加计算并使用所有可用线程并观察:
# use all available threads/cores
ne.set_num_threads(ne.detect_number_of_threads())
%timeit -n 10 ne.evaluate(cubic_poly_str)
# 16.1 ms ± 82.4 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
# sanity check
np.allclose(cubic_poly, ne.evaluate(cubic_poly_str))
不出所料且令人信服,这比仅使用单线程快 5 倍。
为什么即使使用相同数量的线程(即 1),NumExpr 也快 10 倍?
最佳答案
您认为加速仅/主要来自并行化的假设是错误的。正如@Brenlla 已经指出的那样,numexpr 加速的大部分通常来自对缓存的更好利用。然而,还有其他一些原因。
首先,numpy 和 numexpr 不计算相同的表达式:
x**3
和 x**2
计算为 pow(x,3)
和 pow(x, 2)
。x**3=x*x*x
和 x**2=x*x
。pow
比一两次乘法更复杂,因此要慢得多,比较一下:
ne.set_num_threads(1)
%timeit ne.evaluate("0.25*x**3 + 0.75*x**2 + 1.5*x - 2")
# 60.7 ms ± 1.2 ms, base line on my machine
%timeit 0.25*x**3 + 0.75*x**2 + 1.5*x - 2
# 766 ms ± 4.02 ms
%timeit 0.25*x*x*x + 0.75*x*x + 1.5*x - 2
# 130 ms ± 692 µs
现在,numexpr 的速度只有原来的两倍。我的猜测是,pow
版本受 CPU 限制,而乘法版本受内存限制更多。
当数据很大时,Numexpr 会大放异彩 - 大于 L3 缓存(例如我机器上的 15Mb),这在您的示例中给出,因为 x
大约为 76Mb:
我们可以使用例如 valgrind
查看缓存未命中(请参阅本文附录中的脚本):
>>> valgrind --tool=cachegrind python np_version.py
...
...
==5676== D refs: 1,144,572,370 (754,717,376 rd + 389,854,994 wr)
==5676== D1 misses: 220,844,716 (181,436,970 rd + 39,407,746 wr)
==5676== LLd misses: 217,056,340 (178,062,890 rd + 38,993,450 wr)
==5676== D1 miss rate: 19.3% ( 24.0% + 10.1% )
==5676== LLd miss rate: 19.0% ( 23.6% + 10.0% )
....
对我们来说有趣的部分是 LLd-misses
(即 L3-misses,有关输出解释的信息,请参阅 here)- 大约 25% 的读取访问是未命中。
对 numexpr 的相同分析显示:
>>> valgrind --tool=cachegrind python ne_version.py
...
==5145== D refs: 2,612,495,487 (1,737,673,018 rd + 874,822,469 wr)
==5145== D1 misses: 110,971,378 ( 86,949,951 rd + 24,021,427 wr)
==5145== LLd misses: 29,574,847 ( 15,579,163 rd + 13,995,684 wr)
==5145== D1 miss rate: 4.2% ( 5.0% + 2.7% )
==5145== LLd miss rate: 1.1% ( 0.9% + 1.6% )
...
只有 5% 的读取未命中!
然而,numpy 也有一些优势:在引擎盖下,numpy 使用 mkl-routines(至少在我的机器上),而 numexpr 没有。因此 numpy 最终使用打包的 SSE 操作(movups
+mulpd
+addpd
),而 numexpr 最终使用标量版本( movsd
+mulsd
).
这解释了 numpy 版本 25% 的未命中率:一次读取是 128 位 (movups
),这意味着在 4 次读取后缓存行(64 字节)被处理并产生了一个未命中。可以在配置文件中看到(例如Linux上的perf
):
32,93 │ movups 0x10(%r15,%rcx,8),%xmm4
1,33 │ movups 0x20(%r15,%rcx,8),%xmm5
1,71 │ movups 0x30(%r15,%rcx,8),%xmm6
0,76 │ movups 0x40(%r15,%rcx,8),%xmm7
24,68 │ movups 0x50(%r15,%rcx,8),%xmm8
1,21 │ movups 0x60(%r15,%rcx,8),%xmm9
2,54 │ movups 0x70(%r15,%rcx,8),%xmm10
每四个 movups
需要更多时间,因为它等待内存访问。
Numpy 擅长较小的数组大小,适合 L1 缓存(但是最大的份额是开销而不是计算本身,这在 numpy 中速度更快 - 但这并没有起到很大的作用):
x = np.linspace(-1, 1, 10**3)
%timeit ne.evaluate("0.25*x*x*x + 0.75*x*x + 1.5*x - 2")
# 20.1 µs ± 306 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit 0.25*x*x*x + 0.75*x*x + 1.5*x - 2
# 13.1 µs ± 125 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
附带说明:将函数计算为 ((0.25*x + 0.75)*x + 1.5)*x - 2
会更快。
两者都是因为 CPU 使用率较低:
# small x - CPU bound
x = np.linspace(-1, 1, 10**3)
%timeit ((0.25*x + 0.75)*x + 1.5)*x - 2
# 9.02 µs ± 204 ns
和更少的内存访问:
# large x - memory bound
x = np.linspace(-1, 1, 10**7)
%timeit ((0.25*x + 0.75)*x + 1.5)*x - 2
# 73.8 ms ± 3.71 ms
list :
A np_version.py
:
import numpy as np
x = np.linspace(-1, 1, 10**7)
for _ in range(10):
cubic_poly = 0.25*x*x*x + 0.75*x*x + 1.5*x - 2
B ne_version.py
:
import numpy as np
import numexpr as ne
x = np.linspace(-1, 1, 10**7)
ne.set_num_threads(1)
for _ in range(10):
ne.evaluate("0.25*x**3 + 0.75*x**2 + 1.5*x - 2")
关于python - 使用 NumExpr : An analysis 提高 NumPy 代码的运行时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55675127/
我需要在大约一千万点处评估 BesselK 函数。我知道scipy.special支持此为 scipy.special.kv(n, x) ,但我想要更快的评估以及内存有效的评估。理想的numexpr会
我过去在很多机器上成功安装了 numexpr。但现在我实在无法应付使其在新的 Linux 系统上运行。本质上我下载了 tar.gz 文件并且 python setup.py install --hom
我正在尝试执行以下操作: import numexpr as ne a = np.random.rand(10, 1) b = np.random.rand(1, 10) ne.NumExpr('su
我想评估 python (2.7) 中 numexpr 模块的性能。为此,我创建了一个大小为 (10^5, 10^5) 的随机稀疏矩阵。但是,下面的脚本已经在表达式求值步骤中抛出错误,表示它无法识别对
我正在使用 sympy 进行一些符号数学运算,然后使用 eval 和 sympy 的 lambdastr 实用程序生成 Python lambda 函数。这是我的意思的一个简化示例: import s
我需要使用 numexpr 重写这段代码,它正在计算矩阵数据 [行 x 列] 和向量 [1 x 列] 的欧几里德范数矩阵。 d = ((data-vec)**2).sum(axis=1) 如何实现?或
我正在尝试在 numexpr 表达式中使用对象属性。最明显的做法是: import numpy as np import numexpr as ne class MyClass: def __
如何使用 numexpr 有效地表达以下内容? z = min(x-y, 1.0) / (x+y) 这里,x 和y 是一些相同形状的大型 NumPy 数组。 换句话说,在除以 x+y 之前,我试图将
在 python numexpr 中将值分配给您正在操作的同一数组以避免创建临时数组是否安全? 来自 project homepage 上的内存使用说明看起来不错,但如果不深入研究源代码,这几乎不是一
在 Windows 10 Python 3.7.9 (IDLE) 上,我成功安装了“pip install numexpr”,但在“import numexpr as ne”时出现错误: 回溯(最近调
我在 Windows 7 机器上通过 pip 安装 numexpr 包: pip list | grep numexpr numexpr (2.4.6) 但是当我打开 ipython 并尝试使用 df
考虑以下 dtype float32 的 NumPy 数组: In [29]: x = numpy.arange(10, dtype=numpy.float32) 当我使用pytables.Expr将
我刚刚开始使用 numexpr,虽然 github 存储库似乎有一些如何使用它的基本示例,但我无法清楚地理解它们如何应用于某些复杂的情况。假设我有一个函数: def func(x): #x is
我在安装 numexpr 以安装 PyTables 时遇到问题,因此我可以处理大量数据集。 构建细节:Windows 7 专业版 64 位、Python 2.7、numexpr 2.01、numpy
我一直在尝试学习 Numexpr包裹。关于如何使用它的例子充其量是稀疏的。有人可以给我一个关于如何使用“local_dict”和“global_dict”参数的简单示例吗? 最佳答案 下面的例子可能会
我对 NumPy 据说是对其算术数组操作进行矢量化的概念感到有点挣扎:它是否克服了 Python 的 GIL,因为 NumPy 的一部分是用 C 实现的?另外,Numexpr 是如何工作的呢?如果我理
我正在尝试使用 numexpr 加速 lambdify 生成的一些数字代码。不幸的是,基于numexpr 的函数在使用sqrt 函数时会中断,即使它是supported functions 之一。 .
由于 NumPy 不使用多核,我正在学习使用 NumExpr 加速 NumPy 代码,因为它对多线程有很好的支持。下面是我正在使用的示例: # input array to work with x =
测试代码: import numpy as np import pandas as pd COUNT = 1000000 df = pd.DataFrame({ 'y': np.random.
我正在尝试创建一个较小的,分层的样本,以减少处理时间。 运行此代码: df_strat= stratified_sample(df, ["Parental Status","Gender", "Age
我是一名优秀的程序员,十分优秀!