самый быстрый способ перебора в питоне - PullRequest
4 голосов
/ 12 декабря 2011

До сих пор мне никогда не приходилось сталкиваться с этой проблемой, но теперь мне нужно использовать большое количество вершин, которые должны быть буферизованы PyOpenGL, и кажется, что итерация python является узким местом.Здесь ситуация.У меня есть массив трехмерных точек vertices, и на каждом шаге я должен вычислять массив четырехмерных цветов для каждой вершины.Мой подход на данный момент таков:

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, например, то же исчисление выдает 9FPS, а в Python я получаю только 2-3 FPS.

С уважением, Богдан

Ответы [ 2 ]

12 голосов
/ 12 декабря 2011

Чтобы сделать этот код быстрым, вам нужно «векторизовать» его: заменить все явные петли Python неявными петлями, используя правила boradcasting 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.min и abs_diff являются скалярами, а все другие имена ссылаются на массивы NumPy следующих форм:

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

Выглядит так, как будто vertices является одномерным массивом и должен быть двумерным массивом.

6 голосов
/ 12 декабря 2011
  1. Прежде всего: профиль вашего кода с cProfile
  2. Вы должны использовать xrange вместо диапазона
  3. Вам следует избегать вызова self.color_array 4 раза в каждом цикле, попытаться создать локальную переменную перед циклом и использовать ее в цикле: 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
    ])
    
...