Röntgenoptik-Projekt
@Gigaz: Falls das tatsächlich funktionieren sollte würde ich behaupten das ist Zufall. Und mit funktionieren meine ich hier nicht dass das kompiliert, sondern das man über den Umweg eines void-Zeigers tatsächlich eine Python Erweiterungsklasse die einen Pointer auf ein `_MyClass`-C++-Objekt hält in solch ein Objekt ”casten” kann.
``input[0].ptr`` hat nicht kompiliert weil Cython an der Stelle nicht genug Informationen über den Typ von ``input[0]`` hatte. Laut Funktionssignatur ist `input` ein Numpy-Array mit Python-Objekten, Du musst an der Stelle aber wissen/sicherstellen/Cython wissen lassen, dass es sich um `Myclass`-Python-Objekte handelt. Denn *dann* hat Cython auch genug Typinformation, dass das `ptr` Attribut ein Zeiger vom C++Typ `_MyClass` ist und kein beliebiges Python-Objekt. Die Funktion müsste also so lauten:
Code: Alles auswählen
def Take_An_Array_Of_Myclass(np.ndarray[object] input):
cdef int L
L = Take_A_Class_Pointer((<Myclass?> input[0]).ptr)
return L
Ich mache sowas mit Swig. Swig ermöglicht mir im Python-Code auf meine C++ Klassen zuzugreifen, als ob es Python-Klassen wären.Gigaz hat geschrieben:Die Algorithmen existieren in C alle, wir haben sie selbst geschrieben und optimiert. Die Problemstellung gibt es leider nicht her dass man mit mathematisch optimierten Algorithmen was rausholt. Man braucht eine Programmiersprache die für möglichst schnelle Berechnungen gedacht ist. Also Fortran oder C. Aber wir hätten eben auch gerne die Python-Funktionalität drumrum.
Hallo,
schonmal daran gedacht, das Ganze auf der GPU zu rechnen?
Viele Tausend Datenpunkte mit ihrerseits vielen Hundert kleinen Matrixmultiplikationen hört sich nach guter Parallelisierbarkeit an.
Ich bin vor einiger Zeit auf eine Bibliothek namens Arrayfire (C/C++ und Python-Wrapper) gestoßen und es ist genaugenommen kein Mehraufwand im Vergleich zum Rechnen auf der CPU.
Ich glaube ich würde für jeden Datenpunkt einen parallelen Thread spawnen und dann die matmuls direkt rechnen oder auch wieder Threads dafür spawnen.
Schaut's euch mal an, es gibt jede Menge implementierte Funktionen, läuft auf CPU, CUDA und OpenCL und es ist open source.
http://www.arrayfire.com
https://github.com/arrayfire/arrayfire-python
schonmal daran gedacht, das Ganze auf der GPU zu rechnen?
Viele Tausend Datenpunkte mit ihrerseits vielen Hundert kleinen Matrixmultiplikationen hört sich nach guter Parallelisierbarkeit an.
Ich bin vor einiger Zeit auf eine Bibliothek namens Arrayfire (C/C++ und Python-Wrapper) gestoßen und es ist genaugenommen kein Mehraufwand im Vergleich zum Rechnen auf der CPU.
Ich glaube ich würde für jeden Datenpunkt einen parallelen Thread spawnen und dann die matmuls direkt rechnen oder auch wieder Threads dafür spawnen.
Schaut's euch mal an, es gibt jede Menge implementierte Funktionen, läuft auf CPU, CUDA und OpenCL und es ist open source.
http://www.arrayfire.com
https://github.com/arrayfire/arrayfire-python
Danke für den Tip, ich werde das gleich mal lesen.Diggum hat geschrieben:Hallo,
schonmal daran gedacht, das Ganze auf der GPU zu rechnen?
Viele Tausend Datenpunkte mit ihrerseits vielen Hundert kleinen Matrixmultiplikationen hört sich nach guter Parallelisierbarkeit an.
Ich bin vor einiger Zeit auf eine Bibliothek namens Arrayfire (C/C++ und Python-Wrapper) gestoßen und es ist genaugenommen kein Mehraufwand im Vergleich zum Rechnen auf der CPU.
Ich glaube ich würde für jeden Datenpunkt einen parallelen Thread spawnen und dann die matmuls direkt rechnen oder auch wieder Threads dafür spawnen.
Schaut's euch mal an, es gibt jede Menge implementierte Funktionen, läuft auf CPU, CUDA und OpenCL und es ist open source.
http://www.arrayfire.com
https://github.com/arrayfire/arrayfire-python
Wie's der Zufall so will bin ich nämlich grade dabei Parallelisierung des Pakets zu ermöglichen. Eigentlich würde ich das lieber auf der Python-Ebene lösen, aber wenn es sinnvoll ist auch kurze Rechnungen zu parallelisieren, dann sollte ich mir das vielleicht anders überlegen.
Bei meiner jetztigen Variante der Parallelisierung bin ich auf ein seltsames Problem gestoßen. Offenbar werden einige Inputs nach dem Threaden nicht mehr erkann, vielleicht weiß jemand Rat bei dem Problem. Hier ist ein Minimalbeispiel:
Pyparr.pyx
Code: Alles auswählen
import cython
cdef class Myclass:
cdef int dummy
def __init__(self, i):
self.dummy=i
def getdummy(self):
return self.dummy
def pyfunc(a):
return a.getdummy()
Code: Alles auswählen
from distutils.core import setup, Extension
from Cython.Build import cythonize
setup(ext_modules = cythonize(Extension(
"Pyparr", # the extension name
sources=["Pyparr.pyx"],
)))
Code: Alles auswählen
import Pyparr as Pyp
from joblib import Parallel, delayed
if __name__ == '__main__':
a=[]
a=a+[Pyp.Myclass(4)]
a=a+[Pyp.Myclass(5)]
a=a+[Pyp.Myclass(6)]
a=a+[Pyp.Myclass(7)]
n=1
out=Parallel(n_jobs=n)(delayed(Pyp.pyfunc)(a[i]) for i in range(4))
print out
a=[]
a=a+[Pyp.Myclass(4)]
a=a+[Pyp.Myclass(5)]
a=a+[Pyp.Myclass(6)]
a=a+[Pyp.Myclass(7)]
n=4
out=Parallel(n_jobs=n)(delayed(Pyp.pyfunc)(a[i]) for i in range(4))
print out
[4,5,6,7]
[0,0,0,0]
Die Ausgabe hängt also auf seltsame Art von der Anzahl der Jobs ab, die joblib Parallel verteilt.

Mit einer nativen Python-Klasse anstatt einer kompilierten Cython-Klasse tritt das Problem nicht auf.
@Gigaz: Das Problem ist, dass Exemplare Deiner Klasse von `joblib` nicht korrekt serialisiert werden können:
`pickle` aus der Standardbibliothek funktioniert natürlich auch nicht:
Code: Alles auswählen
In [52]: a = Pyparr.Myclass(42)
In [53]: a.getdummy()
Out[53]: 42
In [54]: joblib.dump(a, 'test.dat')
Out[54]: ['test.dat']
In [55]: b = joblib.load('test.dat')
In [56]: b.getdummy()
Out[56]: 0
Code: Alles auswählen
In [57]: import pickle
In [58]: pickle.dumps(a)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-58-ffc7f744baa1> in <module>()
----> 1 pickle.dumps(a)
/usr/lib/python2.7/pickle.pyc in dumps(obj, protocol)
1372 def dumps(obj, protocol=None):
1373 file = StringIO()
-> 1374 Pickler(file, protocol).dump(obj)
1375 return file.getvalue()
1376
/usr/lib/python2.7/pickle.pyc in dump(self, obj)
222 if self.proto >= 2:
223 self.write(PROTO + chr(self.proto))
--> 224 self.save(obj)
225 self.write(STOP)
226
/usr/lib/python2.7/pickle.pyc in save(self, obj)
304 reduce = getattr(obj, "__reduce_ex__", None)
305 if reduce:
--> 306 rv = reduce(self.proto)
307 else:
308 reduce = getattr(obj, "__reduce__", None)
/usr/lib/python2.7/copy_reg.pyc in _reduce_ex(self, proto)
68 else:
69 if base is self.__class__:
---> 70 raise TypeError, "can't pickle %s objects" % base.__name__
71 state = base(self)
72 args = (self.__class__, base, state)
TypeError: can't pickle Myclass objects
Aha, ok. Hast du einen Tip wie ich das Problem löse?BlackJack hat geschrieben:@Gigaz: Das Problem ist, dass Exemplare Deiner Klasse von `joblib` nicht korrekt serialisiert werden können:
Meine naive Vorstellung ist grade, dass es eine Möglichkeit geben sollte, der Klasse Myclass eine Methode zum picklen zu geben. :K Mal suchen.
@Gigaz: Deine Myclass muß einfach nur das Pickle-Protokoll unterstützen. Dann lassen sich auch c-Klassen serialisieren.
DankeSirius3 hat geschrieben:@Gigaz: Deine Myclass muß einfach nur das Pickle-Protokoll unterstützen. Dann lassen sich auch c-Klassen serialisieren.

Ich werde das Picke-Protokoll sicher implementieren. Ich aber festgestellt dass es eigentlich viel günstiger ist wenn ich folgendendermaßen parallelisiere:
Code: Alles auswählen
def Wrapper(dummy):
arg=Pyp.Myclass(i)
out=Pyp.pyfunc(arg)
return out
...
result=Parallel(n_jobs=n)(delayed(Wrapper)(a[i]) for i in range(4))

@Gigaz: Vielleicht noch zwei Anmerkungen zu dem Python-Quelltext:
``a = a + [value]`` erzeugt eine Liste mit einen Wert. Die wird dann durch ``+`` mit der alten Liste zu einer neuen Verbunden, das heisst es wird eine neue Liste erstellt in die alles aus `a` kopiert wird und das eine Element aus der literalen Liste und dann wird die alte Liste, die an `a` gebunden war, und die einelementige Liste verworfen. Umständlicher geht es kaum. Um eine Liste um ein Element zu erweitern gibt es die `append()`-Methode. Aber selbst das ist unnötig umständlich, man hätte auch einfach eine literale Liste mit den vier Werten schreiben können:
Oder, da die Argumente von `Myclass` aufsteigende Zahlen sind, eine „list comprehension“:
Das zweite ist das „anti pattern“ mit dem Index. Man kann in Python direkt über die Elemente einer Liste iterieren, ohne den Umweg über einen Index. Was hier zusätzlich den Vorteil hat, dass man die Liste verkürzen oder verlängern kann und automatisch alle Elemente erfasst werden, im Gegensatz zu der hart kodierten 4 in Deinem Code.
``a = a + [value]`` erzeugt eine Liste mit einen Wert. Die wird dann durch ``+`` mit der alten Liste zu einer neuen Verbunden, das heisst es wird eine neue Liste erstellt in die alles aus `a` kopiert wird und das eine Element aus der literalen Liste und dann wird die alte Liste, die an `a` gebunden war, und die einelementige Liste verworfen. Umständlicher geht es kaum. Um eine Liste um ein Element zu erweitern gibt es die `append()`-Methode. Aber selbst das ist unnötig umständlich, man hätte auch einfach eine literale Liste mit den vier Werten schreiben können:
Code: Alles auswählen
a = [pyp.Myclass(4), pyp.Myclass(5), pyp.Myclass(6), pyp.Myclass(7)]
Code: Alles auswählen
a = [pyp.Myclass(i) for i in xrange(4, 8)]
Code: Alles auswählen
out = Parallel(n_jobs=n)(delayed(Pyp.pyfunc)(x) for x in a)
Danke 
Inzwischen läuft das Programm und fittet parallelisiert. Ich musste allerdings aufs Python pp-Modul umsteigen, weil die joblib-Parallel-Prozesse offenbar nur I/O-Aufgaben beschleunigen.
Ich bin optimistisch, dass ich demnächst noch ein paar Bugs ausmerzen und ein echtes Python-Modul erstellen kann.
Die Berechnungen des Programms scheinen jedenfalls noch in keinem anderen Modul verfügbar zu sein.

Inzwischen läuft das Programm und fittet parallelisiert. Ich musste allerdings aufs Python pp-Modul umsteigen, weil die joblib-Parallel-Prozesse offenbar nur I/O-Aufgaben beschleunigen.
Ich bin optimistisch, dass ich demnächst noch ein paar Bugs ausmerzen und ein echtes Python-Modul erstellen kann.
