1 points par GN⁺ 2025-02-10 | 1 commentaires | Partager sur WhatsApp

Introduction

  • Cet article vise à corriger certaines idées reçues sur les branches sur GPU.
  • Certains sites éducatifs diffusent des informations erronées, et l’objectif est de les rectifier.

Problème

  • Un exemple de code utilisant l’opérateur ternaire pour implémenter une exécution conditionnelle dans du code GPU est présenté.
  • Certaines personnes proposent comme « optimisation » de le remplacer par des opérations arithmétiques, mais cela repose sur une mauvaise compréhension.
  • L’opérateur ternaire effectue un déplacement conditionnel, implémenté à l’aide de simples opérations sur les bits.
  • De vraies branches existent dans le code GPU, mais elles ne sont pas utilisées pour de petits déplacements de registres.

Problèmes de cette mauvaise optimisation

  • L’optimisation proposée s’exécute en réalité plus lentement que le code d’origine.
  • La fonction step() est implémentée avec un opérateur ternaire, ce qui ajoute des multiplications et additions inutiles.
  • Dans le code d’origine, la valeur est déplacée directement de manière conditionnelle.

Analyse du code machine

  • Le code machine généré par les compilateurs AMD et Microsoft permet de confirmer que le GPU n’effectue pas de branchement.
  • Il réalise le déplacement conditionnel à l’aide d’une comparaison et d’un masque binaire.

Conclusion

  • Les propositions d’optimisation utilisant la fonction step() relèvent d’une information erronée, et il faut les corriger.

  • Cette mauvaise information circule depuis plus de 20 ans, et il est nécessaire de la rectifier.

  • Inigo Quilez - étudie l’infographie depuis 1994.

1 commentaires

 
GN⁺ 2025-02-10
Commentaire Hacker News
  • Je suis convaincu que la conclusion de l’article est correcte, mais l’argument serait plus solide si la génération de code des deux versions était fournie, et pas seulement celle de la meilleure

    • La version « optimisée » montre bien, via le code machine généré, qu’elle s’exécute en réalité bien plus lentement que la version d’origine, mais cela ne prouve pas que la mauvaise version soit effectivement pire
  • J’aimerais qu’il existe un bon moyen de savoir dans quels cas un if force réellement une branche

    • Si les gens utilisent des mix/lerp plus coûteux, c’est parce qu’ils craignent de créer une branche, même si le surcoût est parfois faible
    • Il est appréciable que v = x > y ? a : b; fonctionne réellement, mais il est préoccupant qu’un if soit une branche dans certains cas et pas dans d’autres
  • Cet article est aussi lié : corriger les mauvais conseils sur l’écriture de branches sur GPU

    • Autrefois, les optimisations visant à éviter les branches étaient efficaces, mais il ne faut plus procéder ainsi aujourd’hui
    • Comme les processeurs et les compilateurs évoluent, il vaut mieux proposer plusieurs variantes et sélectionner à l’exécution la plus rapide
  • Je me demande pourquoi le compilateur ne reconnaît pas que la version « optimisée » est équivalente

    • Il devrait être capable de comprendre step() et d’optimiser séparément les cas step()=0.0 et step()==1.0
  • Je suis déjà tombé sur ce problème. Claude/ChatGPT le proposent aussi comme optimisation, mais cela dégrade les performances

    • Merci à Inigo
  • Je me demande comment savoir si une fonction OpenGL est émulée au lieu d’appeler directement une fonctionnalité de base du GPU

  • Lorsqu’on écrit du code, il faut de l’expérience pour avoir la certitude qu’il n’y aura pas de branche conditionnelle

    • Il est difficile de savoir combien d’opérations après une condition déclencheront une branche, et quelles opérations le compilateur pourra éliminer
    • Je me demande s’il faut utiliser une suite de tests de performance pour détecter les régressions accidentelles
  • Explication de la manière dont les variantes de la fonction mix fonctionnent sur les vecteurs