- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
所以我正在测试同一功能的两个版本的速度;一种是两次反转 numpy 数组的 View ,另一种是没有。代码如下:
import numpy as np
from numba import njit
@njit
def min_getter(arr):
if len(arr) > 1:
result = np.empty(len(arr), dtype = arr.dtype)
local_min = arr[0]
result[0] = local_min
for i in range(1,len(arr)):
if arr[i] < local_min:
local_min = arr[i]
result[i] = local_min
return result
else:
return arr
@njit
def min_getter_rev1(arr1):
if len(arr1) > 1:
arr = arr1[::-1][::-1]
result = np.empty(len(arr), dtype = arr.dtype)
local_min = arr[0]
result[0] = local_min
for i in range(1,len(arr)):
if arr[i] < local_min:
local_min = arr[i]
result[i] = local_min
return result
else:
return arr1
size = 500000
x = np.arange(size)
y = np.hstack((x[::-1], x))
y_min = min_getter(y)
yrev_min = min_getter_rev1(y)
令人惊讶的是,带有额外操作的那个在多个场合运行得稍微快一些。我用了%timeit
两个功能大约 10 次;尝试了不同大小的数组,差异很明显(至少在我的电脑上是这样)。 min_getter
的运行时间在附近:
2.35 ms ± 58.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
(有时是2.33,有时是2.37,但从不低于2.30)
和 min_getter_rev1
的运行时间在附近:
2.22 ms ± 23.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
(有时是2.25,有时是2.23,但很少超过2.30)
关于为什么以及如何发生的任何想法?速度差异大约增加了 4-6%,这在某些应用程序中可能是一个大问题。加速的底层机制可能有助于加速一些 jitted 代码
注1:我试过size=5000000,每个函数都测试了5-10次,差异更加明显。较快的运行在 23.2 ms ± 51.7 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
较慢的是 24.4 ms ± 234 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
注2:numpy
的版本和 numba
在测试期间是 1.16.5
和 0.45.1
; python 版本是 3.7.4
; IPython
版本是 7.8.0
;使用的 Python IDE 是 spyder
.不同版本测试结果可能不同。
最佳答案
TL;DR:第二个代码更快可能只是一个幸运的巧合。
检查生成的类型揭示了一个重要的区别:
arr
被键入为 array(int32, 1d, C)
一个 C 连续数组。min_getter.inspect_types()
min_getter (array(int32, 1d, C),) <--- THIS IS THE IMPORTANT LINE
--------------------------------------------------------------------------------
# File: <>
# --- LINE 4 ---
# label 0
@njit
# --- LINE 5 ---
def min_getter(arr):
[...]
arr
被键入为 array(int32, 1d, A)
,这是一个不知道是否连续的数组。这是因为 [::-1]
返回一个没有连续性信息的数组,一旦丢失,第二个 [::-1]
就无法恢复。<>>> min_getter_rev1.inspect_types()
[...]
# --- LINE 18 ---
# arr1 = arg(0, name=arr1) :: array(int32, 1d, C)
# $const0.2 = const(NoneType, None) :: none
# $const0.3 = const(NoneType, None) :: none
# $const0.4 = const(int, -1) :: Literal[int](-1)
# $0.5 = global(slice: <class 'slice'>) :: Function(<class 'slice'>)
# $0.6 = call $0.5($const0.2, $const0.3, $const0.4, func=$0.5, args=(Var($const0.2, <> (18)), Var($const0.3, <> (18)), Var($const0.4, <> (18))), kws=(), vararg=None) :: (none, none, int64) -> slice<a:b:c>
# del $const0.4
# del $const0.3
# del $const0.2
# del $0.5
# $0.7 = static_getitem(value=arr1, index=slice(None, None, -1), index_var=$0.6) :: array(int32, 1d, A)
# del arr1
# del $0.6
# $const0.8 = const(NoneType, None) :: none
# $const0.9 = const(NoneType, None) :: none
# $const0.10 = const(int, -1) :: Literal[int](-1)
# $0.11 = global(slice: <class 'slice'>) :: Function(<class 'slice'>)
# $0.12 = call $0.11($const0.8, $const0.9, $const0.10, func=$0.11, args=(Var($const0.8, <> (18)), Var($const0.9, <> (18)), Var($const0.10, <> (18))), kws=(), vararg=None) :: (none, none, int64) -> slice<a:b:c>
# del $const0.9
# del $const0.8
# del $const0.10
# del $0.11
# $0.13 = static_getitem(value=$0.7, index=slice(None, None, -1), index_var=$0.12) :: array(int32, 1d, A)
# del $0.7
# del $0.12
# arr = $0.13 :: array(int32, 1d, A) <---- THIS IS THE IMPORTANT LINE
# del $0.13
arr = arr1[::-1][::-1]
[...]
(其余生成的代码几乎相同)
如果已知数组是连续的,索引和迭代应该会更快。但这不是我们在这种情况下观察到的情况 - 恰恰相反。
那么可能是什么原因呢?
Numba 本身使用 LLVM 来“编译” jitted 代码。所以有一个实际的编译器参与,编译器可以进行优化。尽管 inspect_types()
检查的代码几乎相同,但实际的 LLVM/ASM 代码与 inspect_llvm()
和 inspect_asm()
大不相同。因此,编译器(或 numba)能够在第二种情况下进行某种优化,这在第一种情况下是不可能的。或者应用于第一种情况的某些优化实际上使代码变得更糟。
然而,这意味着我们在第二种情况下只是“走运”。这可能不是可以控制的,因为它取决于:
有太多可以应用优化(或不应用优化)的移动部件。
有趣的事实:如果你扔掉外面的 if
s:
import numpy as np
from numba import njit
@njit
def min_getter(arr):
result = np.empty(len(arr), dtype = arr.dtype)
local_min = arr[0]
result[0] = local_min
for i in range(1,len(arr)):
if arr[i] < local_min:
local_min = arr[i]
result[i] = local_min
return result
@njit
def min_getter_rev1(arr1):
arr = arr1[::-1][::-1]
result = np.empty(len(arr), dtype = arr.dtype)
local_min = arr[0]
result[0] = local_min
for i in range(1,len(arr)):
if arr[i] < local_min:
local_min = arr[i]
result[i] = local_min
return result
size = 500000
x = np.arange(size)
y = np.hstack((x[::-1], x))
y_min = min_getter(y)
yrev_min = min_getter_rev1(y)
%timeit min_getter(y) # 2.29 ms ± 86.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit min_getter_rev1(y) # 2.37 ms ± 212 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
在那种情况下,没有 [::-1][::-1]
的速度更快。
因此,如果你想让它可靠地更快:移动 if len(arr) > 1
检查函数外部并且不要使用 [::-1][::-1]
因为在大多数情况下,这会使函数运行得更慢(并且可读性更差)!
关于python - 在 jitted 函数中两次反转 numpy 数组的 View 使函数运行得更快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58039192/
在动态语言中,动态类型代码 JIT 是如何编译成机器码的?更具体地说:编译器是否会在某个时候推断类型?还是在这些情况下严格解释? 例如,如果我有类似下面的伪代码 def func(arg) i
X86 和 AMD64 是许多计算环境(桌面、服务器和 super 计算机)最重要的架构。显然,JIT 编译器应该同时支持它们才能获得认可。 直到最近,SPARC 架构才是编译器合乎逻辑的下一步,特别
既然有如此多的 JIT 实现,每个 JIT 都会发出 native 代码。那么为什么没有人制作像 JIT2EXE 这样的工具来将 native 代码保存为 native 可执行文件呢? 最佳答案 这个
JIT 编译器将字节码编译成机器码的概念我还是不太清楚。我想知道为什么它比非 JIT 解释器产生更快的代码。有人可以给我一个很好的例子来说明这个过程是如何完成的吗? 最佳答案 假设您有一个需要执行一百
Torchscript 提供了 torch.jit.trace 和 torch.jit.script 将 pytorch 代码从 Eager 模式转换为脚本模型。从文档中,我可以理解 torch.ji
好的,我已经阅读了一些关于 JIT 和非 JIT 启用解释器之间差异的讨论,以及为什么 JIT 通常会提高性能。 但是,我的问题是: 最终,不支持 JIT 的解释器是否必须像 JIT 编译器那样将字节
有没有办法在消除 JIT 开销的同时实现 JIT 性能?最好通过将类文件编译为 native 镜像。 我研究过GCJ,但即使对于简单的程序,GCJ输出的性能也比Java JIT差很多。 最佳答案 您可
我试图更好地理解 JIT 编译器在 volatile 变量值缓存方面如何为 java 工作。考虑这个问题中提出的例子: Infinite loop problem with while loop an
我的代码是这样的: @jit(nopython=True) def sum_fn(arg1, arg2, ...argn): ..... for i in xrange(len(arg
以下代码无效: def get_unique(arr): return jnp.unique(arr) get_unique = jit(get_unique) get_unique(jnp.
我需要能够调用一个 GPU 函数,该函数本身间接调用另一个 GPU 函数: from numba import cuda, jit import numpy as np # GPU function
我有一个关于使用 Continuum 的 Accelerate 和 numba 包中的 Python CUDA 库的问题。正在使用装饰器@jit与 target = gpu同 @cuda.jit ?
有人可以指出我的方向,这可能会让我明白为什么 JIT 会取消优化我的循环? (OSR)。看起来它被 C1 编译一次,然后多次取消优化(我可以看到数十或数百个以 开头的日志) 这是包含该重要循环的类:
我引用了Oracle的以下文档: http://docs.oracle.com/cd/E13150_01/jrockit_jvm/jrockit/geninfo/diagnos/underst_jit
我需要在 C++ 中运行预训练的 pytorch 神经网络模型(在 python 中训练)来进行预测。 为此,我按照此处给出的有关如何在 C++ 中加载 pytorch 模型的说明进行操作:https
我正在使用 numbas @jit 装饰器在 python 中添加两个 numpy 数组。如果我使用 @jit 与 python 相比,性能是如此之高。 然而,即使我传入 @numba.jit(nop
我是Python新手。 我编写了一些代码尝试将图片混合为新图片。 我完成了,但是浪费了太多时间。 所以我尝试使用 Numba 让代码在我的 GPU 上运行。但遇到一些警告和错误 os Ubuntu 1
我正在将我的网站从安装在共享网络托管帐户(在 DreamHost)上的 PHP v.5 转换为在 PHP 7.3.11 上运行。转换后,我开始注意到偶尔会收到以下警告: Warning: preg_m
在 Stack Overflow 上向所有编译器设计者致以问候。 我目前正在从事一个项目,该项目的重点是开发一种用于高性能计算的新脚本语言。源代码首先被编译成字节码表示。字节码然后由运行时加载,它对其
我相信 Apple 已禁止在 ARM64 架构上同时写入和执行内存,请参阅: 参见 mmap() RWX page on MacOS (ARM64 architecture)? 这使得像 jonesf
我是一名优秀的程序员,十分优秀!