面向对象与基于矢量的编程

时间:2013-06-04 11:43:51

标签: python performance oop numpy vectorization

我在面向对象和基于矢量的设计之间徘徊。我喜欢物体给整个建筑带来的能力,结构和安全。但与此同时,速度对我来说非常重要,在数组中使用简单的浮点变量确实有助于基于矢量的语言/库(如Matlab或Python中的numpy)。

这是我写的一段代码来说明我的观点

问题:添加Tow波动率数字。如果x和y是两个波动率数,则波动率之和为(x ^ 2 + y ^ 2)^ 0.5(假设某些数学条件,但这里不重要)。

我想非常快地执行此操作,同时我需要确保人们不仅仅以错误的方式添加波动率(x + y)。这两个都很重要。

基于OO的设计将是这样的:

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的人来说,添加只是一个覆盖'+'运算符的函数)

假设我添加了两个波动率值列表

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

(旁白:再一次,Python中的系列是一个带索引的列表) 现在我想添加两个:

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

只是在我的机器上以3.8秒的速度运行,我给出的结果根本不包括对象初始化时间,它只是已定时的添加代码。如果我使用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

它在0.03秒内运行。这快了100多倍!

正如你所看到的,OOP方式给了我很多安全性,人们不会以错误的方式添加Volatility,但是矢量方法很快就疯了!我有两种设计吗?我相信很多人都遇到了类似的设计选择,你是如何解决的?

这里的语言选择并不重要。我知道很多人会建议使用C ++或Java,而且代码可能比基于矢量的语言运行得更快。但那不是重点。我需要使用Python,因为我有许多其他语言不可用的库。那是我的约束。我需要在其中进行优化。

我知道,很多人会建议并行化,gpgpu等。但我想首先最大化单核性能,然后我可以并行化两个版本的代码。

提前致谢!

2 个答案:

答案 0 :(得分:3)

这些案例的可能解决方案是退后一步:您真的需要或想要将单个值表示为对象吗?如果您的对象是Volatile的整个数组(或系列),该怎么办?你可以获得两全其美的效果。

即使对单个Volatile对象有一些用处,你也可以实现flyweight pattern,其中Volatile对象只是数组中位置的包装,所有方法都会对阵列进行操作。

答案 1 :(得分:3)

您可以在矢量化操作的顶部进行抽象,封装,代码重用等(通过OOP或其他方式)。您只需选择正确的粒度:您的单元抽象(对象)应该是多个值,以匹配矢量化实现。据我所知,没有复数波动,但对于您的具体用例,可能还有一个更合适的术语(您的波动率值意味着什么?)。

是的,这意味着性能问题会影响抽象和API(但无论如何都会发生这种情况)。不,影响不是(必然)减少抽象,甚至是漏洞抽象。它只是改变了抽象的形状。事实上,如果它只能同时对所有值进行操作,这甚至可能为其余代码提供更方便的API。