x86環境用にアライメント合わせるのが壊滅的に難しい件

やねうら王miniをx86(32bit環境)でコンパイルが通るように作業してたのですが、どうもアライメントを合わせるのが壊滅的に難しいです。

SSE命令を使うときに16byte単位にアライメントが合っていないと実行時にエラーになるものがあります。_mm_store_si128()等です。

まあそれはそれで仕方ないのでアライメントを合わせようとBitboardなどのstructにalignas(16)をつけたわけです。

ところが、ヒープから割り当てたときにはこれがアライメントされていない可能性があるようです。

なぜx64環境でこの問題が顕在化しないかというと、x64環境ではnewは16byteにアライメントされたポインターが返るからで、x86環境は8byte単位にアライメントされたポインターが返ることが原因のようです。(コンパイルオプションでどうにかならないのかな?)

てか、何故、コンパイラーがそのことを知っていて、警告を出してくるのか不思議ですが、きっと、何か泥臭いコードが書いてあるのでしょう。

それはそれとして次のようなallocatorを書きました。

そもそもヒープから取ってくるときもalignasつけてるならそれを考慮して欲しいわけですけど。こんなallocatorを書かないといけないのはマジ勘弁です。std::align()みたいな関数は要らないので、aligned_newでも用意していただきたいものです。

これでめでたしめでたしかと思ったら、Tはvirtual関数を含むので仮想関数テーブルへのポインタの分(x86環境では4バイト)、ここで返された値が増えてしまうのです。あれれ..せっかくalignしてポインターを返してるのにな…。どうなるのだ、これ。alignasがついてるから、アドレスの下位bitを切り捨てるなどしてそこはアライメントが合ってることが保証されるのか…。そうか…。

それはそうと、AVX256のstore命令(_mm256_store_si256)等で32バイトでアライメントを要求してくる命令があるんですけど…。その場合は、上のallocatorが必要になります。

それで、そこまではさっと直せたんですけど、x86用にコンパイルした実行ファイル、スレッド生成のところで落ちるので、ランタイムとか追いかけてたのですが、どうもスタックのサイズを400MBにしているとこれが大きすぎるのか落ちるようです。協力詰めsolverで4万手とかの作品を解くのに必要だったので大きくしたのですが、400MBは32bit環境では大きすぎたようです。VC++のランタイムのバグ(?)のような気がしなくもないですが…。

とりあえずスタックサイズは100MBに変更するとうまく動きました。
ちなみにx86だと指し手生成などはx64のときの半分ぐらいの速度しか出ません。悲しい…。

2016/01/18 12:00 追記

コメント欄で教えていただきました。

std::thread* th = new (_mm_malloc(sizeof(T),alignof(T))) T();

のように、_mm_malloc() ~_mm_free()を使うと一発で書けるようです。上のコード要らんかったのか!!なにこれ…。

x86環境用にアライメント合わせるのが壊滅的に難しい件」への11件のフィードバック

  1. >CSAライブラリ、特定のバージョンを指定して申請しないといけないのが割とオープンソースのプロジェクトと相性悪い感じある(`・?・´)(@平岡)
    >登録は随時可能ですが、5月の選手権で使えるのは1月31日登録分までですね(かず@なのは)

    想定外はいつものこと。
    そうして、締め切りに追われるのは人気作家の宿命です。

    最後の追い込み、がんばってください。
    きっとサンタさん達も応援してくれますよ。

    https://twitter.com/yaneuraou/status/688667958785146880?ref_src=twsrc%5Etfw

  2. >x86用にコンパイルした実行ファイル、スレッド生成のところで落ちるので、ランタイムとか追いかけてたのですが、どうもスタックのサイズを400MBにしているとこれが大きすぎるのか落ちるようです。

    何スレッド起動しようとしたのでしょうか?
    6スレッド目でメモリアドレス使用量が2GBをこえて落ちるのは必然な気がするのですが……
    (/LARGEADDRESSAWAREをコンパイルオプションで指定していれば64bitOSでは4GBまで使えるので落ちるのは11スレッド目までは引っ張れますが)

    • 4スレッド起動してたのですが、確かにメモリが確保できないんですね…。Windows10なので実際にアクセスに行くまではタスクマネージャーで見ても使用しているようには見えなくて(Windows10のメモリマネージメント、賢い!)、気づきませんでした。なるほどです。

  3. new (_mm_malloc(sizeof(T),alignof(T))) T();

    ほんと、ナニコレー ですわ。
    AVX使うと 32 にアラインメントされていない とかなんとかー が出て困っとったんです。
    おおきにー!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です