Programowanie obiektowe a programowanie wektorowe

14

Jestem rozdarty między projektowaniem obiektowym a projektowaniem wektorowym. Uwielbiam umiejętności, strukturę i bezpieczeństwo, jakie obiekty dają całej architekturze. Ale jednocześnie szybkość jest dla mnie bardzo ważna, a posiadanie prostych zmiennych zmiennoprzecinkowych w tablicy naprawdę pomaga w językach / bibliotekach wektorowych, takich jak Matlab lub numpy w Pythonie.

Oto fragment kodu, który napisałem, aby zilustrować mój punkt widzenia

Problem: Dodanie liczb zmienności Holowania. Jeśli x i y są dwiema liczbami zmienności, suma zmienności wynosi (x ^ 2 + y ^ 2) ^ 0,5 (przy założeniu pewnego warunku matematycznego, ale tutaj nie jest to ważne).

Chcę wykonać tę operację bardzo szybko, a jednocześnie muszę upewnić się, że ludzie nie dodają po prostu zmienności w niewłaściwy sposób (x + y). Oba są ważne.

Projekt oparty na OO wyglądałby mniej więcej tak:

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

(Poza tym: dla tych, którzy są nowicjuszami w Pythonie, __add__jest tylko funkcja, która zastępuje +operatora)

Załóżmy, że dodałem listy holownicze wartości zmienności

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

(Poza tym: znowu, seria w Pythonie jest rodzajem listy z indeksem) Teraz chcę dodać dwa:

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

Po prostu dodawanie działa na mojej maszynie w 3,8 sekundy, podane przeze mnie wyniki w ogóle nie uwzględniają czasu inicjalizacji obiektu, jest to tylko kod czasowy, który został odmierzony. Jeśli uruchomię to samo za pomocą tablic 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

Działa za 0,03 sekundy. To ponad 100 razy szybciej!

Jak widać, metoda OOP daje mi dużo bezpieczeństwa, że ​​ludzie nie będą dodawać Zmienności w niewłaściwy sposób, ale metoda wektorowa jest po prostu szalona szybko! Czy istnieje projekt, w którym mogę uzyskać oba? Jestem pewien, że wielu z was wpadło na podobne wybory projektowe, jak to się udało?

Wybór języka tutaj jest nieistotny. Wiem, że wielu z was radziłoby używać C ++ lub Java, a kod może i tak działać szybciej niż języki oparte na wektorze. Ale nie o to chodzi. Potrzebuję użyć Pythona, ponieważ mam wiele bibliotek niedostępnych w innych językach. To moje ograniczenie. Muszę w nim zoptymalizować.

I wiem, że wiele osób sugerowałoby równoległość, gpgpu itp. Ale najpierw chcę zmaksymalizować wydajność jednego rdzenia, a następnie mogę zrównoleglić obie wersje kodu.

Z góry dziękuję!

Ramanuj Lal
źródło
3
Blisko spokrewniony sposób myślenia o tym problemie: czy powinieneś użyć struktury tablic (SoA) lub tablicy struktur (AoS) do wydajności? Ponieważ SoA jest łatwiejsze do wektoryzacji, a AoS jest bardziej przyjazny dla OOP w większości języków.
Patrick
tak @Patrick, jeśli zobaczysz pierwszą odpowiedź, myślę, że Bart dał praktyczny przykład tego, o czym mówisz. Czy mam rację? Zauważyłem, że mówisz większość języków, więc czy są języki, w których oba są bliskie pod względem wydajności?
Ramanuj Lal
Algorytmy + struktury danych = Programy Niklausa Wirtha
radarbob

Odpowiedzi:

9

Jak widać, metoda OOP daje mi dużo bezpieczeństwa, że ​​ludzie nie będą dodawać Zmienności w niewłaściwy sposób, ale metoda wektorowa jest po prostu szalona szybko! Czy istnieje projekt, w którym mogę uzyskać oba? Jestem pewien, że wielu z was wpadło na podobne wybory projektowe, jak to się udało?

Projektuj większe obiekty. PixelObiekt nie ma miejsca dla oddychania i równoległego pętli lub przekształceń graficznych GPU lub coś podobnego. Tak Image, pod warunkiem, że nie musi przechodzić przez barierę małego Pixelobiektu, aby uzyskać dostęp do danych.


źródło
5

Jest to jeden z tych obszarów, w którym nie można udzielić ostatecznych odpowiedzi, ponieważ dotyczy kompromisu. Jak się dowiedziałeś, ani OO, ani oparte na wektorze nie zawsze są lepsze, ale wszystko zależy od tego, jak oprogramowanie będzie używane.

Możesz spróbować połączyć to, co najlepsze, i stworzyć zarówno Volatilityobiekt, jak i VolatilitySeriesobiekt, gdzie drugi koncepcyjnie reprezentuje serię obiektów zmienności, ale wewnętrznie używa metody przechowywania, która jest znacznie lepiej dostosowana do wektoryzacji obliczeń (struktura tablic) . Następnie musisz tylko poinformować użytkowników, że korzystanie z nich VolatilitySeriesjest o wiele lepsze niż używanie Series(Volatility).

Bart van Ingen Schenau
źródło
Dzięki Bart, to dobry pomysł. W rzeczywistości poszedłem tą drogą w moim obecnym projekcie w częściach, w których niektóre obiekty, takie jak kwoty pieniężne, zostały przeprojektowane w ten sposób. Ale wkrótce zdałem sobie sprawę, że mój kod staje się niewolnikiem tej konkretnej struktury danych. Na przykład, jeśli mam VolatilitySeriesjak sugerujesz, to nie mogę mieć list, tuplelub (zakładając, że znasz Python) a DataFrameelementów zmienności. Niepokoi mnie to, ponieważ wtedy moja architektura nie skaluje się dobrze, a korzyści znikają po pewnym czasie. I to mnie tu sprowadza :).
Ramanuj Lal
Innym problemem jest to, że nic nie powstrzymuje nikogo przed napisaniem takiego kodu volatilitySeries[0] + 3.0, co będzie błędne. Po wykręceniu wartości VolatilitySeriesmożesz wpaść w szał, więc bezpieczeństwo trwa krótko. Jest to bardzo możliwe w środowisku polimorficznym, w którym ludzie nie zawsze zdają sobie sprawę z tego, jaką dokładnie klasę zastosowano. I wiesz, możesz tylko edukować swoich użytkowników. Wiem, że to powiesz, hej, ja też mogę zrobić to samo, jeśli się wykręcę Volatility.value, ale wiesz, przynajmniej użytkownik wie teraz, że używa specjalnej wartości.
Ramanuj Lal
Niektórzy mogą również sugerować, że zastępują wszystkie zwykłe funkcje odziedziczone Seriespo VolatilitySeries, ale to pokonuje cały cel. Tak więc nauczyłem się z tej ścieżki, że posiadanie VolatilitySeriesobiektu naprawdę działa na dłuższą metę tylko wtedy, gdy poszczególne komórki są typu Volatility.
Ramanuj Lal
@RamanujLal: Nie znam wystarczająco dobrze Pythona, aby ustalić, czy VolatileSeriespodejście jest wykonalne. Jeśli już go wypróbowałeś i nie zadziałało, masz trudny wybór między bezpieczeństwem a prędkością. Nie możemy ci tam pomóc. (chyba że ktoś inny ma świetną odpowiedź)
Bart van Ingen Schenau