Programação orientada a objeto vs baseada em vetor

14

Estou dividido entre o design orientado a objetos e o vetor. Adoro as habilidades, a estrutura e a segurança que os objetos dão a toda a arquitetura. Mas, ao mesmo tempo, a velocidade é muito importante para mim, e ter variáveis ​​flutuantes simples em uma matriz realmente ajuda em bibliotecas / linguagens baseadas em vetores como Matlab ou numpy em Python.

Aqui está um pedaço de código que escrevi para ilustrar meu ponto

Problema: Adicionando números de volatilidade do Tow. Se xey são dois números de volatilidade, a soma da volatilidade é (x ^ 2 + y ^ 2) ^ 0,5 (assumindo certas condições matemáticas, mas isso não é importante aqui).

Quero executar essa operação muito rapidamente e, ao mesmo tempo, preciso garantir que as pessoas não adicionem apenas a volatilidade da maneira errada (x + y). Ambos são importantes.

O design baseado em OO seria algo como isto:

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))

(Além disso: para quem é novo no Python, __add__é apenas uma função que substitui o +operador)

Digamos que eu adicione duas listas de valores de volatilidade

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))) 

(Além disso: Novamente, uma série em Python é uma espécie de lista com um índice) Agora, quero adicionar as duas:

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

Apenas a adição é executada em 3,8 segundos na minha máquina, os resultados que eu forneci não incluem o tempo de inicialização do objeto, é apenas o código de adição que foi cronometrado. Se eu executar a mesma coisa usando matrizes numpy:

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

É executado em 0,03 segundos. Isso é mais de 100 vezes mais rápido!

Como você pode ver, o modo POO me dá muita segurança para que as pessoas não adicionem Volatilidade da maneira errada, mas o método vetorial é tão rápido demais! Existe um design no qual eu possa obter os dois? Tenho certeza de que muitos de vocês já escolheram opções de design semelhantes. Como vocês resolveram isso?

A escolha do idioma aqui é imaterial. Sei que muitos de vocês recomendariam o uso de C ++ ou Java, e o código pode ser executado mais rapidamente do que as linguagens baseadas em vetores. Mas esse não é o ponto. Preciso usar o Python, porque tenho várias bibliotecas não disponíveis em outros idiomas. Essa é a minha restrição. Eu preciso otimizar dentro dele.

E eu sei que muitas pessoas sugeririam paralelização, gpgpu etc. Mas quero maximizar primeiro o desempenho de núcleo único e, em seguida, paralelizar as duas versões do código.

Desde já, obrigado!

Ramanuj Lal
fonte
3
Uma maneira intimamente relacionada a pensar sobre esse problema: você deve usar uma estrutura de matrizes (SoA) ou uma matriz de estruturas (AoS) para desempenho? Com SoA sendo mais fácil de vetorizar e AoS sendo mais amigável ao OOP na maioria dos idiomas.
Patrick
@ Patrick, se você vir a primeira resposta, acho que Bart deu um exemplo prático do que você está dizendo. Estou certo? Percebo que você diz a maioria dos idiomas, então existem idiomas em que ambos têm desempenho próximo?
Ramanuj Lal

Respostas:

9

Como você pode ver, o modo POO me dá muita segurança para que as pessoas não adicionem Volatilidade da maneira errada, mas o método vetorial é tão rápido demais! Existe um design no qual eu possa obter os dois? Tenho certeza de que muitos de vocês já escolheram opções de design semelhantes. Como vocês resolveram isso?

Projete objetos maiores. Um Pixelobjeto não tem espaço para respirar para um loop paralelo ou transformações de imagem da GPU ou algo assim. E Imagedesde que ele não precise atravessar a barreira de um Pixelobjeto pequenino para acessar os dados.


fonte
5

Essa é uma daquelas áreas em que é impossível dar respostas definitivas, porque se trata de uma troca. Como você descobriu, nem o OO, nem o vetor são sempre superiores, mas tudo depende de como o software será usado.

Você pode tentar combinar o melhor de ambos e criar um Volatilityobjeto e um VolatilitySeriesobjeto, onde o segundo representa conceitualmente objetos da Série of Volatility, mas usa internamente um método de armazenamento que é muito mais adequado para vetorizar os cálculos (uma estrutura de matrizes) . Então você só precisa educar seus usuários que o uso VolatilitySeriesé muito preferível Series(Volatility).

Bart van Ingen Schenau
fonte
Obrigado Bart, é uma boa ideia. Na verdade, eu segui esse caminho em meu design atual em partes, onde alguns objetos como valores monetários foram redesenhados dessa maneira. Mas logo percebi que meu código se torna escravo dessa estrutura de dados específica. Por exemplo, se eu tenho um VolatilitySeriescomo você sugere, não posso ter um list, ou um tupleou (supondo que você esteja familiarizado com o Python) um DataFramedos itens de volatilidade. Isso me incomoda, porque minha arquitetura não se adapta bem e os benefícios desaparecem depois de um tempo. E é isso que me traz aqui :).
Ramanuj Lal
A outra questão é que nada está impedindo alguém de escrever um código como volatilitySeries[0] + 3.0, o que estará errado. Depois de contornar os valores VolatilitySeries, você pode ficar furioso, para que a segurança tenha vida curta. Em um ambiente polimórfico em que as pessoas nem sempre estão cientes da classe exata que está sendo usada, isso é altamente possível. E você sabe, você só pode educar muito seus usuários. Eu sei que você dirá isso, ei, também posso fazer o mesmo se me esquivar Volatility.value, mas você sabe, pelo menos o usuário está ciente agora que ele está usando um valor especial.
Ramanuj Lal
Alguns também podem sugerir que anulem todas as funções usuais herdadas de Seriesin VolatilitySeries, mas isso derrota todo o propósito. Então, o que aprendi ao seguir esse caminho é que ter um VolatilitySeriesobjeto só realmente funciona a longo prazo se as células individuais forem do tipo Volatility.
Ramanuj Lal
@RamanujLal: Não conheço python o suficiente para determinar se a VolatileSeriesabordagem é viável. Se você já experimentou e não funcionou, terá uma escolha difícil a fazer entre segurança e velocidade. Não podemos ajudá-lo lá. (a menos que alguém tem uma resposta brilhante)
Bart van Ingen Schenau