le guide de programmation CUDA dit que toute opération atomique peut être implémentée en utilisant atomicCAS ()
, et donne un exemple de double add atomique:
*address_1 = *address_1+val_1; *address_2 = *address_2+val_2;
faire face au problème que:
Je veux écrire une fonction qui peut faire fonctionner deux variables d'adresse atomiquement.
par exemple: atomic ajoute environ deux variables
input
double *address_1, int *address_2 double val_1,int val_2
result
__device__ float single(double *address,double val) { unsigned long long int *address_as_ull =(unsigned long long int*)address; unsigned long long int assumed; unsigned long long int old = *address_as_ull; do { assumed = old; old = atomicCAS(address_as_ull,assumed,__double_as_longlong(val + __longlong_as_double(assumed))); }while(assumed !=old); return __longlong_as_double(old); }
comment puis-je gérer le problème? merci.
3 Réponses :
Je pense que vous manquez le point de l'opération implémentée ici. En a + = b
, l'opération logique est a = a + b
, mais avec CAS vous évitez les modifications fallacieuses de a
entre sa lecture et sa écrivez. b
est utilisé une fois et pas de problème.
Dans a = b + c
, aucune des valeurs n'apparaît deux fois, il n'est donc pas nécessaire de se protéger contre les modifications intermédiaires.
Merci pour votre réponse. Je veux écrire un code atomique qui peut modifier deux variables de manière atomique. avez-vous une suggestion?
merci pour votre clarification ~ maintenant j'ai la solution ~
merci à tous les gars répondez-moi! J'ai maintenant la solution. nous pouvons combiner les deux variables dans une structure. ainsi nous pouvons transférer "deux variables avec deux adresses" dans "une structure avec une adresse". voici le code:
x=15000.000000 y=20000
et la solution est
#include <stdio.h> struct pair_t { float x; int y; }; __device__ float single(double *address,double val) { unsigned long long int *address_as_ull =(unsigned long long int*)address; unsigned long long int assumed; unsigned long long int old = *address_as_ull; do { assumed = old; old = atomicCAS(address_as_ull,assumed,__double_as_longlong(val + __longlong_as_double(assumed))); }while(assumed !=old); return __longlong_as_double(old); } __device__ void myadd(pair_t *address, double val_1 ,int val_2) { union myunion { pair_t p; unsigned long long int ull; }; unsigned long long int *address_as_ull; address_as_ull = (unsigned long long int *)address; union myunion assumed; union myunion old_value; union myunion new_value; old_value.p = *(pair_t *)address_as_ull; do { assumed = old_value; // cirtical area begin-------------------- new_value.p.x = assumed.p.x+val_1; new_value.p.y = assumed.p.y+val_2; // cirtical area end---------------------- old_value.ull = atomicCAS(address_as_ull,assumed.ull,new_value.ull); }while(assumed.ull !=old_value.ull); } __global__ void kernel (pair_t *p) { myadd(p,1.5,2); } int main() { pair_t p; p.x=0; p.y=0; pair_t *d_p = NULL; cudaMalloc((pair_t **)&d_p, sizeof(pair_t)); cudaMemcpy(d_p, &p, sizeof(pair_t), cudaMemcpyHostToDevice); kernel<<<100, 100>>>(d_p); cudaMemcpy(&p, d_p, sizeof(pair_t), cudaMemcpyDeviceToHost); cudaDeviceSynchronize(); printf("x=%lf\n", p.x); printf("y=%d\n", p.y); cudaDeviceReset(); return 0; }
maintenant tout ira bien ~
1. C'est compliqué. Vous n'avez pas du tout à passer par des doubles. 2. Plus généralement - cela ne fonctionne que puisque la taille totale de la structure est suffisamment petite pour que vous puissiez agir sur elle de manière atomique.
En général, vous ne pouvez pas faire cela. Le matériel ne prend pas en charge les modifications atomiques à plusieurs emplacements de la mémoire. Bien que vous puissiez contourner cela si les deux variables sont suffisamment petites pour tenir dans la taille d'une seule opération atomique, cette approche échouera si vous avez plus de 8 octets au total. Vous rencontrerez le "trop de lait" problème.
Une chose que vous pourriez faire est d'avoir une sorte de protocole de synchronisation pour accéder à ces deux valeurs. Par exemple, vous pouvez utiliser un mutex, que seul un thread peut obtenir, pour savoir en toute sécurité que personne d'autre ne change les valeurs pendant que ce thread travaille dessus. Voir: Évitez de prendre trop de temps pour terminer le scénario "trop de lait" .
Bien sûr, cela coûte assez cher dans un réglage GPU. Vous feriez probablement mieux de faire l'une des choses suivantes (en augmentant l'ordre de favorabilité):
Merci pour votre clarté ~ Le matériel que vous avez joint m'a vraiment aidé ~
Qu'y a-t-il de mal à appeler la fonction d'addition atomique deux fois? D'après la description de votre question, les deux opérations sont indépendantes.
voici un exemple simple, en fait dans ce cas faire l'opération deux fois est ok ~ mais dans ma situation, j'ai besoin de la fonction atomique avec deux adresses. avez-vous une suggestion? merci ~
Rien de tel n'existe
Je pense que l'utilisation d'une structure pour stocker les deux variables peut être utile. vous voyez que le double est transféré dans ull, mais je n'ai aucune idée de comment traiter le long long int et la structure non signés.