Объектно-ориентированное и векторное программирование

14

Я разрываюсь между объектно-ориентированным и векторным дизайном. Я люблю способности, структуру и безопасность, которые объекты дают всей архитектуре. Но в то же время скорость очень важна для меня, и наличие простых переменных с плавающей точкой в ​​массиве действительно помогает в векторных языках / библиотеках, таких как Matlab или numpy в Python.

Вот фрагмент кода, который я написал, чтобы проиллюстрировать мою точку зрения.

Проблема: добавление значений волатильности буксировки. Если x и y - два числа волатильности, сумма волатильности равна (x ^ 2 + y ^ 2) ^ 0.5 (при условии определенного математического условия, но это здесь не важно).

Я хочу выполнить эту операцию очень быстро, и в то же время мне нужно убедиться, что люди не просто неправильно добавляют волатильность (х + у). Оба из них важны.

ОО-дизайн будет выглядеть примерно так:

from datetime import datetime 
from pandas import *

class Volatility:
    def __init__(self,value):
       self.value = value

    def __str__(self):
       return "Volatility: "+ str(self.value)

    def __add__(self,other):
        return Volatility(pow(self.value*self.value + other.value*other.value, 0.5))

(В сторону: для тех, кто плохо знаком с Python, __add__это просто функция, которая переопределяет +оператор)

Допустим, я добавляю списки значений волатильности

n = 1000000
vs1 = Series(map(lambda x: Volatility(2*x-1.0), range(0,n)))
vs2 = Series(map(lambda x: Volatility(2*x+1.0), range(0,n))) 

(Помимо: опять же, Series в Python - это своего рода список с индексом) Теперь я хочу добавить два:

t1 = datetime.now()
vs3 = vs1 + vs2
t2 = datetime.now()
print t2-t1

Просто добавление выполняется на моей машине за 3,8 секунды, результаты, которые я дал, вообще не включают время инициализации объекта, а только код добавления, который был рассчитан по времени. Если я запускаю то же самое, используя массивы:

nv1 = Series(map(lambda x: 2.0*x-1.0, range(0,n)))
nv2 = Series(map(lambda x: 2.0*x+1.0, range(0,n)))

t3 = datetime.now()
nv3 = numpy.sqrt((nv1*nv1+nv2*nv2))
t4 = datetime.now()
print t4-t3

Работает за 0,03 секунды. Это более чем в 100 раз быстрее!

Как вы можете видеть, способ ООП дает мне большую безопасность, так что люди не будут добавлять Волатильность неправильно, но векторный метод просто безумно быстр! Есть ли дизайн, в котором я могу получить оба? Я уверен, что многие из вас сталкивались с подобным выбором дизайна, как вы с этим справились?

Выбор языка здесь не имеет значения. Я знаю, что многие из вас посоветовали бы использовать C ++ или Java, и в любом случае код может работать быстрее, чем векторные языки. Но дело не в этом. Мне нужно использовать Python, потому что у меня есть множество библиотек, недоступных на других языках. Это мое ограничение. Мне нужно оптимизировать в нем.

И я знаю, что многие люди предложили бы распараллеливание, gpgpu и т. Д. Но я хочу сначала максимизировать производительность одного ядра, а затем я могу распараллелить обе версии кода.

Заранее спасибо!

Раманудж Лал
источник
3
Тесно связанный способ думать об этой проблеме: следует ли вам использовать структуру массивов (SoA) или массив структур (AoS) для производительности? С SoA стало проще векторизоваться, а AOS стал более дружественным к ООП на большинстве языков.
Патрик
да @ Патрик, если вы видите первый ответ, я думаю, что Барт привел практический пример того, что вы делаете. Я прав? Я заметил, что вы говорите большинстве языков, так есть ли языки, в которых оба близки по производительности?
Раманудж Лал

Ответы:

9

Как вы можете видеть, способ ООП дает мне большую безопасность, так что люди не будут добавлять Волатильность неправильно, но векторный метод просто безумно быстр! Есть ли дизайн, в котором я могу получить оба? Я уверен, что многие из вас сталкивались с подобным выбором дизайна, как вы с этим справились?

Дизайн больших объектов. У Pixelобъекта нет передышки для параллельного преобразования изображения или преобразования графического процессора или чего-то подобного. Действительно Image, при условии, что он не должен проходить через барьер крошечного Pixelобъекта, чтобы получить данные.


источник
5

Это одна из тех областей, где невозможно дать однозначные ответы, потому что это касается компромисса. Как вы узнали, ни OO, ни Vector-based не всегда лучше, но все зависит от того, как будет использоваться программное обеспечение.

Вы можете попытаться объединить лучшее из обоих и создать как Volatilityобъект, так иVolatilitySeries объект, где второй концептуально представляет объекты серии изменчивости, но внутренне использует метод хранения, который гораздо лучше подходит для векторизации вычислений (структура массивов). , Тогда вам просто нужно научить своих пользователей, что использование VolatilitySeriesнамного предпочтительнее Series(Volatility).

Барт ван Инген Шенау
источник
Спасибо, Барт, это хорошая идея. На самом деле я пошел по этому пути в моем текущем проекте по частям, где некоторые объекты, такие как денежные суммы, были переработаны таким образом. Но вскоре я понял, что мой код становится рабом этой конкретной структуры данных. Например, если у меня есть, VolatilitySeriesкак вы предлагаете, то я не могу иметь list, tupleили (или (если вы знакомы с Python)) DataFrameэлементы волатильности. Это беспокоит меня, потому что тогда моя архитектура плохо масштабируется, и через некоторое время преимущества исчезают. И это то, что приводит меня сюда :).
Раманудж Лал
Другая проблема заключается в том, что ничто не мешает кому-то написать подобный код volatilitySeries[0] + 3.0, что будет неправильно. Как только вы извлекаете значения VolatilitySeries, вы можете сходить с ума, поэтому безопасность недолговечна. В полиморфной среде, где люди не всегда знают, какой именно класс используется, это очень возможно. И вы знаете, вы можете только обучить своих пользователей так много. Я знаю, вы скажете, что, эй, я могу сделать то же самое, если выверну Volatility.value, но вы знаете, по крайней мере, пользователь теперь знает, что он использует специальное значение.
Раманудж Лал
Некоторые могут также предложить переопределить все эти обычные функции, унаследованные от Seriesin VolatilitySeries, но это наносит ущерб всей цели. Итак, что я узнал из этого пути, так это то, что наличие VolatilitySeriesобъекта действительно работает в долгосрочной перспективе, только если отдельные ячейки имеют тип Volatility.
Раманудж Лал
@RamanujLal: Я не знаю Python достаточно хорошо, чтобы определить, является ли VolatileSeriesподход жизнеспособным. Если вы уже попробовали это, и это не сработало, тогда вам приходится выбирать между безопасностью и скоростью. Мы не можем помочь вам там. (если у кого-то еще нет блестящего ответа)
Барт ван Инген Шенау