kubo39's blog

ただの雑記です。

LDCの最適化とか

http://robert.ocallahan.org/2017/04/rust-optimizations-that-c-cant-do_5.html これを読んで、なるほどRustだとこういう最適化できるのね、というのを学んだ。

Here it’s really clear that the semantics of Rust are making this optimization possible. In C++ v could be a reference to a global variable which is modified by the callback function, in which case hoisting the load would be incorrect.

とあるようにC++だとvがグローバル変数への参照として渡ってきて、callbackの中でそのグローバル変数の書き換えが起こるかもしれない。 なんで、 [qword ptr [r15]] って感じでメモリをloadしにいく必要がある。(下にあるLink-Time Optimizationうんぬんは無視する)。 まあそのへんはsharedを型に使ってるD言語LDCあたりだとどうなるんだろう(まあなんとなくC++と同じ気はする、グローバル変数触らないという制約はもてない気する)と思ってざっとみてみる。

こういうコードを書く。

ulong foo(ref ulong v, void function() callback)
{
    auto sum = 0;
    foreach (_; 0 .. 100)
    {
        sum += v;
        callback();
    }
    return sum;
}

ldc2 -O -c -ofldc.o hoge.d でコンパイルした結果:

...
  20:   41 03 1e                add    (%r14),%ebx
  23:   41 ff d7                callq  *%r15
  26:   48 63 db                movslq %ebx,%rbx
  29:   ff cd                   dec    %ebp
  2b:   75 f3                   jne    20 <_D4hoge3fooFKmPFZvZm+0x20>

ループ内部はこんな感じになっていて、まあ期待(?)した結果になった。Intel記法だとdword ptrとかだしてくれるんでそっちしたほうがよかったかもしれない。

ただRust側で addとcallが並び変わってる理由がよくわかってない。

追記

pure を使えばいけるかも、と思ったが

$ ldc2 -O -c -ofldc.o hoge.d
hoge.d(7): Error: pure function 'hoge.foo' cannot call impure function pointer 'callback'

のように怒られてしまった。

ちょっと書き換えてみる。

ulong foo(ref ulong v, int function() pure nothrow @safe @nogc callback)
{
    auto sum = 0;
    foreach (_; 0 .. 100)
    {
        sum += v;
        callback();
    }
    return sum;
}

それでもだめなようだ

$ ldc2 -O -c -ofldc.o hoge.d
$ objdump -Mintel  -Cd ldc.o  # Intel記法にした
...
  20:   41 03 1e                add    ebx,DWORD PTR [r14]
  23:   41 ff d7                call   r15
  26:   48 63 db                movsxd rbx,ebx
  29:   ff cd                   dec    ebp
  2b:   75 f3                   jne    20 <_D4hoge3fooFKmPFNaNbNiNfZiZm+0x20>