4 votes

__sync_bool_compare_and_swap avec différents types de paramètres en Cython

J'utilise Cython pour le traitement parallèle rapide de données, en ajoutant des éléments à une liste liée à mémoire partagée à partir de plusieurs fils. J'utilise __sync_bool_compare_and_swap, qui fournit une opération atomique de comparaison et d'échange (CAS) pour comparer si la valeur n'a pas été modifiée (par un autre thread) avant de la remplacer par une nouvelle valeur.

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

Cela fonctionne très bien. Cependant, maintenant je veux aussi garder la trace de la taille de la liste liée, et je veux utiliser la même fonction CAS pour les mises à jour, cependant, cette fois ce ne sont pas des pointeurs qui doivent être mis à jour mais un int. Comment utiliser deux fois la même fonction externe en Cython, une fois avec un paramètre void** et une fois avec un paramètre int* ?

EDIT

Ce que j'ai à l'esprit est deux opérations atomiques séparées, dans une opération atomique je veux mettre à jour la liste liée, dans l'autre je veux mettre à jour la taille. Vous pouvez le faire en C, mais pour Cython cela signifie que vous devez référencer la même fonction externe deux fois avec des paramètres différents, est-ce possible ?

CONCLUSION

La réponse proposée par DavidW fonctionne. Au cas où quelqu'un envisagerait d'utiliser une construction similaire, vous devez savoir que lorsque vous utilisez deux fonctions de mise à jour séparées, il n'y a aucune garantie qu'elles soient traitées en séquence (c'est-à-dire qu'un autre thread peut effectuer une mise à jour entre les deux), cependant, si l'objectif est de mettre à jour une valeur cumulative, par exemple pour surveiller la progression en multithreading ou pour créer un résultat agrégé qui n'est pas utilisé avant que tous les threads aient terminé, CAS garantit que toutes les mises à jour sont effectuées exactement une fois. De manière inattendue, gcc refuse de compiler sans casting vers void*, donc soit vous définissez des versions séparées et typées en dur, soit vous devez faire un cast. Un extrait de mon code :

dans 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

dans 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

dans 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

4voto

DavidW Points 13614

Donc le problème (tel que je le comprends) est que c'est __sync_bool_compare_and_swap est un intrinsèque du compilateur plutôt qu'une fonction, et n'a donc pas vraiment de signature fixe, car le compilateur se contente de la déterminer. Cependant, Cython exige de connaître les types, et comme vous voulez l'utiliser avec deux types différents, vous avez un problème.

Je ne vois pas de moyen plus simple que de recourir à une (très) petite quantité de C pour "aider" Cython. Créez un fichier d'en-tête avec un tas de fichiers #defines

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

Vous pouvez alors dire à Cython que chacun de ces éléments est une fonction distincte

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

À ce stade, vous devriez être en mesure de les utiliser naturellement comme de simples fonctions sans aucun casting de type (c'est-à-dire pas de <void**> dans votre code, car cela tend à masquer les véritables erreurs). Le préprocesseur C générera le code que vous voulez et tout va bien.


Je ne voudrais pas vous recommander de savoir s'il est sûr pour vous d'utiliser deux opérations atomiques comme celle-ci, ou si cela passera par un état avec des données dangereusement incohérentes.....

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X