kubo39's blog

ただの雑記です。

C言語のmainでめちゃくちゃできる、ってネタみたけど別にDでもできるよな。 main is usually a function はでないけど。

extern (C) void main() pure nothrow @nogc
{
    asm pure nothrow @nogc
    {
        naked;
        db 0x31; db 0xC0;  // xor EAX, EAX;
        db 0xFF; db 0xC0;  // inc EAX;
        db 0xC3;           // retq;
    }
}

これでGNU coreutilsのfalseコマンド的な動作になる。

64bitだとxor EAX, EAX は嘘でmov RAX, 0 のほうがいいかもしれないのでみなさんはちゃんとしてください(?)

druntimeのcore.syncについて

いろいろいけてないよな。

  • Mutexの実装がデフォルトでre-entrant
  • 条件変数およびセマフォタイムアウトつきwaitがmonotonicなclockじゃなくシステムクロック
    • なので、システムの時刻変更によって予期せぬ振る舞いが起こりえる

このあたりはC++ではあたりまえに認知されている(デフォルトのmutex実装と別にrecursive mutexを用意、タイムアウトつきの待ちではstd::chrono::steady_clock使う)のと、Rustも実装では同じようになっているのになぜDは微妙な実装になってるのだろう。

ErupteDを使ってDからVulkan触ろうと思ったけどできなかった話

最近しょうもない記事ばっかり書いてるけど、無職だとこうやってメリハリつけないとだめかなーと思っての行為なので許容して。

DでVulkanさわろうとおもうと https://github.com/ParticlePeter/ErupteD というライブラリが一番スターついてるっぽいのでこれを使うことにする。

もろもろ必要そうなやつを入れて

$ apt install libvulkan-dev vulkan-utils

git cloneしたeruptedでexampleを動かそうとしたら、無事に死んだ。

$ dun run :devices
(...)
erupted:devices 2.0.15+v1.1.91: building configuration "application"...
Linking...
Running ./erupted_devices 
object.Exception@examples/devices.d(13): VK_ERROR_INCOMPATIBLE_DRIVER
----------------
/home/kubo39/dlang/dmd-2.083.0/linux/bin64/../../src/phobos/std/exception.d:515 pure @safe void std.exception.bailOut!(Exception).bailOut(immutable(char)[], ulong, scope const(char)[]) [0x7b31f62]
/home/kubo39/dlang/dmd-2.083.0/linux/bin64/../../src/phobos/std/exception.d:436 pure @safe bool std.exception.enforce!().enforce!(bool).enforce(bool, lazy const(char)[], immutable(char)[], ulong) [0x7b31ede]
examples/devices.d:13 void devices.enforceVK(erupted.types.VkResult) [0x7b2c6c4]
examples/devices.d:34 _Dmain [0x7b2c80a]
Program exited with code 1

手元の環境だとだめそうですね。

LDC向けのSIMDライブラリを作っているけど、いろいろつらい

x86intrin

勉強がてらにLDCでx86intrinsicっぽくSIMDを書けるライブラリを書いてみている。まだまだ全然途中なので使わないでください。

github.com

_m128iとかじゃなくてそのままbyte16とかを直接 _mm_XXX の引数にとったりするようにしてるけど、将来的にはたぶん変えます。

そういう作業をしてる中でいくつか問題にあたったので、書き残しておきます。

LDCでunittestブロックとDFLAGSの指定を共存できない

そういう問題があるので dub test --compiler だと DFLAGS="-mattr=+sse4.2 つけてもsse4.2環境のテストができない。別にスクリプトを作ってそっちを実行するようにして回避。

intrinsicがundefined reference

clangやrustと同じバージョンのLLVMを使っているけどLDCだけundefined referenceといわれるintrinsicがあったりする。cvt系とか全部死亡です、ありがとうございました。

Comment out convert intrinsics since undefined reference error · kubo39/x86intrin@4c831f9 · GitHub

普通の関数がundefined reference

sse2でintrinsic使ってない関数を呼び出そうとしたらundefined referenceで無事死亡。

おわりに

LDCSIMDけっこうつらいのではないか

perfを使ってD言語のプロファイリング結果をみる

まず手元の /proc/sys/kernel/perf_event_paranoid を確認する。Ubuntuとかだと最弱設定になってるので変えとく。

$ cat /proc/sys/kernel/perf_event_paranoid
3
$ sudo sh -c 'echo 1 > /proc/sys/kernel/perf_event_paranoid'

普通に perf record にかける。

$ perf record dmd fib.d
[ perf record: Woken up 1 times to write data ]
[kernel.kallsyms] with build id ef8b1ed123757213c70bca103ce70b59825d9c11 not found, continuing without symbols
[ perf record: Captured and wrote 0.052 MB perf.data (1240 samples) ]

perf report で出力するときに ddemangle に食わせるとsymbol nameがdemangleされるので便利。

$ perf report| ddemangle| head -20
[kernel.kallsyms] with build id ef8b1ed123757213c70bca103ce70b59825d9c11 not found, continuing without symbols
# To display the perf.data header info, please use --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 1K of event 'cycles:uppp'
# Event count (approx.): 706573056
#
# Overhead  Command   Shared Object            Symbol                                                                                                                                                                                                                                                
# ........  ........  .......................  ......................................................................................................................................................................................................................................................
#
    12.51%  ld        libbfd-2.30-system.so    [.] bfd_hash_lookup
     7.98%  ld        libc-2.27.so             [.] __gconv_transform_utf8_internal
     5.34%  dmd       dmd                      [.] void dmd.lexer.Lexer.scan(dmd.tokens.Token*)
     3.80%  ld        libc-2.27.so             [.] __strcmp_sse2_unaligned
     3.50%  ld        libc-2.27.so             [.] __mbsrtowcs_l
     3.32%  ld        x86_64-linux-gnu-ld.bfd  [.] walk_wild_section_general
     3.31%  ld        libc-2.27.so             [.] internal_fnwmatch
     3.25%  ld        x86_64-linux-gnu-ld.bfd  [.] walk_wild_section_specs3_wild2
     2.81%  ld        libc-2.27.so             [.] __strnlen_avx2

まあここまでは別によい。

dmd -profile と perf(oprofile) の使い分けってどうなの?みたいなの聞かれたときに dmd -profile はLinuxだとrbtsc命令使っててコア間同期がうんたら〜みたいな話をしてしまって、まあそれは間違いじゃないのだけれど、実際そもそもdmd -profileはtracing方式(すべてのDの関数コールにフックする)のに対しperfはsampling方式(タイマーの間隔を決めて現在のスタックの状態を収集してどの関数が統計的に多くよばれてるか)という違いがあり、用途によって使い分けられるという話をすべきだったなー、と思った。(長い)

でも大体のケースはperfでいいんじゃないのかな、みたいな。特にsampling方式だと実際に負荷がかかってる本番環境とかでも計測できるのが嬉しい。実はこのパスがめっちゃ呼ばれてる、みたいなのとかは開発環境じゃわからなかったりするので。

D言語で一ヶ月前を表すとき

日付の扱いは忘れそうなのでメモ。

一ヶ月前を表す場合、可能であれば前月の同日同時刻を表し、前月に同日が存在しない場合は差分を計算して付け足す。

import std.datetime;
import std.stdio;

void main()
{
    auto currTime = Clock.currTime(UTC());
    writeln(currTime);                              //  2018-Sep-30 20:12:00.0198441Z
    writeln(currTime.add!"months"(-1));  // 2018-Aug-30 20:12:00.0198441Z
    currTime = Clock.currTime(UTC());
    writeln(currTime.add!"months"(1));  // 2018-Oct-30 20:12:00.0199153Z

    auto date = Date(2018, 7, 31);
    writeln(date.add!"months"(-1));  // 2018-Jul-01
    date = Date(2018, 7, 30);
    writeln(date.add!"months"(-1));  // 2018-Jun-30
}

なので3月とかはこうなる。

import std.datetime;
import std.stdio;

void main()
{
    auto date = Date(2018, 3, 31);
    writeln(date.add!"months"(-1));  // 2018-Mar-03
    date = Date(2018, 3, 29);
    writeln(date.add!"months"(-1));  // 2018-Mar-01

    // うるう年は考慮される
    date = Date(2016, 3, 29);
    writeln(date.add!"months"(-1));  // 2016-Feb-29
}

D言語のprecise(正確な) GC

(追記: 編集あり)

実は結構前に保守的なGCから正確なGCになって いた いなかった。仕組みはマーク・アンド・スィープで変わらないが性能は大幅に向上が期待できそう。

github.com

これによって、

  • メモリ確保時
    • allocatorの使い分けによる効率的なメモリ管理(それぞれのアロケータの特性が活かせる)、これは保守的GCでは安全側に倒さざるをえなかったので最大のメリットになる
    • スレッドローカルキャッシュを使うことでグローバルなGCロックを避けられる
  • ゴミ集め時
    • 並列にスキャンするようになったのでパフォーマンスが向上
    • 特定の条件を満たしていればストップ・ザ・ワールド(STW)が発生しなくなる
      • 例えば新たに追加された型レベルの仕組み isolated によって静的にスレッドローカルなヒープのみを使っているかを判定(isolatedはスレッドローカルなヒープにつく型といえる)でき、isloatedしかないと判定したときはSTWしない

わりとGoに近い感じで、アロケータの柔軟性でGoより優れていて、インクリメンタルGCによるレイテンシの仕組みはシステムプログラミング言語という特性上入れなかったという感じか。 さすがにJVMに比べると厳しいが、これまでの保守的GCに比べるとずいぶんよくなっている、はず。

(追記ここから)

まだマージされてなかった。。すまんな、、