高速化のためにデータ構造を変更している SDPA 7.0.5.rev を用いて、gcc(g++)のコンパイラオプションと実行速度の関係を調べてみた。以前 GMP(任意精度ライブラリ)では、あまり多くのオプションを付けると明らかに速度が落ちた。マシンは Intel Core2(E6600 : 2.4GHz), OS は Fedora 8 (64bit), gcc は 4.2.2, GotoBLAS は 1.20 を -O2 で最適化したものを用いる。
0: -O0 (最適化なし)
1: -O1
2: -O2
3: -O3
4: -O2 -march=nocona
5: -O2 -march=nocona -msse3 -mfpmath=sse
6: -O2 -march=nocona -msse3 -mfpmath=sse -funroll-all-loops
7: -O3 -march=nocona -msse3 -mfpmath=sse -funroll-all-loops
●データ1: mcp1000-10
0: 38.57s
1: 38.14s
2: 35.89s
3: 35.89s
4: 35.92s
5: 35.98s
6: 35.94s
7: 35.97s
データ1は以下のように全体計算時間のほとんどが BLAS(GotoBLAS)で占めるという典型的な外注タイプで SDPA 本体のする仕事量は少ない。だからコンパイラオプションは何でもあまり変化はないが、この場合では単純に -O2 で十分。-funroll-all-loops を付けるとやや遅くなる。
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls s/call s/call name
68.48 22.53 22.53 dgemm_kernel
7.20 24.90 2.37 dgemv_n
3.34 26.00 1.10 daxpy_k
3.16 27.04 1.04 dcopy_k
2.64 27.91 0.87 dtrsm_kernel_LT
2.31 28.67 0.76 dgemm_otcopy
2.10 29.36 0.69 dgemm_oncopy
1.70 29.92 0.56 dtrsm_kernel_RN
1.28 30.34 0.42 dtrmm_kernel_LN
1.25 30.75 0.41 dgemm_beta
1.22 31.15 0.40 15 0.03 0.03 compute_bMat_dense_SDP_C
1.00 31.48 0.33 dscal_k
0.91 31.78 0.30 dgemv_t
0.82 32.05 0.27 ddot_
●データ2: theta5
0: 32.46s
1: 28.43s
2: 27.93s
3: 28.32s
4: 27.96s
5: 27.97s
6: 28.04s
7: 28.05s
データ2も dgemm_kernel の占める部分が大きいが、compute_bMat_dense_SDP_C という関数が上位に現れる。これは関数 calF3_C の宣言に static inline を指定しているので名前が消えているが、実際には calF3_C の時間が含まれている。これも -O2 で十分。
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls s/call s/call name
58.94 15.10 15.10 dgemm_kernel
17.25 19.52 4.42 18 0.25 0.25 compute_bMat_dense_SDP_C
8.70 21.75 2.23 dtrsm_kernel_RN
5.97 23.28 1.53 dgemm_otcopy
3.16 24.09 0.81 74 0.01 0.01 sdpa::DenseMatrix::setZero()
1.95 24.59 0.50 dgemv_n
1.29 24.92 0.33 dgemv_t
0.55 25.06 0.14 18684 0.00 0.00 dtrsm_oltncopy
0.27 25.13 0.07 54504 0.00 0.00 calF2_C
0.23 25.19 0.06 13 0.00 0.00 sdpa::DenseMatrix::initialize(int, int, sdpa::DenseMatrix::Type)
0.20 25.24 0.05 19296 0.00 0.00 dsyrk_kernel_L
0.20 25.29 0.05 dtrmm_kernel_LN
●データ3: mater-5
0: 63.05s
1: 28.93s
2: 26.60s
3: 26.55s
4: 27.04s
5: 27.04s
6: 23.88s
7: 23.83s
データ3は疎な問題なので、Sparse Cholesky 分解を用いる。Cause_Fillin は前処理なので省いてみると、getCholesky が多くを占める。この場合には -funroll-all-loops があった方が良いようだ。
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls s/call s/call name
39.15 15.27 15.27 1 15.27 15.27 sdpa::UTSpMatCst::Cause_Fillin(sdpa::UTSpMat&, sdpa::MTS_Graph&)
29.80 26.89 11.62 27 0.43 0.43 sdpa::Lal::getCholesky(sdpa::SparseMatrix&, int*)
3.46 28.24 1.35 dgemm_kernel
3.31 29.53 1.29 1 1.29 1.29 sdpa::UTSpMat::Form_Aggregate(sdpa::InputData&, int)
2.49 30.50 0.97 daxpy_k
2.36 31.42 0.92 15355388 0.00 0.00 sdpa::Lal::getInnerProduct(double&, sdpa::SparseMatrix&, sdpa::DenseMatrix&)
2.26 32.30 0.88 131652 0.00 0.00 dsterf_
1.21 32.77 0.47 27 0.02 0.10 sdpa::Newton::compute_bMat_sparse_SDP(sdpa::InputData&, sdpa::Solutions&, sdpa::Wor
kVariables&, sdpa::ComputeTime&)
1.18 33.23 0.46 3843398 0.00 0.00 sdpa::Lal::plus(sdpa::DenseMatrix&, sdpa::DenseMatrix&, sdpa::SparseMatrix&, double
*)
1.08 33.65 0.42 54 0.01 0.01 sdpa::Lal::let(sdpa::Vector&, char, sdpa::SparseMatrix&, char, sdpa::Vector&)
よって、以下のようなオプションが良いでのはないかという推測が出てくるが、実際にこれで make してみると以下のような結果になる。データ1だと少しだけ遅くなるが、SDPA に関してはこれで十分なようだ。
8: -O2 -funroll-all-loops
データ1:35.94s
データ2:27.92s
データ3:23.74s
0: -O0 (最適化なし)
1: -O1
2: -O2
3: -O3
4: -O2 -march=nocona
5: -O2 -march=nocona -msse3 -mfpmath=sse
6: -O2 -march=nocona -msse3 -mfpmath=sse -funroll-all-loops
7: -O3 -march=nocona -msse3 -mfpmath=sse -funroll-all-loops
●データ1: mcp1000-10
0: 38.57s
1: 38.14s
2: 35.89s
3: 35.89s
4: 35.92s
5: 35.98s
6: 35.94s
7: 35.97s
データ1は以下のように全体計算時間のほとんどが BLAS(GotoBLAS)で占めるという典型的な外注タイプで SDPA 本体のする仕事量は少ない。だからコンパイラオプションは何でもあまり変化はないが、この場合では単純に -O2 で十分。-funroll-all-loops を付けるとやや遅くなる。
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls s/call s/call name
68.48 22.53 22.53 dgemm_kernel
7.20 24.90 2.37 dgemv_n
3.34 26.00 1.10 daxpy_k
3.16 27.04 1.04 dcopy_k
2.64 27.91 0.87 dtrsm_kernel_LT
2.31 28.67 0.76 dgemm_otcopy
2.10 29.36 0.69 dgemm_oncopy
1.70 29.92 0.56 dtrsm_kernel_RN
1.28 30.34 0.42 dtrmm_kernel_LN
1.25 30.75 0.41 dgemm_beta
1.22 31.15 0.40 15 0.03 0.03 compute_bMat_dense_SDP_C
1.00 31.48 0.33 dscal_k
0.91 31.78 0.30 dgemv_t
0.82 32.05 0.27 ddot_
●データ2: theta5
0: 32.46s
1: 28.43s
2: 27.93s
3: 28.32s
4: 27.96s
5: 27.97s
6: 28.04s
7: 28.05s
データ2も dgemm_kernel の占める部分が大きいが、compute_bMat_dense_SDP_C という関数が上位に現れる。これは関数 calF3_C の宣言に static inline を指定しているので名前が消えているが、実際には calF3_C の時間が含まれている。これも -O2 で十分。
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls s/call s/call name
58.94 15.10 15.10 dgemm_kernel
17.25 19.52 4.42 18 0.25 0.25 compute_bMat_dense_SDP_C
8.70 21.75 2.23 dtrsm_kernel_RN
5.97 23.28 1.53 dgemm_otcopy
3.16 24.09 0.81 74 0.01 0.01 sdpa::DenseMatrix::setZero()
1.95 24.59 0.50 dgemv_n
1.29 24.92 0.33 dgemv_t
0.55 25.06 0.14 18684 0.00 0.00 dtrsm_oltncopy
0.27 25.13 0.07 54504 0.00 0.00 calF2_C
0.23 25.19 0.06 13 0.00 0.00 sdpa::DenseMatrix::initialize(int, int, sdpa::DenseMatrix::Type)
0.20 25.24 0.05 19296 0.00 0.00 dsyrk_kernel_L
0.20 25.29 0.05 dtrmm_kernel_LN
●データ3: mater-5
0: 63.05s
1: 28.93s
2: 26.60s
3: 26.55s
4: 27.04s
5: 27.04s
6: 23.88s
7: 23.83s
データ3は疎な問題なので、Sparse Cholesky 分解を用いる。Cause_Fillin は前処理なので省いてみると、getCholesky が多くを占める。この場合には -funroll-all-loops があった方が良いようだ。
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls s/call s/call name
39.15 15.27 15.27 1 15.27 15.27 sdpa::UTSpMatCst::Cause_Fillin(sdpa::UTSpMat&, sdpa::MTS_Graph&)
29.80 26.89 11.62 27 0.43 0.43 sdpa::Lal::getCholesky(sdpa::SparseMatrix&, int*)
3.46 28.24 1.35 dgemm_kernel
3.31 29.53 1.29 1 1.29 1.29 sdpa::UTSpMat::Form_Aggregate(sdpa::InputData&, int)
2.49 30.50 0.97 daxpy_k
2.36 31.42 0.92 15355388 0.00 0.00 sdpa::Lal::getInnerProduct(double&, sdpa::SparseMatrix&, sdpa::DenseMatrix&)
2.26 32.30 0.88 131652 0.00 0.00 dsterf_
1.21 32.77 0.47 27 0.02 0.10 sdpa::Newton::compute_bMat_sparse_SDP(sdpa::InputData&, sdpa::Solutions&, sdpa::Wor
kVariables&, sdpa::ComputeTime&)
1.18 33.23 0.46 3843398 0.00 0.00 sdpa::Lal::plus(sdpa::DenseMatrix&, sdpa::DenseMatrix&, sdpa::SparseMatrix&, double
*)
1.08 33.65 0.42 54 0.01 0.01 sdpa::Lal::let(sdpa::Vector&, char, sdpa::SparseMatrix&, char, sdpa::Vector&)
よって、以下のようなオプションが良いでのはないかという推測が出てくるが、実際にこれで make してみると以下のような結果になる。データ1だと少しだけ遅くなるが、SDPA に関してはこれで十分なようだ。
8: -O2 -funroll-all-loops
データ1:35.94s
データ2:27.92s
データ3:23.74s