Objektorientierte vs. vektorbasierte Programmierung

14

Ich bin hin- und hergerissen zwischen objektorientiertem und vektorbasiertem Design. Ich liebe die Fähigkeiten, die Struktur und die Sicherheit, die Objekte der gesamten Architektur verleihen. Gleichzeitig ist mir die Geschwindigkeit sehr wichtig, und einfache Float-Variablen in einem Array helfen wirklich bei vektorbasierten Sprachen / Bibliotheken wie Matlab oder Numpy in Python.

Hier ist ein Teil des Codes, den ich geschrieben habe, um meinen Standpunkt zu veranschaulichen

Problem: Hinzufügen von Tow-Volatilitätszahlen. Wenn x und y zwei Flüchtigkeitszahlen sind, ist die Summe der Flüchtigkeit (x ^ 2 + y ^ 2) ^ 0,5 (unter der Annahme bestimmter mathematischer Bedingungen, aber das ist hier nicht wichtig).

Ich möchte diese Operation sehr schnell durchführen und gleichzeitig sicherstellen, dass die Leute die Volatilität nicht einfach falsch addieren (x + y). Beides ist wichtig.

Das OO-basierte Design würde ungefähr so ​​aussehen:

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

(Nebenbei: Für diejenigen, die Python noch nicht kennen, __add__ist dies nur eine Funktion, die den +Operator außer Kraft setzt. )

Nehmen wir an, ich füge zwei Listen mit Volatilitätswerten hinzu

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

(Übrigens: Wieder ist eine Reihe in Python eine Art Liste mit einem Index.) Nun möchte ich die beiden hinzufügen:

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

Nur die Addition läuft in 3,8 Sekunden auf meinem Computer. Die von mir angegebenen Ergebnisse enthalten nicht die Objektinitialisierungszeit, sondern nur den zeitlich festgelegten Additionscode. Wenn ich dasselbe mit Numpy-Arrays ausführe:

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

Es läuft in 0.03 Sekunden. Das ist mehr als 100 mal schneller!

Wie Sie sehen, gibt mir der OOP-Weg eine Menge Sicherheit, dass die Leute Volatility nicht auf die falsche Weise hinzufügen, aber die Vektormethode ist einfach so verrückt, schnell! Gibt es ein Design, in dem ich beides bekommen kann? Ich bin sicher, dass viele von Ihnen ähnliche Designentscheidungen getroffen haben. Wie haben Sie das herausgefunden?

Die Wahl der Sprache ist dabei unerheblich. Ich weiß, dass viele von Ihnen raten würden, C ++ oder Java zu verwenden, und der Code kann sowieso schneller als vektorbasierte Sprachen laufen. Darum geht es aber nicht. Ich muss Python verwenden, da ich eine Vielzahl von Bibliotheken habe, die in anderen Sprachen nicht verfügbar sind. Das ist meine Einschränkung. Ich muss darin optimieren.

Und ich weiß, dass viele Leute Parallelisierung, GPGPU usw. vorschlagen würden. Aber ich möchte zuerst die Single-Core-Leistung maximieren und dann beide Codeversionen parallelisieren.

Danke im Voraus!

Ramanuj Lal
quelle
3
Eine eng verwandte Methode, um über dieses Problem nachzudenken: Sollten Sie eine Array-Struktur (SoA) oder eine Array-Struktur (AoS) für die Leistung verwenden? So lässt sich SoA leichter vektorisieren und AoS ist in den meisten Sprachen OOP-freundlicher.
Patrick
yes @Patrick, wenn Sie die erste Antwort sehen, hat Bart, glaube ich, ein praktisches Beispiel für den Punkt gegeben, den Sie ansprechen. Habe ich recht? Ich stelle fest, dass Sie die meisten Sprachen sprechen. Gibt es also Sprachen, bei denen beide in der Leistung nahe beieinander liegen?
Ramanuj Lal
Algorithmen + Datenstrukturen = Programme von Niklaus Wirth
radarbob

Antworten:

9

Wie Sie sehen, gibt mir der OOP-Weg eine Menge Sicherheit, dass die Leute Volatility nicht auf die falsche Weise hinzufügen, aber die Vektormethode ist einfach so verrückt, schnell! Gibt es ein Design, in dem ich beides bekommen kann? Ich bin sicher, dass viele von Ihnen ähnliche Designentscheidungen getroffen haben. Wie haben Sie das herausgefunden?

Entwerfen Sie größere Objekte. Ein PixelObjekt hat keinen Freiraum für parallelisierte Loop- oder GPU-Bildtransformationen oder ähnliches. Ein Imagenicht , sofern sie nicht durch die Barriere eines Teeny zu gehen haben PixelObjekt an den Daten zu erhalten.


quelle
5

Dies ist einer der Bereiche, in denen es unmöglich ist, endgültige Antworten zu geben, da es sich um einen Kompromiss handelt. Wie Sie herausgefunden haben, ist weder OO noch vektorbasiert immer überlegen, aber alles hängt davon ab, wie die Software verwendet wird.

Sie könnten versuchen, das Beste aus beiden zu kombinieren und sowohl ein VolatilityObjekt als auch ein VolatilitySeriesObjekt zu erstellen , wobei das zweite konzeptionell eine Reihe von Volatility-Objekten darstellt, aber intern eine Speichermethode verwendet, die sich viel besser für die Vektorisierung der Berechnungen eignet (eine Struktur von Arrays). . Dann müssen Sie nur noch Ihre Benutzer darüber informieren, dass die Verwendung VolatilitySeriesviel vorzuziehen ist Series(Volatility).

Bart van Ingen Schenau
quelle
Danke Bart, das ist eine gute Idee. Tatsächlich bin ich in meinem aktuellen Design teilweise so vorgegangen, dass einige Objekte wie Geldbeträge auf diese Weise neu gestaltet wurden. Aber bald wurde mir klar, dass mein Code ein Sklave dieser bestimmten Datenstruktur wird. Zum Beispiel, wenn ich ein habe, VolatilitySerieswie Sie vorschlagen, dann kann ich kein list, oder ein tupleoder (vorausgesetzt, Sie sind mit Python vertraut) DataFramevon Volatilitätselementen haben. Das stört mich, denn dann lässt sich meine Architektur nicht gut skalieren und die Vorteile lassen nach einer Weile nach. Und genau das bringt mich hierher :).
Ramanuj Lal
Das andere Problem ist, dass nichts jemanden daran hindert, einen Code wie diesen zu schreiben volatilitySeries[0] + 3.0, was falsch sein wird. Sobald Sie die Werte von herausgerissen haben VolatilitySeries, können Sie wahnsinnig werden, sodass die Sicherheit nur von kurzer Dauer ist. In einer polymorphen Umgebung, in der die Leute nicht immer wissen, welche Klasse genau verwendet wird, ist dies sehr gut möglich. Und Sie wissen, Sie können Ihre Benutzer nur so gut schulen. Ich weiß, dass Sie das sagen werden, hey, ich kann das auch tun, wenn ich mich winde Volatility.value, aber Sie wissen, zumindest ist dem Benutzer jetzt bewusst, dass er einen speziellen Wert verwendet.
Ramanuj Lal
Einige mögen auch vorschlagen, dass all diese üblichen Funktionen, die von Seriesin geerbt wurden, außer Kraft gesetzt werden VolatilitySeries, aber das macht den ganzen Zweck zunichte. Was ich also aus diesem Weg gelernt habe, ist, dass ein VolatilitySeriesObjekt auf lange Sicht nur dann wirklich funktioniert, wenn die einzelnen Zellen vom Typ sind Volatility.
Ramanuj Lal
@RamanujLal: Ich kenne Python nicht gut genug, um festzustellen, ob der VolatileSeriesAnsatz durchführbar ist. Wenn Sie es bereits ausprobiert haben und es nicht funktioniert hat, haben Sie eine schwierige Wahl zwischen Sicherheit und Geschwindigkeit. Wir können Ihnen dort nicht helfen. (es sei denn, jemand anderes hat eine brillante Antwort)
Bart van Ingen Schenau