Röntgenoptik-Projekt

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
BlackJack

@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.
BlackJack

``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
Gigaz
User
Beiträge: 16
Registriert: Freitag 22. April 2016, 16:58

Ich habe auch schon festgestellt beim Testen dass die Zahlen die ich in python eingebe offenbar nicht im C-Programm ankommen :lol:
Deine neue Variante funktioniert jetzt aber. Vielen Dank.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

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.
Ich mache sowas mit Swig. Swig ermöglicht mir im Python-Code auf meine C++ Klassen zuzugreifen, als ob es Python-Klassen wären.
a fool with a tool is still a fool, www.magben.de, YouTube
Diggum
User
Beiträge: 1
Registriert: Samstag 21. Mai 2016, 10:53

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
Gigaz
User
Beiträge: 16
Registriert: Freitag 22. April 2016, 16:58

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
Danke für den Tip, ich werde das gleich mal lesen.

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()
setup.py: Ausführen mit python setup.py build_ext --inplace

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
Bei mir steht dann als Ausgabe:
[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.
BlackJack

@Gigaz: Das Problem ist, dass Exemplare Deiner Klasse von `joblib` nicht korrekt serialisiert werden können:

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
`pickle` aus der Standardbibliothek funktioniert natürlich auch nicht:

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
Gigaz
User
Beiträge: 16
Registriert: Freitag 22. April 2016, 16:58

BlackJack hat geschrieben:@Gigaz: Das Problem ist, dass Exemplare Deiner Klasse von `joblib` nicht korrekt serialisiert werden können:
Aha, ok. Hast du einen Tip wie ich das Problem löse?
Meine naive Vorstellung ist grade, dass es eine Möglichkeit geben sollte, der Klasse Myclass eine Methode zum picklen zu geben. :K Mal suchen.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@Gigaz: Deine Myclass muß einfach nur das Pickle-Protokoll unterstützen. Dann lassen sich auch c-Klassen serialisieren.
Gigaz
User
Beiträge: 16
Registriert: Freitag 22. April 2016, 16:58

Sirius3 hat geschrieben:@Gigaz: Deine Myclass muß einfach nur das Pickle-Protokoll unterstützen. Dann lassen sich auch c-Klassen serialisieren.
Danke :D

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))
Das funktioniert jetzt natürlich, weil der gepickelte Input nur noch eine Zahl ist. Damit ist das Problem erstmal gelöst, danke für euer aller Hilfe. :wink:
BlackJack

@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:

Code: Alles auswählen

a = [pyp.Myclass(4), pyp.Myclass(5), pyp.Myclass(6), pyp.Myclass(7)]
Oder, da die Argumente von `Myclass` aufsteigende Zahlen sind, eine „list comprehension“:

Code: Alles auswählen

a = [pyp.Myclass(i) for i in xrange(4, 8)]
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.

Code: Alles auswählen

out = Parallel(n_jobs=n)(delayed(Pyp.pyfunc)(x) for x in a)
Gigaz
User
Beiträge: 16
Registriert: Freitag 22. April 2016, 16:58

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. :D Die Berechnungen des Programms scheinen jedenfalls noch in keinem anderen Modul verfügbar zu sein.
Antworten