C++Guns – RoboBlog

08.12.2018

C++ Guns: Level of Indirection ("dereferencing")

Filed under: Allgemein — Tags: — Thomas @ 13:12

Die Performance ist ein Opfer von Level of Indirection. Jetzt auch bei mir. Ich will keinen langweiligen Code zeigen wie so etwas aussieht, oder wie man es verhindern kann. Viel interessanter ist es doch, wie das aus Assembler Ebene aussieht.
Dazu zwei Assembler Auszüge. Der erste mit zwei zusätzlichen Indirektionen. Es wird auf das i-th Element eines std::vector zugegriffen und zurück gegeben.

func(std::vector<T> const&, int):
        movq    %rdi, %rax       }
        movslq  %edx, %rdx       |
        salq    $4, %rdx         | Addressberechnung
        addq    (%rsi), %rdx     }
        movq    (%rdx), %rdx     * Indirektion 
        movq    (%rdx), %rdx     * Indirektion
        movdqu  (%rdx), %xmm0    }
        movups  %xmm0, (%rdi)    |
        movdqu  16(%rdx), %xmm1  | Kopieren der 4 double Variablen
        movups  %xmm1, 16(%rdi)  }
        ret

Ohne zweimalige Indirektion:

func2(std::vector<T> const&, int):
        movq    %rdi, %rax        }
        movslq  %edx, %rdx        |
        salq    $5, %rdx          | Addressberechnung
        addq    (%rsi), %rdx      }
        movdqu  (%rdx), %xmm0     }
        movups  %xmm0, (%rdi)     |
        movdqu  16(%rdx), %xmm1   | Kopieren der 4 double Variablen
        movups  %xmm1, 16(%rdi)   }
        ret

Es sind zwei Dinge zu erkennen.
Erstens gibt es zwei zusätzliche mov Anweisungen, welche eine Dereferenzierung eines Pointers bewirken. Die zusätzliche Arbeit den Befehl zu bearbeiten ist nicht das Problem. Vielmehr, dass die Daten nicht mehr hintereinander im RAM stehen. Es wird im RAM umher gesprungen. Dies geht bekanntlich auf die Performance, Cache misses u.s.w.

Bei dem konkreten Problem, aus dem dieses Beispiel hervorgegangen ist, lief das Programm nach dem Entfernen der zwei Indirektionen 10 mal schneller!

Zweitens ist zu erkennen, dass die zurück gegebenen Variablen aus 4 double Werten besteht, aber nur zwei Kopieraktionen zu erkennen sind. Von dem Register rdx, nach xmm0, nach rdi. Und noch einmal mit einem Offset von 16Byte (2 double Werte). Der Compiler hat erkannt, dass SIMD angewendet werden kann. Das Kopieren von zwei Werten mit einer Instruktion. Und das ohne zusätzlichen Bemühungen meinerseits C++ Code extra SIMD tauglich zu schreiben!

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress