Programación orientada a objetos versus programación basada en vectores

14

Estoy dividido entre el diseño orientado a objetos y el diseño basado en vectores. Me encantan las habilidades, la estructura y la seguridad que los objetos dan a toda la arquitectura. Pero al mismo tiempo, la velocidad es muy importante para mí, y tener variables flotantes simples en una matriz realmente ayuda en lenguajes / bibliotecas basadas en vectores como Matlab o numpy en Python.

Aquí hay un fragmento de código que escribí para ilustrar mi punto

Problema: Agregar números de volatilidad de Tow. Si x e y son dos números de volatilidad, la suma de la volatilidad es (x ^ 2 + y ^ 2) ^ 0.5 (suponiendo cierta condición matemática pero eso no es importante aquí).

Quiero realizar esta operación muy rápido, y al mismo tiempo necesito asegurarme de que las personas no solo agreguen la volatilidad de la manera incorrecta (x + y). Ambos son importantes.

El diseño basado en OO sería algo como esto:

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

(Aparte: para aquellos que son nuevos en Python, __add__es solo una función que anula el +operador)

Digamos que agrego listas de valores de volatilidad

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

(Aparte: una vez más, una serie en Python es una especie de lista con un índice) Ahora quiero agregar los dos:

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

Solo la adición se ejecuta en 3.8 segundos en mi máquina, los resultados que he dado no incluyen el tiempo de inicialización del objeto, solo el código de adición que se ha cronometrado. Si ejecuto lo mismo usando matrices 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

Se ejecuta en 0.03 segundos. ¡Eso es más de 100 veces más rápido!

Como puede ver, la forma OOP me da mucha seguridad de que la gente no agregará Volatility de la manera incorrecta, ¡pero el método vectorial es tan rápido! ¿Hay un diseño en el que pueda obtener ambos? Estoy seguro de que muchos de ustedes se han encontrado con opciones de diseño similares, ¿cómo lo resolvieron?

La elección del idioma aquí es irrelevante. Sé que muchos de ustedes aconsejarían usar C ++ o Java, y el código puede ejecutarse más rápido que los lenguajes basados ​​en vectores de todos modos. Pero ese no es el punto. Necesito usar Python, porque tengo una gran cantidad de bibliotecas que no están disponibles en otros idiomas. Esa es mi restricción. Necesito optimizar dentro de ella.

Y sé que mucha gente sugeriría paralelización, gpgpu, etc. Pero primero quiero maximizar el rendimiento de un solo núcleo, y luego puedo paralelizar ambas versiones de código.

¡Gracias por adelantado!

Ramanuj Lal
fuente
3
Una forma estrechamente relacionada de pensar sobre este problema: ¿Debería usar una estructura de matrices (SoA) o una matriz de estructuras (AoS) para el rendimiento? Con SoA es más fácil de vectorizar y AoS es más amigable con OOP en la mayoría de los idiomas.
Patrick
sí @Patrick, si ves la primera respuesta, creo que Bart dio un ejemplo práctico del punto que estás haciendo. Estoy en lo cierto? Me doy cuenta de que dices la mayoría de los idiomas, entonces ¿hay idiomas donde ambos tienen un rendimiento cercano?
Ramanuj Lal

Respuestas:

9

Como puede ver, la forma OOP me da mucha seguridad de que la gente no agregará Volatility de la manera incorrecta, ¡pero el método vectorial es tan rápido! ¿Hay un diseño en el que pueda obtener ambos? Estoy seguro de que muchos de ustedes se han encontrado con opciones de diseño similares, ¿cómo lo resolvieron?

Diseña objetos más grandes. Un Pixelobjeto no tiene espacio para respirar para un bucle paralelo o transformaciones de imagen de GPU o algo así. Sí Image, siempre que no tenga que atravesar la barrera de un Pixelobjeto pequeño para obtener los datos.


fuente
5

Esta es una de esas áreas donde es imposible dar respuestas definitivas, ya que se trata de una compensación. Como descubrió, ni OO, ni basado en vectores siempre es superior, pero todo depende de cómo se utilizará el software.

Podría intentar combinar lo mejor de ambos y crear un Volatilityobjeto y un VolatilitySeriesobjeto, donde el segundo representa conceptualmente una serie de objetos de volatilidad, pero internamente utiliza un método de almacenamiento que es mucho más adecuado para vectorizar los cálculos (una estructura de matrices) . Luego, solo tiene que educar a sus usuarios sobre el uso VolatilitySeriespreferible Series(Volatility).

Bart van Ingen Schenau
fuente
Gracias Bart, es una buena idea. De hecho, he seguido ese camino en mi diseño actual en partes, donde algunos objetos como los montos monetarios fueron rediseñados de esa manera. Pero pronto me di cuenta de que mi código se convierte en esclavo de esa estructura de datos en particular. Por ejemplo, si tengo un VolatilitySeriescomo usted sugiere, entonces no puedo tener un list, o un tupleo (suponiendo que esté familiarizado con Python) a DataFramede elementos de volatilidad. Eso me molesta, porque entonces mi arquitectura no escala bien y los beneficios se desvanecen después de un tiempo. Y eso es lo que me trae aquí :).
Ramanuj Lal
El otro problema es que nada está deteniendo a nadie para escribir un código como volatilitySeries[0] + 3.0, lo que estará mal. Una vez que extrae los valores VolatilitySeries, puede volverse loco, por lo que la seguridad es de corta duración. En un entorno polimórfico donde las personas no siempre son conscientes de la clase exacta que se utiliza, esto es muy posible. Y ya sabes, solo puedes educar tanto a tus usuarios. Sé que dirás eso, oye, también puedo hacer lo mismo si me escabullo Volatility.value, pero ya sabes, al menos el usuario sabe ahora que está usando un valor especial.
Ramanuj Lal
Algunos también pueden sugerir que anule todas las funciones habituales heredadas de Seriesin VolatilitySeries, pero eso frustra todo el propósito. Entonces, lo que aprendí al seguir ese camino es que tener un VolatilitySeriesobjeto solo funciona a largo plazo si las celdas individuales son de tipo Volatility.
Ramanuj Lal
@RamanujLal: No conozco Python lo suficientemente bien como para determinar si el VolatileSeriesenfoque es viable. Si ya lo probaste y no funcionó, entonces tienes que elegir entre seguridad y velocidad. No podemos ayudarte allí. (a menos que alguien más tenga una respuesta brillante)
Bart van Ingen Schenau