我在面向对象和基于向量的设计之间摇摆不定。我喜欢物体赋予整个建筑的能力、结构和安全性。但与此同时,速度对我来说非常重要,在数组中使用简单的浮点变量确实有助于基于向量的语言/库,如Matlab或Python中的numpy。
这是我写的一段代码来说明我的观点
问题:添加两个波动数。如果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的人,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)))
(旁白:再说一次,Python中的Series是一种带有索引的列表)现在我想添加两个:
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多倍!
正如你所看到的,面向对象的方法给了我很多的安全性,人们不会以错误的方式添加波动性,但是向量方法是如此的快!有没有一种设计可以让我两者兼得?我相信你们很多人都遇到过类似的设计选择,你是如何解决的?
这里语言的选择无关紧要。我知道你们中的许多人会建议使用c++或Java,无论如何,代码可能比基于向量的语言运行得更快。但这不是重点。我需要使用Python,因为我有许多在其他语言中不可用的库。这是约束条件。我需要在里面优化
我知道,很多人会建议并行化,gpgpu等。但是我想首先最大化单核性能,然后我可以并行处理两个版本的代码。提前感谢!
对于这些情况,一个可能的解决方案是后退一步:您真的需要或想要将单个值表示为对象吗?如果您的对象是Volatile
的整个数组(或系列)怎么办?你可以两全其美。
即使单个Volatile
对象有一些用途,你也可以实现一个轻量级模式,其中Volatile
对象将仅仅是数组中位置的包装,所有方法都将对数组进行操作。
您可以在向量化操作之上进行抽象、封装、代码重用等(通过OOP或其他方式)。您只需要选择正确的粒度:您的单元抽象(对象)应该是多个值,以匹配向量化实现。据我所知,波动率没有复数,但对于您的特定用例,可能有一个更合适的术语(您的波动率值的集合意味着什么?)。
是的,这意味着性能问题会影响抽象和API(但无论如何,这种情况一直在发生)。不,这种影响(不一定)不是抽象的减少,甚至不是抽象的泄漏。它只是改变了抽象的形式。事实上,如果一次只对所有值进行操作,这甚至可以为其余代码提供更方便的API。