next up previous contents
Next: Loop-Blocking Up: Datenlokalität - Wie Compiler Previous: Hydra besitzt ausgeklügelte Cache-Hierarchien.

Loop Unrolling

Loop Unrolling bezeichnet im einfachsten Fall folgende Umformung:

     vorher                            nachher

   DO I= 1, 4                       A(1) = B(1) + C(1)
      A(I) = B(I) + C(I)            A(2) = B(2) + C(2)
   ENDDO                            A(3) = B(3) + C(3)
                                    A(4) = B(4) + C(4)

Der Vorteil dieser Transformation ist klar: Nicht nur verschwindet der Overhead für das Initialisieren und Abfragen der Schleifen-Variablen, es treten auch keine Sprünge mehr auf, die die schöne Pipeline durcheinanderbringen.

Leider ist die Arbeit für einen Compiler meistens schwerer als hier: Zum einen ist die Zahl der Iterationen meistens zu groß, um sie komplett ``aufzurollen''. Dann nimmt der Compiler ein ``partial unrolling'' vor, wie etwa im folgenden Fall:

     vorher                            nachher

   DO I = 1, 4000                   DO I = 1, 4000, 4
      A(I) = B(I) + C(I)               A(I  ) = B(I)   + C(I)
   ENDDO                               A(I+1) = B(I+1) + C(I+1)
                                       A(I+2) = B(I+2) + C(I+2)
                                       A(I+3) = B(I+3) + C(I+3)
                                    ENDDO

Den Unrolling-Faktor, im Beispiel 4, bestimmt der Compiler i.w. aus der Größe des Schleifeninneren.

Zum anderen sieht man dem Code die Zahl der Iterationen meistens nicht an. In diesem (Normal)Fall wird die Schleife teilweise ``abgerollt'', es wird aber auch noch ein Teil für den ``Rest'' eingefügt:

     vorher                            nachher

   DO I = 1, N                     REST = MOD(N, 4)                   
      A(I) = B(I) + C(I)           DO I=1,REST                        
   ENDDO                              A(I,J) = B(I,J) + C(I,J)        
                                   ENDDO                              
                                   DO I = 1+REST, N, 4                
                                      A(I,  J) = B(I  ,J) + C(I  ,J)
                                      A(I+1,J) = B(I+1,J) + C(I+1,J) 
                                      A(I+2,J) = B(I+2,J) + C(I+2,J)  
                                      A(I+3,J) = B(I+3,J) + C(I+3,J)
                                   ENDDO

Wie man sieht, ist die einfache Idee durch die harte Wirklichkeit ganz schön durcheinandergerüttelt worden. Das Beispiel zeigt auch ganz klar den Schwachpunkt an: Wenn N klein ist, wird der Optimierungs-Overhead den Gewinn mehr als zunichte machen. Ein schönes Beispiel dafür ist im vorstehenden Artikel zu sehen. In einem solchen Fall hilft nur, durch gezieltes Einfügen von Compiler-Direktiven den Unrolling-Faktor anzupassen oder Unrolling ganz zu verbieten.



rztms 26.2.96