ソースを詳しく検証してみました。
GitHub - COPT-Public/cuPDLP-C: Code for solving LP on GPU using first-order methods
PDLPは、メジャーループとマイナーループをもっています。
マイナーループでは、下で示したブロックが時間を食います。
<時間計測する場合は、DeviceSynchronoize()で挟む>
最初に嵌ったのは、ボトルネック部を見極めるための時間計測の誤りでした。
各ブロック間に、DeviceSynchronoizeを入れないと、時間計測が正しくありません。上図は、DeviceSynchronoizeを各ブロックにいれたものですが、トータルの時間が大きくなってしまいます。 cudaSetDeviceFlags(cudaDeviceScheduleSpin);は、行ってもこれは効いていないようで、終了を待たずに次のブロック処理に入るためのようです。次に行ったのは、double ⇒single float にすることでした。これによる効果は2倍以下でした。
<ボトルネックは、DOTとSPMV>
DOT処理部は、別に検討することにして、メジャーループ・マイナーループ共に必要なのは、SPMVです。余談ですが、PDLPの前身は、PDHGです。
Primal-Dual Hybrid Gradient Algorithm (PDHG) — odl 0.8.1 documentation
PDHGにAdptiveStepSize等を加えたのが、PDLPになります。
<SPMV時間の検討>
SPMV処理に関わる転送時間を計算してみます。
(NNZ数x3+XCols数x2 +YRows数x2)*sizeof(cupd_float)
これにVRAM帯域
P1000:80GB/Sec
4060:270GB/Sec
を考えると、数us以下になるはずです。しかし、実際に観測されるのは、この10倍程度以上の値です。CUDAの起動時間もあるのですが、それにしてもキャッシュミス等、未だ何かあると考えざるをえません。
ということで、未だ検討の余地は、あると考えます。COPTが使っているSPMVは、NVidia cusparse で、これ以上に高速でないと意味がありません。
<高速SPMVの検討>
サーベイ論文が参考になります。
A Systematic Literature Survey of Sparse Matrix-Vector Multiplication
具体的な実装例は、次です。
https://github.com/PAA-NCIC/AlphaSparse/tree/main
https://github.com/KaHIP/HeiStream?tab=readme-ov-file
0 件のコメント:
コメントを投稿