带扰动的逐元素向量反演的优化方法

Optimized way of element wise vector inverse with perturbations

我有一个大向量,我想每次都用一个小的扰动计算元素的逆向。例如,对于大 N,我有 y 及其元素的逆 y_inv

y = np.random.rand(1, N)  
y_inv = 1. / y
y_inv[y_inv == np.inf] = 0 # y may contain zeros

我想计算

p = 0.01
z = y + p
z_inv = 1./ z = 1./ (y + p)

多次不同 p 和固定 y。因为 N 非常大,有没有一种有效的方法或近似值,可以在 z_inv 的计算中使用已经计算出的 y_inv?非常感谢任何提高反转速度的建议 z

浮点除法很慢,尤其是 double-precision 浮点数。 Simple-precision 更快(relative 错误可能小于 2.5e-07),如果你这样做,它可以变得更快不需要 high-precision 通过计算 近似倒数 (此近似值的最大 相对 误差小于 3.66e-4 x86-64 处理器)。

假设您可以降低结果的精度,以下是不同方法在 Skylake 处理器上的性能(假设 Numpy 是在 AVX SIMD 指令集的支持下编译的):

Double-precision division:                8 operation/cycle/core
Simple-precision division:                5 operation/cycle/core
Simple-precision approximate reciprocal:  1 operation/cycle/core

您可以通过指定数组的 dtype 轻松切换到 Numpy 的 simple-precision。对于倒数,您需要使用配置为使用 fastmath 模型(njit 的标志)的 Numba(或 Cython)。

另一种解决方案是使用多线程 简单地执行此操作。这可以通过 Numba 使用 njit 标志 parallel=True 和循环遍历 nb.prange 轻松完成。该解决方案可以与前一个解决方案(关于精度)相结合,从而与最初基于 Numpy double-precision 的代码相比,计算速度要快得多。

此外,计算数组 in-place 应该更快(尤其是在使用多线程时)。或者,您可以预分配输出数组并重用它(比 in-place 方法慢但比原始方法快)。 Numpy 函数的参数 out(如 np.divide)可用于执行此操作。

这是一个(未经测试的)并行 Numba 代码示例:

import numba as nb

# See fastmath=True and float32 types for faster performance of approximate results
# Assume `out` is already preallocated
@njit('void(float64[::1], float64[::1], float64)', parallel=True)
def compute(out, y, p):
    assert(out.size == y.size)
    for i in nb.prange(y.size):
        out[i] = 1.0 / (y[i] + p)