【问题标题】:more efficient array calculation on this python code case在这个 python 代码案例上更有效的数组计算
【发布时间】:2021-07-02 12:33:52
【问题描述】:

我写了一个函数,它接受一个 N×N 数组并基于它计算一个输出数组。

我的代码如下所示:

 def calculate_output(input,N):
    output = np.zeros((N, N))
    for y in range(N):
        for x in range(N):
            val1 = 0 if y-1<0 else output[y-1][x]+input[y][x]
            val2 = 0 if x-1<0 else output[y][x-1]+input[y][x]
            output[y][x] = max(val1,val2)
    return output

N = 10000
input = np.reshape(np.random.binomial(1, [0.25] * N * N), (N, N))
output =calculate_output(input,N)

但是这个计算速度不够快,在我的机器上大约需要 300 秒。(与在 C++ 上实现时的 3 秒相比) 有什么方法可以在不编写 C 扩展的情况下改善这一点? 我曾尝试使用 pypy,但在这种情况下,使用 pypy 的代码会更慢

【问题讨论】:

  • 这是你为this 尝试的解决方案,是吗?
  • 如果是,您的解决方案几乎可以做到。为了正确起见,您需要使用output[y][x] = input[y][x] + max(val1,val2) 而不是在条件中添加输入,因为我们计算了起始方块的权重。
  • 嗨,确实是我采纳了你的建议,它确实提高了性能。但是,它直到很慢,有什么办法可以改善吗?

标签: python arrays performance optimization


【解决方案1】:

CPython 非常慢,因为它是一个解释器,在这种情况下它显然无法与 C 和 C++ 竞争。降低解释器成本的常用方法是尽可能避免循环,而是使用少量 Numpy 矢量化调用。但是在这种情况下,几乎不可能使用 Numpy 矢量化调用编写高效的实现。

另一方面,由于 JIT 编译,PyPy 通常更适合数字代码。但是它的 Numpy 实现一点也不好,主要是因为他们使用了一个用 Python 重写的 Numpy 实现,它不如原生 Numpy 实现好,而且由于 Python 模块目前的实现方式,原生实现效率不高。简而言之,AFAIK,PyPy JIT 无法使用本机实现优化 Numpy 访问。因此,在您的情况下,JIT 可能比 CPython 解释器慢。

但是,您可以使用专为这个确切用例编写的 Numba JIT 编译器 大大加快代码速度。此外,可以实施一些优化来进一步加快代码速度(无论使用什么编程语言):

  • 条件通常很慢,您可以在循环中移动它们,只执行边界
  • 最初在输出矩阵中写入零不是必需的,实际上速度较慢
  • 使用 2D 直接索引更简洁,可能更快
  • 可以使用整数代替浮点数,因为输出仅包含整数,并且计算整数比使用浮点数计算相同的运算要快。
import numba as nb

@nb.njit(['int32[:,::1](int32[:,::1],int32)', 'int64[:,::1](int64[:,::1],int64)'])
def calculate_output(input,N):
    output = np.empty((N, N), input.dtype)
    for x in range(0,N):
        val2 = 0 if x-1<0 else output[0,x-1]+input[0,x]
        output[0,x] = max(0,val2)
    for y in range(1,N):
        val1 = 0 if y-1<0 else output[y-1,0]+input[y,0]
        output[y,0] = max(val1,0)
    for y in range(1,N):
        for x in range(1,N):
            val1 = output[y-1,x]+input[y,x]
            val2 = output[y,x-1]+input[y,x]
            output[y,x] = max(val1,val2)
    return output

在我的机器上,calculate_output 的调用速度快了 730 倍

【讨论】:

    猜你喜欢
    • 2020-12-25
    • 1970-01-01
    • 2016-03-18
    • 1970-01-01
    • 2012-01-05
    • 2015-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多