在Python中最快的迭代方法

5 投票
2 回答
7218 浏览
提问于 2025-04-17 08:13

我之前从来没有遇到过这个问题,但现在我需要使用一些大量的顶点,这些顶点需要通过PyOpenGL进行缓冲,结果发现Python的迭代速度太慢了。情况是这样的:我有一个包含3D点的数组vertices,在每一步我都需要为每个顶点计算一个4D的颜色数组。到目前为止,我的做法是:

upper_border = len(self.vertices) / 3
#Only generate at first step, otherwise use old one and replace values
if self.color_array is None:
     self.color_array = numpy.empty(4 * upper_border)  

for i in range(upper_border):
     #Obtain a color between a start->end color
     diff_activity = (activity[i] - self.min) / abs_diff  
     clr_idx = i * 4
     self.color_array[clr_idx] = start_colors[0] + diff_activity * end_colors[0]
     self.color_array[clr_idx + 1] = start_colors[1] + diff_activity * end_colors[1]
     self.color_array[clr_idx + 2] = start_colors[2] + diff_activity * end_colors[2]
     self.color_array[clr_idx + 3] = 1

现在我觉得我已经没有其他办法可以消除循环中每一步的操作了,但我猜一定有更好的方法来提高这个循环的性能。我这么说是因为在JavaScript中,做同样的计算可以达到9帧每秒,而在Python中我只能得到2-3帧每秒。

祝好,
博格丹

2 个回答

6
  1. 首先:用 cProfile 来分析你的代码性能。
  2. 你应该用 xrange 代替 range。
  3. 在每次循环中不要重复调用 self.color_array 四次,试着在循环之前创建一个局部变量,然后在循环中使用它:local_array = self.color_array
  4. 尝试预先计算 start_colors[N]end_colors[N]start_color_0 = start_colors[0]
  5. 尝试使用 list.extend() 来减少循环中的代码行数:

    local_array.extend([
       start_colors_0 + diff_activity * end_colors_0,
       start_colors_1 + diff_activity * end_colors_1,
       start_colors_2 + diff_activity * end_colors_2,
       1
    ])
    
13

为了让这段代码运行得更快,你需要“向量化”它:也就是用隐式循环替代所有显式的Python循环,利用NumPy的广播规则。我可以试着给你一个向量化版本的循环:

if self.color_array is None:
     self.color_array = numpy.empty((len(activity), 4))
diff_activity = (activity - self.min) / abs_diff
self.color_array[:, :3] = (start_colors + 
                           diff_activity[:, numpy.newaxis] + 
                           end_colors)
self.color_array[:, 3] = 1

请注意,我做了很多猜测,因为我不太清楚你的所有变量是什么,以及这段代码的具体功能,所以我不能保证这段代码能正常运行。我把color_array变成了一个二维数组,因为这样看起来更合适。这可能需要你在代码的其他部分做一些调整(或者你需要再把数组变回一维)。

我假设self.minabs_diff是标量(就是单个数值),而其他的名字则指的是形状如下的NumPy数组:

activity.shape == (len(vertices) // 3,)
start_colors.shape == (3,)
end_colors.shape == (3,)

看起来vertices是一个一维数组,但它应该是一个二维数组。

撰写回答