In CUDA ist es möglich, Schleifen mit der Anweisung #pragma
auszurollen, um die Leistung durch Erhöhen der Parallelität auf Befehlsebene zu verbessern. Auf das #pragma
kann optional eine Zahl folgen, die angibt, wie oft die Schleife entrollt werden muss.
Leider geben die Dokumente keine genauen Anweisungen darüber, wann diese Anweisung verwendet werden sollte. Da kleine Loops mit einer bekannten Trip-Anzahl bereits vom Compiler abgewickelt werden, sollte #pragma
unroll auf größeren Loops verwendet werden? Auf kleinen Schleifen mit einem variablen Zähler? Und was ist mit der optionalen Anzahl von Abrollungen? Gibt es auch empfohlene Dokumentation über cuda-spezifische Loop-Abrollung?
Es gibt keine schnellen und harten Regeln. Der CUDA-Compiler verfügt über mindestens zwei Unroller, jeweils einen innerhalb der NVVM- oder Open64-Frontends und einen im PTXAS-Backend. Im Allgemeinen neigen sie dazu, Loops ziemlich aggressiv zu entschlüsseln. Daher benutze ich #pragma unroll 1
(um das Abrollen zu verhindern) häufiger als jedes andere Unrolling-Attribut. Die Gründe für das Deaktivieren des Loop-Abrollens sind zweifach:
(1) Wenn eine Schleife vollständig abgerollt wird, kann sich der Registerdruck erhöhen. Beispielsweise können Indizes in kleine lokale Speicherarrays zu Kompilierzeitkonstanten werden, die es dem Compiler ermöglichen, die lokalen Daten in Registern zu platzieren. Ein vollständiges Abrollen kann auch dazu führen, dass Grundblöcke verlängert werden, was eine aggressivere Planung von Textur- und globalen Lasten ermöglicht, die zusätzliche temporäre Variablen und somit Register erfordern können. Ein erhöhter Registerdruck kann zu einer geringeren Leistung aufgrund von Registerüberlauf führen.
(2) Teilweise abgerollte Schleifen benötigen normalerweise eine bestimmte Menge an Vorberechnungs- und Bereinigungscode, um Schleifenzählungen zu verarbeiten, die nicht genau ein Vielfaches des Abwicklungsfaktors sind. Bei Loops mit kurzen Trip-Counts kann dieser Overhead die Performance-Gewinne der abgerollten Loop übertreffen, was zu einer geringeren Performance nach dem Abrollen führt. Während der Compiler Heuristiken zum Finden geeigneter Schleifen unter diesen Einschränkungen enthält, kann die Heuristik nicht immer die beste Entscheidung liefern.
In seltenen Fällen habe ich festgestellt, dass die manuelle Bereitstellung eines höheren Unrolling-Faktors als der, den der Compiler automatisch verwendet, einen kleinen positiven Effekt auf die Performance hat (mit einem typischen Gewinn im einstelligen Prozentbereich). Dies sind in der Regel Fälle von speicherintensivem Code, bei denen ein größerer Unrolling-Faktor eine aggressivere Planung von globalen oder Texturlasten oder sehr enge rechenintensive Schleifen ermöglicht, die von einer Minimierung des Schleifenoverheads profitieren.
Das Spielen mit Abwicklungsfaktoren sollte etwas später im Optimierungsprozess passieren, da die Compiler-Standardeinstellungen die meisten Fälle abdecken, denen man in der Praxis begegnet.
Es ist ein Werkzeug, mit dem Sie Schleifen loeschen können. Die Details, wann es verwendet werden sollte / sollte, variieren stark abhängig von Ihrem Code (was zum Beispiel in der Schleife ist). Es gibt nicht wirklich gute generische Tipps außer denken Sie daran, wie Ihr Code wäre wie abgerollt vs gerollt und denke, wenn es besser abgerollt wäre.
Tags und Links optimization cuda loop-unrolling