Comment allouer dynamiquement des tableaux à l'intérieur d'un noyau?
J'ai besoin d'allouer dynamiquement de certains tableaux à l'intérieur de la fonction noyau. Comment puis-je faire?
Mon code est quelque chose comme ça:
__global__ func(float *grid_d,int n, int nn){
int i,j;
float x[n],y[nn];
//Do some really cool and heavy computations here that takes hours.
}
Mais qui ne fonctionnera pas. Si c'était à l'intérieur de l'hôte code que je pouvais utiliser la fonction malloc. cudaMalloc a besoin d'un pointeur sur l'hôte, et d'autres sur l'appareil. À l'intérieur de la fonction du noyau je n'ai pas l'hôte pointeur.
Alors, que dois-je faire?
Si prend trop de temps (quelques secondes) à allouer tous les tableaux (j'ai besoin d'environ 4 de taille n et 5 de taille nn), ce ne sera pas un problème. Depuis le noyau sera probablement courir pendant 20 minutes, au moins.
- Vous voulez probablement à lire la section sur allocation dynamique de la mémoire dans le code de l'appareil dans la CUDA C guide des programmeurs. Cette fonctionnalité nécessite de calculer la capacité de 2,0 ou plus dans votre GPU.
- Quelle est la configuration (blocs, les threads), vous serez l'exécution de ce noyau? Quelles sont les gammes de
n
etnn
(pour les petites tailles que vous pourriez presser dans les registres, ou de la mémoire partagée).
Vous devez vous connecter pour publier un commentaire.
Allocation dynamique de la mémoire est pris en charge uniquement sur le calcul de la capacité 2.x et un matériel plus récent. Vous pouvez utiliser le C++ nouveau mot-clé ou malloc dans le noyau, de sorte que votre exemple pourrait devenir:
Ce alloue de la mémoire sur la mémoire locale d'exécution tas qui a la durée de vie du contexte, donc assurez-vous de libérer la mémoire après que le noyau de la fin de l'exécution, si votre intention est de ne pas utiliser la mémoire à nouveau. Il convient également de noter que l'exécution de segment de mémoire ne sont pas accessibles directement à partir de l'hôte Api, de sorte que vous ne pouvez pas passer un pointeur alloué à l'intérieur d'un noyau comme un argument de
cudaMemcpy
, par exemple.free
approprié ici, ou est-il une autre fonction pour libérer à partir du tas local à l'intérieur d'un noyau?@talonmies répondu à votre question sur la façon d'allouer dynamiquement de la mémoire au sein d'un noyau. C'est prévu pour compléter la réponse, traitant de la performance de
__device__ malloc()
et alternative, vous pourriez envisager.L'allocation dynamique de la mémoire dans le noyau peut être tentant, car il permet de code GPU à regarder de plus comme le code de CPU. Mais il peut sérieusement affecter les performances. J'ai écrit un autonome de test, et nous avons inclus ci-dessous. Le test des lancements quelque 2,6 millions de threads. Chaque thread remplit 16 entiers de la mémoire globale avec certaines valeurs issues du fil de l'index, puis résume les valeurs et renvoie la somme.
Le test met en œuvre deux approches. La première approche utilise
__device__ malloc()
et la deuxième approche utilise de la mémoire qui est allouée avant que le noyau s'exécute.Sur mon 2.0 appareil, le noyau s'exécute dans 1500ms lors de l'utilisation de
__device__ malloc()
et 27 ms est observée lors de l'utilisation de pré-alloué de la mémoire. En d'autres termes, le test prend 56x plus à exécuter lorsque la mémoire est allouée dynamiquement à l'intérieur du noyau. Le temps comprend la boucle externecudaMalloc()
/cudaFree()
, qui ne fait pas partie du noyau. Si le même noyau est lancé plusieurs fois avec le même nombre de fils, comme c'est souvent le cas, le coût de lacudaMalloc()
/cudaFree()
est amorti sur tous le noyau lance. Qu'apporte la différence encore plus élevé, à environ 60x.De spéculer, je pense que le gain de performance est en partie causé par implicite de sérialisation. Le GPU doit probablement sérialiser tous les appels simultanés à
__device__ malloc()
afin de fournir des morceaux de mémoire pour chaque appelant.La version qui n'utilise pas
__device__ malloc()
alloue tous les GPU de la mémoire avant de lancer le noyau. Un pointeur vers la mémoire est transmis au noyau. Chaque thread calcule un indice dans le précédemment alloué de la mémoire au lieu d'utiliser un__device__ malloc()
.Le problème potentiel avec l'allocation de mémoire à l'avant, c'est que, si seuls certains threads ont besoin d'allouer de la mémoire, et on ne sait pas qui threads ceux qui sont, il sera nécessaire d'allouer de la mémoire pour tous les threads. Si il n'y a pas assez de mémoire pour que, il pourrait être plus efficace de réduire le nombre de threads par noyau appel, puis à l'aide de
__device__ malloc()
. D'autres solutions de contournement serait probablement jusqu'à la fin de réimplanter ce__device__ malloc()
est fait en arrière-plan, et serait de voir une performance similaire a frappé.Tester les performances de
__device__ malloc()
:De sortie:
cudaMalloc
alloue un grand tableau, et ce par rapport à l'allocation de 2,5 millions de petites matrices (pour chaque thread d'un). Une telle procédure est évidemment plus lent, et un test sur le CPU montre, que votre signalé 60x ralentissement est en fait un bon boulot (je suis 1000x fois ralentissement, le code fourni n'a pas d'erreur de segmentation -- allocateur doit supporter autant de matrices). Juste test est: allouer la même (un) array (1) parcudaMalloc
, (2) parkernel<<<1,1>>>
. Je vois lekernel
allocation étant plus lent ~3 fois. Donc, c'est le vrai gain de performance.__device__ malloc()
et de montrer une autre façon d'accomplir la tâche pour laquelle nombreux sont ceux qui considèrent__device__ malloc()
. Le but n'était pas de comparer la performance d'un seulcudaMalloc()
avec un seul__device__ malloc()
.Si la valeur de n et nn étaient connus avant le noyau est appelée, alors pourquoi ne pas cudaMalloc la mémoire de l'hôte de côté et de passer dans la mémoire de l'appareil pointeur vers le noyau?
Mené une expérience basée sur les concepts de la @rogerdahl post. Hypothèses:
Le malloc+appels gratuits local pour le GPU semble être beaucoup plus rapide que la
cudaMalloc
+cudaFree
appels. Le programme de la sortie:Je pars le code pour
timer.h
ettimer.cpp
, mais voici le code pour le test lui-même:Si vous trouvez des erreurs, veuillez lmk dans les commentaires, et je vais essayer de les réparer.
Et j'ai couru de nouveau avec un plus grand tout:
Et cudaMalloc était encore plus lent par beaucoup:
malloc
+free
a pris essentiellement la même quantité de temps quenew
etdelete
.Peut-être que vous devriez tester
au lieu