2016-05-12 8 views
3

Ich verwende Cython für die schnelle parallele Verarbeitung von Daten, Hinzufügen von Elementen zu einer gemeinsamen Speicher verknüpften Liste aus mehreren Threads. Ich benutze __sync_bool_compare_and_swap, das eine atomare Vergleichs- und Auslagerungsoperation (CAS) zum Vergleichen bietet, wenn der Wert nicht geändert wurde (durch einen anderen Thread), bevor er durch einen neuen Wert ersetzt wurde.__sync_bool_compare_and_swap mit verschiedenen Parametertypen in Cython

cdef extern int __sync_bool_compare_and_swap (void **ptr, void *oldval, void *newval) nogil 

cdef bint firstAttempt = 1 
cdef type *next = NULL 
cdef type *newlink = .... 

while firstAttempt or not __sync_bool_compare_and_swap(<void**> c, <void*>next, <void*>newlink): 
    firstAttempt = 0 
    next = c[0] 
    newlink.next = next 

Das funktioniert sehr gut. Jetzt möchte ich aber auch die Größe der verknüpften Liste verfolgen und die gleiche CAS-Funktion für die Updates verwenden, diesmal sind es jedoch keine Zeiger, die aktualisiert werden müssen, sondern ein int. Wie kann dieselbe externe Funktion zweimal in Cython verwendet werden, einmal mit void ** -Parameter und einmal mit einem int * -Parameter?

EDIT

Was ich im Sinn haben zwei getrennte atomare Operationen ist, in einer atomaren Operation ich die verknüpfte Liste aktualisieren möchten, in der anderen möchte ich die Größe aktualisieren. Sie können es in C tun, aber für Cython bedeutet es, dass Sie die gleiche externe Funktion zweimal mit verschiedenen Parametern referenzieren müssen, ist das möglich?

FAZIT

Die Antwort von DavidW vorgeschlagen arbeitet. Falls jemand eine ähnliche Konstruktion verwendet, sollten Sie beachten, dass bei der Verwendung von zwei separaten Update-Funktionen keine Garantie besteht, dass diese nacheinander verarbeitet werden (dh ein anderer Thread kann dazwischen aktualisieren), wenn das Ziel ist, ein Update durchzuführen B. zur Überwachung des Fortschritts während des Multithreading oder zur Erstellung eines aggregierten Ergebnisses, das erst nach Beendigung aller Threads verwendet wird, garantiert CAS, dass alle Updates genau einmal ausgeführt werden. Unerwarteterweise verweigert gcc die Kompilierung, ohne in void * umzuwandeln, also entweder separate hard-typed-Versionen zu definieren, oder Sie müssen casten. Ein Ausschnitt aus meinem Code:

in some_header.h:

#define sync_bool_compare_and_swap_int __sync_bool_compare_and_swap 
#define sync_bool_compare_and_swap_vp __sync_bool_compare_and_swap 

in some_prog.pxd:

cdef extern from "some_header.h": 
    cdef extern int sync_bool_compare_and_swap_vp (void **ptr, void *oldval, void *newval) nogil 
    cdef extern int sync_bool_compare_and_swap_int (int *ptr, int oldval, int newval) nogil 

in some_prog.pyx:

cdef void updateInt(int *value, int incr) nogil: 
    cdef cINT previous = value[0] 
    cdef cINT updated = previous + incr 

    while not sync_bool_compare_and_swap_int(c, previous, updated): 
     previous = value[0] 
     updated = previous + incr 
+0

Das auf eine Sperre verzichten, wenn die Daten konsistent sein sollen. das würde beinhalten, atomare Daten an zwei völlig verschiedenen Speicherorten zu ändern. Es scheint aber nicht so, als ob es irgendwie nützlich wäre. Was ist dein tatsächlicher Anwendungsfall? – Voo

+0

@ Voo hinzugefügt einige Klarheit der Frage, das können zwei separate atomare Operationen sein, das Problem ist, wie die gleiche externe Funktion zweimal in Cython mit verschiedenen Parametern zu referenzieren? –

+0

Fair genug, das Problem ist, dass diese nicht wirklich "Funktionen" sind. Sie sind Einbauten, die sich ähnlich wie eine Funktion verhalten. Ich sehe nur die zwei offensichtlichen Problemumgehungen: Verknüpfen Sie eine c-DLL, die die Aufrufe weiterleitet, oder fügen Sie die Definitionen in separate Module ein. – Voo

Antwort

3

So ist die Ausgabe (wie ich es verstehe) ist, dass es __sync_bool_compare_and_swap ist ein Compiler intrinsisch und nicht eine Funktion, so hat nicht wirklich eine feste Signatur, weil der Compiler es gerade herausfindet. Cython fordert jedoch, die Typen zu kennen, und weil Sie es mit zwei verschiedenen Typen verwenden möchten, haben Sie ein Problem.

Ich kann einen einfacheren Weg nicht sehen, als auf eine (sehr) kleine Menge von C zurückzugreifen, um Cython zu "helfen". Erstellen Sie eine Header-Datei mit einem Bündel von #defines

/* compare_swap.h */ 
#define sync_bool_compare_swap_voidp __sync_bool_compare_swap 
#define sync_bool_compare_swap_int __sync_bool_compare_swap 

Sie dann Cython sagen können, dass jeder von ihnen eine eigene Funktion

cdef extern from "compare_swap.h": 
    int sync_bool_compare_swap_voidp(void**, void*, void*) 
    int sync_bool_compare_swap_int(int*, int, int) 

In diesem Stadium ist, sollten Sie in der Lage sein, sie als Klar Funktionen natürlich verwenden ohne irgendeine Art Casting (dh keine <void**> in Ihrem Code, da dies dazu neigt, echte Fehler zu verbergen). Der C-Präprozessor erzeugt den gewünschten Code und alles ist gut.schlicht unmöglich ist,


Darauf würde ich nicht empfehlen möchte, ob es sicher ist für Sie zwei atomare Operationen wie folgt zu verwenden, oder ob diese durch einen Zustand mit gefährlich inkonsistenten Daten passieren ....

+0

Danke @DavidW, dies scheint erforderlich zu sein, damit es funktioniert. Sie sind berechtigt, Bedenken wegen der Gefahr des Arbeitens mit inkonsistenten Daten zu äußern, jedoch sollte dies für die Verfolgung des Fortschritts oder die Berechnung eines Ergebnisses, das erst nach Beendigung aller Threads verwendet wird, in Ordnung sein. –

+0

@jeroen die Kosten der atomaren Operationen ist wahrscheinlich höher als einmal durch die Liste iterieren, nachdem Sie dann fertig sind. – Voo