gpt4 book ai didi

python - 提高 Python 中 for 循环的性能(可能使用 numpy 或 numba)

转载 作者:行者123 更新时间:2023-11-28 18:35:16 24 4
gpt4 key购买 nike

我想提高此函数中 for 循环的性能。

import numpy as np
import random

def play_game(row, n=1000000):
"""Play the game! This game is a kind of random walk.

Arguments:
row (int[]): row index to use in the p matrix for each step in the
walk. Then length of this array is the same as n.

n (int): number of steps in the random walk
"""
p = np.array([[ 0.499, 0.499, 0.499],
[ 0.099, 0.749, 0.749]])
X0 = 100
Y0 = X0 % 3
X = np.zeros(n)
tempX = X0
Y = Y0

for j in range(n):
tempX = X[j] = tempX + 2 * (random.random() < p.item(row.item(j), Y)) - 1
Y = tempX % 3

return np.r_[X0, X]

困难在于 Y 的值是根据 X 的值在每一步计算的 Y 然后在下一步中用于更新 X 的值。

我想知道是否有一些 numpy 技巧可以产生很大的不同。使用 Numba 是公平的游戏(我尝试过但没有成功)。但是,我不想使用 Cython。

最佳答案

快速观察告诉我们,函数代码中的迭代之间存在数据依赖性。现在,存在不同类型的数据依赖关系。您正在查看的数据依赖性类型是索引依赖性,即任何迭代中的数据选择都取决于先前的迭代计算。这种依赖关系似乎很难在迭代之间进行追踪,因此这篇文章并不是真正的矢量化解决方案。相反,我们会尝试尽可能多地预先计算将在循环中使用的值。基本思想是在循环内做最少的工作。

这里简要说明了我们如何进行预计算,从而获得更有效的解决方案:

  • 鉴于 p 的形状相对较小,要根据输入 row 从中提取行元素,您可以预先选择所有这些行从 pp[row]

  • 对于每次迭代,您都在计算一个随机数。您可以将其替换为您可以在循环之前设置的随机数组,因此,您也可以预先计算这些随机值。

  • 根据到目前为止的预先计算值,您将拥有 p 中所有行的列索引。请注意,这些列索引将是一个包含所有可能列索引的大型 ndarray,并且在我们的代码中,只会根据每次迭代计算选择一个。使用每次迭代列索引,您可以递增或递减 X0 以获得每次迭代输出。

实现看起来像这样-

randarr = np.random.rand(n)
p = np.array([[ 0.499, 0.419, 0.639],
[ 0.099, 0.749, 0.319]])

def play_game_partvect(row,n,randarr,p):

X0 = 100
Y0 = X0 % 3

signvals = 2*(randarr[:,None] < p[row]) - 1
col_idx = (signvals + np.arange(3)) % 3

Y = Y0
currval = X0
out = np.empty(n+1)
out[0] = X0
for j in range(n):
currval = currval + signvals[j,Y]
out[j+1] = currval
Y = col_idx[j,Y]

return out

为了验证原始代码,您可以像这样修改原始代码 -

def play_game(row,n,randarr,p):
X0 = 100
Y0 = X0 % 3
X = np.zeros(n)
tempX = X0
Y = Y0
for j in range(n):
tempX = X[j] = tempX + 2 * (randarr[j] < p.item(row.item(j), Y)) - 1
Y = tempX % 3
return np.r_[X0, X]

请注意,由于此代码预先计算了这些随机值,因此这已经可以为您提供比问题中的代码更好的加速。

运行时测试和输出验证 -

In [2]: # Inputs
...: n = 1000
...: row = np.random.randint(0,2,(n))
...: randarr = np.random.rand(n)
...: p = np.array([[ 0.499, 0.419, 0.639],
...: [ 0.099, 0.749, 0.319]])
...:

In [3]: np.allclose(play_game_partvect(row,n,randarr,p),play_game(row,n,randarr,p))
Out[3]: True

In [4]: %timeit play_game(row,n,randarr,p)
100 loops, best of 3: 11.6 ms per loop

In [5]: %timeit play_game_partvect(row,n,randarr,p)
1000 loops, best of 3: 1.51 ms per loop

In [6]: # Inputs
...: n = 10000
...: row = np.random.randint(0,2,(n))
...: randarr = np.random.rand(n)
...: p = np.array([[ 0.499, 0.419, 0.639],
...: [ 0.099, 0.749, 0.319]])
...:

In [7]: np.allclose(play_game_partvect(row,n,randarr,p),play_game(row,n,randarr,p))
Out[7]: True

In [8]: %timeit play_game(row,n,randarr,p)
10 loops, best of 3: 116 ms per loop

In [9]: %timeit play_game_partvect(row,n,randarr,p)
100 loops, best of 3: 14.8 ms per loop

因此,我们看到了大约 7.5x+ 的加速,不错!

关于python - 提高 Python 中 for 循环的性能(可能使用 numpy 或 numba),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33161030/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com