11
votes

Réduction du nombre de registres utilisés dans le noyau Cuda

J'ai un noyau qui utilise 17 registres, ce qui la réduisait à 16 l'occupation de 100%. Ma question est la suivante: y a-t-il des méthodes pouvant être utilisées pour réduire le nombre ou les registres utilisés, à l'exclusion de la réécriture de mes algorithmes de manière différente. J'ai toujours un peu supposé que le compilateur est beaucoup plus intelligent que moi, alors par exemple, j'utilise souvent des variables supplémentaires pour la clarté. Ai-je tort dans cette pensée?

Remarque: je sais que le drapeau --Max_registers (ou quoi que ce soit la syntaxe est), mais l'utilisation de la mémoire locale serait plus préjudiciable qu'une occupation de 25% inférieure (je devrais tester cela)


5 commentaires

Entrant assez, je viens d'essayer Maxrregcount = 16 et il a réduit le nombre de registres que j'utilisais à 15 et aucune mémoire locale n'a été utilisée. Mais cela s'est effectivement devenu plus lent! Comment ça marche?


Essayez de profiler votre application. Le compilateur peut bien introduire des shenanigans.


L'occupation est plus élevée avec 15 registres que j'ai prédit et tout le reste est le même que le nombre d'instructions augmente avec un registre de registre inférieur. de 3,9m à 4,3 m


Qu'en est-il du nombre de branches / divergence pendant l'exécution?


Seulement 8 ans de retard à la fête, mais la raison pour laquelle il a été plus lent est probablement parce que le compilateur a commencé à faire REMATÉIALISATION . Il évitait d'utiliser plus de registres en calculant simplement la valeur encore et encore chaque fois qu'il était nécessaire.


5 Réponses :


6
votes

C'est vraiment difficile à dire, le compilateur NVCC n'est pas très intelligent à mon avis.
Vous pouvez essayer des choses évidentes, par exemple en utilisant courtes au lieu d'INT, de passer et d'utiliser des variables par référence (par exemple et variable), des boucles déroulantes, à l'aide de modèles (comme dans C ++). Si vous avez des divisions, des fonctions transcendantaires, ont été appliquées en séquence, essayez de les faire en boucle. Essayez de vous débarrasser des conditionnels, éventuellement les remplacer avec des calculs redondants.

Si vous publiez du code, vous obtiendrez peut-être des réponses spécifiques.


1 commentaires

Étant donné que les registres sont 32 bits et INT sont de 32 bits sur le GPU, ne seraient-ils pas et ne font aucune différence?




1
votes

Le nombre d'instructions augmente en cas d'abaissement de l'utilisation de registres d'une explication simple. Le compilateur pourrait utiliser des registres pour stocker les résultats de certaines opérations qui sont utilisées plus d'une fois par votre code afin d'éviter de recalculer ces valeurs, lorsqu'il est forcé d'utiliser moins de registres, le compilateur décide de recalculer ces valeurs qui seraient stockées dans des registres sinon.


0 commentaires

2
votes

Ce n'est généralement pas une bonne approche pour minimiser la pression du registre. Le compilateur optimise un bon travail optimisant les performances globales du noyau projetées et prend en compte beaucoup de facteurs, incluant le registre.

Comment fonctionne-t-il lorsque la réduction des registres a causé une vitesse plus lente

Très probablement Le compilateur a dû renverser des données d'enregistrement insuffisantes dans la mémoire "locale", qui est essentiellement la même que la mémoire globale, et donc très lente

À des fins d'optimisation, je recommanderais d'utiliser des mots-clés tels que const, volatile et ainsi de suite, le cas échéant, pour aider le compilateur à la phase d'optimisation.

Quoi qu'il en soit, ce n'est pas ces minuscules problèmes tels que des registres qui rendent souvent lentement les noyaux Cuda. Je recommanderais d'optimiser le travail avec la mémoire globale, le modèle d'accès, la mise en cache dans la mémoire de texture si possible, des transactions sur la PCIe.


0 commentaires

4
votes

Utilisation de la mémoire partagée que le cache peut mener moins d'un enregistrement d'utilisation et d'empêcher son registre de renversement à la mémoire locale ...

pense que le noyau calcule certaines valeurs et ces valeurs calculées sont utilisées par tous les threads, P> XXX PRE>

Ainsi, au lieu de garder Reg et Reg0 comme des registres et de les rendre possible à la mémoire locale (Mémoire globale), nous pouvons utiliser la mémoire partagée. P>

__global__ void kernel(...) {
    __shared__ int cache[10];

    int idx = threadIdx.x + blockDim.x * blockIdx.x;

    if (threadIdx.x == 0) {
      int id0 = blockDim.x * blockIdx.x;

      cache[0] = id0 * ...;
      cache[1] = cache[0] * a / x + y;
    }
    __syncthreads();


    ...

    int val =  cache[0] + cache[1] + 2 * idx;

    output[idx] = val > 10;
}


3 commentaires

Chaque bloc distinct nécessite sa propre zone de cache et un premier fil de chaque bloc devrait le remplir. Donc, chaque bloc est indépendant et n'a pas besoin de synchronisation. __syncThreads Après la synchronisation de l'instruction IF.'s the threads dans un bloc. Cependant, la partie série augmente de cette manière et pourrait ne pas être une bonne solution ..


Déjà threfidx.x = 6 ne calculera rien. Il obtiendra le résultat du calcul du cache et le cache aura le résultat du calcul que le point de synchronisation est passé. N'est-ce pas?


Voulez-vous dire les deux dernières lignes? Lire du cache ?? Y a-t-il un moyen de le réparer, de fil_fence, etc.?