Список объектов или параллельные массивы свойств? - PullRequest
3 голосов
/ 27 апреля 2010

Вопрос, по сути, в следующем: что было бы более предпочтительным, как с точки зрения производительности, так и с точки зрения дизайна - иметь список объектов класса Python или иметь несколько списков числовых свойств?

Я пишу какую-то научную симуляцию, в которой задействована довольно большая система взаимодействующих частиц. Для простоты, скажем, у нас есть набор шариков, подпрыгивающих внутри коробки, поэтому у каждого шарика есть ряд числовых свойств, таких как координаты x-y-z, диаметр, масса, вектор скорости и так далее. Как лучше хранить систему? Два основных варианта, о которых я могу подумать:

создать класс "Ball" с этими свойствами и некоторыми методами, а затем сохранить список объектов класса, e. г. [b1, b2, b3, ... bn, ...], где для каждого bn мы можем получить доступ к bn.x, bn.y, bn.mass и т. д .;

для создания массива чисел для каждого свойства, затем для каждого i-го «шара» мы можем получить доступ к его координате «x» как xs [i], координате «y» как ys [i], «mass» как массы [я] и т. д .;

Мне кажется, что первый вариант представляет собой лучший дизайн. Второй вариант выглядит несколько уродливее, но может быть лучше с точки зрения производительности, и было бы проще использовать его с numpy и scipy, который я стараюсь использовать как можно больше.

Я до сих пор не уверен, будет ли Python достаточно быстрым, поэтому может потребоваться переписать его на C ++ или еще что-нибудь после первоначального создания прототипа на Python. Будет ли выбор представления данных другим для C / C ++? Как насчет гибридного подхода, например Python с расширением C ++?

Обновление: Я никогда не ожидал какого-либо увеличения производительности от параллельных массивов как таковых, но в смешанной среде, такой как Python + Numpy (или любой другой SlowScriptingLanguage + FastNativeLibrary), их использование может (или не может?) Позволить вам перенесите больше работы из своего медленного скриптового кода в быструю нативную библиотеку.

Ответы [ 4 ]

3 голосов
/ 27 апреля 2010

Наличие объекта для каждого шара в этом примере, безусловно, лучший дизайн.Параллельные массивы действительно обходные пути для языков, которые не поддерживают надлежащие объекты.Я бы не стал использовать их на языке с возможностями ОО, если только это не крошечный случай, который вписывается в функцию (а может быть, даже тогда) или если у меня закончились все остальные варианты оптимизации, а профилировщик показывает, что доступ к свойствувиновник.Это в два раза больше относится к Python, чем к C ++, так как первый придает большое значение удобочитаемости и элегантности.

2 голосов
/ 28 апреля 2010

Я согласен с тем, что параллельные массивы почти всегда являются плохой идеей, но не забывайте, что вы можете использовать представления в массивном массиве при настройке, хотя ... (Да, я знаю, это эффективно используя параллельные массивы, но я думаю, что это лучший вариант в этом случае ...)

Это замечательно, если вы знаете заранее количество «шариков», которые вы собираетесь создать, поскольку вы можете выделить массив для координат и сохранить представление в этом массиве для каждого объекта шарика.

Вы должны быть немного осторожны, чтобы выполнять операции на месте в массиве координат, но это делает обновление координат для множества "шаров" намного, намного, намного быстрее.

Например ...

import numpy as np

class Ball(object):
    def __init__(self, coords):
        self.coords = coords

    def _set_coord(self, i, value):
        self.coords[i] = value
    x = property(lambda self: self.coords[0],
            lambda self, value: self._set_coord(0, value))
    y = property(lambda self: self.coords[1],
            lambda self, value: self._set_coord(1, value))

    def move(self, dx, dy):
        self.x += dx
        self.y += dy

def main():
    n_balls = 10
    n_dims = 2
    coords = np.zeros((n_balls, n_dims))
    balls = [Ball(coords[i,:]) for i in range(n_balls)]

    # Just to illustrate that that the coords are updating
    ball = balls[0]

    # Random walk by updating coords array
    print 'Moving all the balls randomly by updating coords'
    for step in xrange(5):
        # Add a random value to all coordinates
        coords += 0.5 - np.random.random((n_balls, n_dims))

        # Display the coords for a particular ball and the 
        # corresponding row of the coords array
        print '    Value of ball.x, ball.y:', ball.x, ball.y
        print '    Value of coords[0,:]:', coords[0,:]

    # Move an individual ball object
    print 'Moving a ball individually through Ball.move()'
    ball.move(0.5, 0.5)
    print '    Value of ball.x, ball.y:', ball.x, ball.y
    print '    Value of coords[0,:]:', coords[0,:]

main()

Просто чтобы проиллюстрировать, это выводит что-то вроде:

Moving all the balls randomly by updating coords
    Value of ball.x, ball.y: -0.125713650677 0.301692195466
    Value of coords[0,:]: [-0.12571365  0.3016922 ]
    Value of ball.x, ball.y: -0.304516863495 -0.0447543559805
    Value of coords[0,:]: [-0.30451686 -0.04475436]
    Value of ball.x, ball.y: -0.171589457954 0.334844443821
    Value of coords[0,:]: [-0.17158946  0.33484444]
    Value of ball.x, ball.y: -0.0452864552743 -0.0297552313656
    Value of coords[0,:]: [-0.04528646 -0.02975523]
    Value of ball.x, ball.y: -0.163829876915 0.0153203173857
    Value of coords[0,:]: [-0.16382988  0.01532032]
Moving a ball individually through Ball.move()
    Value of ball.x, ball.y: 0.336170123085 0.515320317386
    Value of coords[0,:]: [ 0.33617012  0.51532032]

Преимущество здесь в том, что обновление одного массива numpy будет намного, намного быстрее, чем итерация по всем вашим объектам шара, но вы сохраните более объектно-ориентированный подход.

Только мои мысли об этом, во всяком случае ..

РЕДАКТИРОВАТЬ: Чтобы дать некоторое представление о разнице в скорости, с 1 000 000 шаров:

In [104]: %timeit coords[:,0] += 1.0
100 loops, best of 3: 11.8 ms per loop

In [105]: %timeit [item.x + 1.0 for item in balls]
1 loops, best of 3: 1.69 s per loop

Таким образом, обновление координат напрямую с использованием numpy происходит примерно на 2 порядка быстрее при использовании большого количества шаров. (при использовании 10 шаров разница меньше, чем в примере, примерно в 2 раза, а не в 150 раз)

1 голос
/ 28 апреля 2010

Я думаю, это зависит от того, что вы собираетесь с ними делать, и от того, как часто вы будете работать (все атрибуты одной частицы) против (один атрибут всех частиц). Первый лучше подходит для объектного подхода; последний лучше подходит для массива.

Пару лет назад я столкнулся с аналогичной проблемой (хотя и в другой области). Проект был деприоритизирован до того, как я фактически реализовал этот этап, но я склонялся к гибридному подходу, где в дополнение к классу Ball у меня был класс Ensemble. Ансамбль не будет списком или другим простым контейнером шаров, но будет иметь свои собственные атрибуты (которые будут массивами) и свои собственные методы. Будет ли ансамбль создан из шаров или шары из ансамбля, зависит от того, как вы собираетесь их построить.

Один из моих коллег спорил о решении, в котором основным объектом был бы ансамбль, который мог бы содержать только один шар, так что никакой вызывающий код никогда не должен был бы знать, работали ли вы только на одном шаре (вы когда-либо делали это для вашего приложения?) или по многим.

0 голосов
/ 27 апреля 2010

Будут ли у вас какие-либо силы между шарами (жесткая сфера / столкновение, гравитация, электромагнит)? Я так и думаю. Будет ли у вас достаточно большое количество шаров, чтобы использовать идеи Барнса-Хата идеи? Если это так, то вам определенно следует использовать идею класса Ball, чтобы вы могли легко хранить их в октреях или в другом месте в том же духе. Кроме того, использование моделирования Барнса-Хата сократит сложность моделирования до O (N log N) от O (N ^ 2).

На самом деле, если у вас нет сил между шарами или вы не используете много шаров, вам не нужно возможное увеличение скорости от использования параллельных массивов, и вам следует придерживаться идеи класса Ball для этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...