憂鬱な午後のひととき

9月3日_

macOSでmimallocを使う

ラボユースの人に, 開発してるellipticCurve/test.cppがMacよりも, そのMac上のvirtualマシン上のUbuntu上で動かす方が速いんですが何故でしょうと聞かれた.

私も試してみたら再現した(MacBook Pro 13インチ2018 2.3GHz Core i5, VirtualBoxではなくdockerを利用).

Apple LLVM version 10.0.1 (clang-1001.0.46.4)
on mac(素のMac)
>g++ -O3 -DNDEBUG test.cpp -lgmpxx -lgmp && ./a.out
[*] order test: OK
[*] Fp isEq test1: OK
[*] Fp isEq test2: OK
[*] Fp benchmark
        time = 0.288760usec
[*] EC benchmark
        time = 5.346814usec

Ubuntu 18.04 on docker(19.03.1) on mac(Mac上のUbuntu)
gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)
>g++ -O3 -DNDEUBG test.cpp -lgmpxx -lgmp && ./a.out
[*] order test: OK
[*] Fp isEq test1: OK
[*] Fp isEq test2: OK
[*] Fp benchmark
        time = 0.155400usec
[*] EC benchmark
        time = 2.898436usec

上記プログラムはGMPを使ってるのだけれども, 計算主体のプログラムで2倍速度が違うというのは尋常ではない. GMPのバージョンは同じ. mpz_classを多用しているのでどうもMacのreallocが遅いようだ(instrumentsで調べたら一番食ってた). それでMicrosoftのmimallocを試したらどうだろうと思ったけどダイナミックリンクを使う方法はMac上でうまく動かなかった(cf dynamic overriding on macOS seems broken). 仕方がないのでプログラム上で直接mimallocを使うように修正する.

GMPはmp_set_memory_functionsを使うとメモリ関数を入れ換えられる. とりあえずやっつけでこんなコードを追加した. dynamicリンクは嫌なのでlibmimalloc.aを直接リンクする.

#ifdef USE_MIMALLOC
/*
    g++ -O3 -DNDEBUG -I <mimalloc>/include <mimalloc>/build/libmimalloc.a -lpthread -lgmpxx -lgmp
*/
#include <gmp.h>
#include <mimalloc.h>
static struct UseMiMalloc {
    static void* mi_realloc_wrapper(void *p, size_t, size_t n)
    {
        return mi_realloc(p, n);
    }
    static void mi_free_wrapper(void *p, size_t)
    {
        mi_free(p);
    }
    UseMiMalloc()
    {
        puts("set GMP memory functions before using mpz_class");
        mp_set_memory_functions(mi_malloc, mi_realloc_wrapper, mi_free_wrapper);
    }
} g_UseMiMalloc;
#endif

GMPが要求するreallocとfreeの引数が異なるのでwrapper関数を渡す. staticなmpz_classが作られる前に先に実行しておかないといけない. そうするとMac上でも速くなった.

malloc比較
Mac標準 Ubuntu on Mac Mac標準 + mimalloc Ubuntu on Mac + mimalloc
Fp 0.289 0.155 0.126 0.152
Ec 5.347 2.898 2.291 3.014

Ubuntu on Macのmimallocは気持ち遅くなってるようだ. 別のUbuntuマシン(not Mac Core i7-7700)で試したらmimallocの方が速かった(多分こちらのほうが正常な挙動なのだろう). Linuxでは-lpthreadのリンクも必要だった.

malloc比較2
Ubuntu Ubuntu + mimalloc
Fp 0.088 0.067
Ec 2.068 1.881

今回, malloc単体の速度は調べてないのだけれどもMac標準のmallocは遅すぎる印象(ちゃんと調べるべき). tcmallocだとどうだろう. MacとLinuxを使っていて同じプログラムなのになんか遅いなと思ったらmallocを調べるとよいかもしれない.


御意見は光成滋生<herumi@nifty.com>までお願いします