# mpbench — 多倍長計算ライブラリ性能評価のコード一式 記事「多倍長数値計算の性能評価」で使ったベンチマークコードと測定データ。測定仕様は `common/SPEC.md`、共通乱数は `common/xorshift.md`。 ``` benchmark_misc/ ├── common/ 共通仕様 (SPEC.md), 共通乱数 (xorshift.md, xorshift_ref.py) ├── cpp/ C/C++: FLINT (eig, fft), MPLAPACK (eig, gemm), Eigen+mpreal, MPFR+OpenMP FFT, スカラー演算, Makefile ├── julia/ Julia: GenericSchur, GenericLinearAlgebra, MultiFloats, DoubleFloats, Arblib, GenericFFT, FFTW ├── python/ Python: mpmath (+gmpy2), python-flint ├── matlab/ MATLAB + Advanpix: advanpix_bench.m (課題 A/B/C), advanpix_scaling.m (時間・メモリ vs N), advanpix_dense.m (密行列, MPLAPACK と同条件) ├── wolfram/ Mathematica (wolframscript) ├── scripts/ env_info.sh, run_all.sh, analyze.py, run_advanpix_scaling.sh, fit_advanpix.py └── results/ results.csv (全測定値), env.txt (測定環境) ``` すべてのスクリプトは同一スキーマの CSV(`timestamp,host,library,version,language,task,subtask,digits,n,threads,trial,time_s,check_value,notes`)に追記し、`scripts/analyze.py` が表(`summary.md`)と図(`fig_*.png`)を生成する。 ## 準備(Debian / Ubuntu 系の例) ### システムパッケージ ``` apt install build-essential libgmp-dev libmpfr-dev libmpc-dev libflint-dev libeigen3-dev libboost-dev libfftw3-dev python3-pip ``` `libflint-dev` は 3.0 以上(arb 統合後)が必要。QD(double-double / quad-double)は MPLAPACK 同梱のものを使うので `libqd-dev` は不要(下記)。 ### mpreal.h(Eigen と scalar_bench で使用) Pavel Holoborodko 氏の mpreal.h(http://www.holoborodko.com/pavel/mpfr/ 、GPL)を `cpp/mpreal/` に置く。 ``` git clone https://github.com/advanpix/mpreal.git cpp/mpreal ``` ### MPLAPACK 3.0.0 ``` wget https://github.com/nakatamaho/mplapack/releases/download/v3.0.0/mplapack-3.0.0.tar.xz tar xf mplapack-3.0.0.tar.xz && cd mplapack-3.0.0 ./configure --prefix=$HOME/opt/mplapack --enable-gmp=yes --enable-mpfr=yes --enable-dd=yes --enable-qd=yes --enable-binary128=yes --enable-double=yes --enable-optimization=yes --enable-gmpfrxx-mkII --with-system-gmp=yes --with-system-mpfr=yes --with-system-mpc=yes --with-cxx-standard=17 make -j$(nproc) && make install ``` 注意点: - configure は 1 行で実行する(`\` 継続と行末コメントを混ぜると `#` が引数に渡って `invalid host type: #` になる)。 - `--with-system-qd` は付けない。dd/qd の複素型(`dd_complex`, `qd_complex`)は MPLAPACK 同梱の QD にしか無く、システムの libqd では `qd/qd_complex.h` が見つからずビルドが失敗する。 - `--enable-optimization` で OpenMP 版の `libmplapack__opt.so` ができる。3.0 では BLAS と LAPACK が一つのライブラリにまとまっており(`libmpblas_*` は無い)、`cpp/Makefile` はそれに合わせてある(`MPL_OPT=` を付けると参照版にリンク)。 - gmpfrxx_mkII の GMP バックエンドは `mpf_set_default_prec` を無視する。精度は `gmpxx::set_default_mpf_precision_bits(bits)`(MPFR は `mpfrxx::set_default_precision_bits`)で設定する。`cpp/mplapack_eig.cpp` はそのようにしてある。 - 型名は `mpfr_class` / `mpc_class`(MPFR)、`mpf_class` / `mpfc_class`(GMP)、`mplapack_binary128_t`(binary128、ヘッダは `mpblas_binary128.h`)。 ### Julia パッケージ ``` julia -e 'using Pkg; Pkg.add(["GenericSchur","GenericLinearAlgebra","MultiFloats","DoubleFloats","Arblib","GenericFFT","FFTW"])' ``` MultiFloats は `MultiFloat{Float64,2}` と `{Float64,4}` を使う(N=8 は算術が未定義のバージョンがある)。超越関数は `MultiFloats.use_bigfloat_transcendentals()` で MPFR にフォールバックさせている。 ### Python パッケージ ``` pip3 install mpmath gmpy2 python-flint pandas matplotlib tabulate ``` (mpmath は gmpy2 があると自動でバックエンドに使う。python-flint は conda より pip の方が確実。tabulate は analyze.py の Markdown 表出力に必要) ### MATLAB / Mathematica Advanpix のパスは `~/Documents/MATLAB/startup.m` で `addpath` しておく(ベンチマークの関数ファイルの先頭に書くと「Local function name must be different from the script name」になる)。`matlab` と `wolframscript` は PATH に無ければ `MATLAB=...`、`WOLFRAMSCRIPT=...` の環境変数で `run_all.sh` に渡す。 ## ビルド ``` make -C cpp flint mpfrfft scalar eigen MPREAL_DIR=./mpreal HAVE_BOOST=1 make -C cpp mplapack mplapack_gemm MPLAPACK=$HOME/opt/mplapack ``` ## 実行 ``` bash scripts/env_info.sh # 環境記録 → results/env_host>.txt bash scripts/run_all.sh smoke # 全スクリプトの疎通確認 (数分) bash scripts/run_all.sh scalar # 課題 A bash scripts/run_all.sh fft # 課題 C bash scripts/run_all.sh eig # 課題 B (長時間。下記の環境変数で分割する) python3 scripts/analyze.py # results/summary.md と fig_*.png ``` `run_all.sh` の環境変数(既定値): | 変数 | 意味 | |---|---| | `THREADS=1,2,4,8,16,32,64,` | FLINT / Julia / Advanpix のスレッド数スイープ(Julia の eig と fft はスレッドを使わないので最初の段だけ) | | `MPL_THREADS=1,8,` | MPLAPACK のスレッド数(Rgeev/Cheev は並列 BLAS の恩恵をほぼ受けないので段数を絞ってある) | | `N_EIG=10,20,50,...,1000` / `N_FFT=...` / `DIGITS=100,1000` / `TRIALS=3` | サイズ・桁数・試行回数 | | `ONLY=mplapack,flint,julia,advanpix,seq` | 回すライブラリ群を絞る(`seq` = Eigen, GenericLinearAlgebra, mpmath, Mathematica) | | `VERIFIED=` | FLINT の保証付き固有値(`eig_multiple`)を省く(1000 桁 N=300 で 3 時間かかる) | 例: `ONLY=advanpix THREADS=32,64 N_EIG=500,700,1000 bash scripts/run_all.sh eig` ### Advanpix の時間・メモリ vs N と見積り式 ``` MATLAB=... HERM=1 bash scripts/run_advanpix_scaling.sh # 1 点 1 プロセスで N=100〜2000 (既定 64 スレッド) python3 scripts/fit_advanpix.py results/advanpix_scaling_.csv # フィットと見積り表 → results/advanpix_fit.md, fig_advanpix_*.png ``` ### MPLAPACK の Rgemm / Rgehrd / Rgeev(密行列)と Advanpix の同条件比較 ``` for t in 1 8 64; do OMP_NUM_THREADS=$t cpp/mplapack_gemm --digits 100,1000 --n 300,1000 --threads $t --trials 1 --out results/results_.csv; done matlab -batch "addpath('matlab'); advanpix_dense('digits',[100 1000],'n',[300 1000],'threads',[1 8 64],'out','results/results_.csv')" ``` ### 個別実行の例 ``` OMP_NUM_THREADS=8 cpp/mplapack_eig_mpfr --digits 100 --n 100,200 --threads 8 --check --herm cpp/flint_eig --digits 100,1000 --n 10,50,100 --threads 1 --verified cpp/mpfr_fft_omp --digits 1000 --n 1048576 --threads 16 julia -t 1 julia/eig_bench.jl --libs genericschur,multifloats --digits 100 --n 100 --herm python3 python/eig_bench.py --digits 100 --n 100 --verified --herm matlab -batch "addpath('matlab'); advanpix_bench('task','eig','digits',100,'n',[100 200],'threads',8,'herm',true)" wolframscript -file wolfram/bench.wls task=fft digits=100 n=65536 ``` すべてのスクリプトは `--smoke`(MATLAB は `'smoke',true`、Wolfram は `smoke=1`)で最小設定の動作確認ができる。 ## 測定上の注意(記事本文の「落とし穴」も参照) - スカラー演算と gemm の入力は sqrt(u) で仮数を目標精度いっぱいに埋めている。倍精度からの変換値をそのまま使うと arb や MPFR が短い仮数で速く見える。 - Circus 行列は上 Hessenberg 形なので、対角化の時間に Hessenberg 化は含まれない。 - Advanpix は物理コア数を超えるスレッド(SMT)で大きく遅くなる。`maxNumCompThreads` を物理コア数にする。 - MPLAPACK の `_opt` 版は小さい行列でスレッド起動のオーバーヘッドが支配的になるので、`OMP_NUM_THREADS` を明示する(未設定だと全論理コアを使う)。 - Julia の GenericSchur と GenericLinearAlgebra は同時に読み込むと衝突するので別プロセスで走らせる(`run_all.sh` は分けてある)。