トップページ

最新記事

cudaMallocManagedは遅いのか?

何の話か

UnifiedメモリがCUDA 6にはじめて導入されしばらく経ちました。
これはメモリのpage faultを用いた自動メモリ転送機能で、cudaMallocManagedでDeviceメモリを確保することで利用できます。
何か既存のコードをGPU移植する場合などには重宝していますが、初めから生CUDAを書いている場合はなんとなく遅い気がして敬遠しがちです。
今回少し性能テストを行ったのでその結果をまとめてみました。

性能評価

今回私が知りたいのは次の2点です。

  1. cudaMalloc/cudaMallocHost/cudaMemcpyを合わせて用いた場合とcudaMallocManagedを用いた場合では、どちらの方が速くHost to Deviceのメモリコピーができるのか?
  2. cudaMallocManagedcudaMallocではDeviceメモリ上のデータへのアクセス速度は異なるのか?

この2点を簡単なSgemmのコードを用いて評価しました。
コードの内容としては、cudaMalloc&cudaMallocHostを用いてメモリを確保した場合とcudaMallocManagedで確保した場合で、ホストで行列を初期化した後にGPU上でSgemmを計算するだけのものとなっています。

評価コード : enp1s0/cuda-unified-memory-test - GitHub

1. データ転送「cudaMalloc&cudaMallocHost&cudaMemcpy」対「cudaMallocManaged」

上記の評価コードをtest_count=1で回します。
転送開始からSgemm完了までにかかった時間はそれぞれこの様になっています。

行列サイズが小さい時、すなわちHtoDのデータ転送量が小さいときはUnifiedメモリの方が時間がかかることが分かります。
これは、データ転送量は\(O(N^2)\)で増えるに対し行列積の計算量は\(O(N^3)\)で増えるので、Nが大きくなるのに従いUnifiedメモリによるメモリの転送の遅さが計算時間に対して相対的に小さくなっていると考えられます。
左端のN=32では10倍程度実行時間に差があります。

2. GPUからのメモリアクセス「cudaMalloc」対「cudaMallocManaged」

続いてメモリアクセスの速度の比較です。
こちらはCULiPを使ってcublasSgemmにかかった時間を測ることで評価します。
今回はtest_count=8として実行します。
この場合、Unifiedメモリでは1回目のSgemmの際にHtoD転送が起き、2~7回目ではデータはDeviceメモリ上にあるためHtoD転送は起きません。
ですので、今行いたいのは2~7回目のSgemmにかかった時間の比較です。


こちらがCULiPで計測したcublasSgemmの時間です。

  • 普通のDeviceメモリを用いた場合
    - cublasSgemm_v2 : [4000826546 ns; 4.000827e+00 s;100.00%]
                    params    count                   sum          avg          max          min
      m16384-n16384-k16384        7   3475.566ms( 86.87%)    496.509ms    496.570ms    496.415ms
         m8192-n8192-k8192        7    441.875ms( 11.04%)     63.125ms     65.970ms     62.164ms
         m4096-n4096-k4096        7     73.810ms(  1.84%)     10.544ms     11.199ms      9.832ms
         m2048-n2048-k2048        7      7.414ms(  0.19%)      1.059ms      1.062ms      1.057ms
         m1024-n1024-k1024        7      1.450ms(  0.04%)      0.207ms      0.209ms      0.206ms
            m512-n512-k512        7      0.314ms(  0.01%)      0.045ms      0.046ms      0.044ms
            m256-n256-k256        7      0.140ms(  0.00%)      0.020ms      0.031ms      0.018ms
            m128-n128-k128        7      0.096ms(  0.00%)      0.014ms      0.015ms      0.013ms
               m64-n64-k64        7      0.088ms(  0.00%)      0.013ms      0.019ms      0.009ms
               m32-n32-k32        7      0.073ms(  0.00%)      0.010ms      0.013ms      0.009ms
    
  • Unifiedメモリを用いた場合
    - cublasSgemm_v2 : [3998602916 ns; 3.998603e+00 s;100.00%]
                    params    count                   sum          avg          max          min
      m16384-n16384-k16384        7   3477.529ms( 86.97%)    496.790ms    498.403ms    496.514ms
         m8192-n8192-k8192        7    438.032ms( 10.95%)     62.576ms     65.861ms     58.048ms
         m4096-n4096-k4096        7     74.214ms(  1.86%)     10.602ms     12.583ms      9.095ms
         m2048-n2048-k2048        7      6.807ms(  0.17%)      0.972ms      0.979ms      0.969ms
         m1024-n1024-k1024        7      1.345ms(  0.03%)      0.192ms      0.203ms      0.190ms
            m512-n512-k512        7      0.306ms(  0.01%)      0.044ms      0.052ms      0.042ms
            m256-n256-k256        7      0.120ms(  0.00%)      0.017ms      0.020ms      0.017ms
            m128-n128-k128        7      0.093ms(  0.00%)      0.013ms      0.015ms      0.013ms
               m64-n64-k64        7      0.092ms(  0.00%)      0.013ms      0.025ms      0.009ms
               m32-n32-k32        7      0.065ms(  0.00%)      0.009ms      0.011ms      0.009ms
    

どの大きさの行列でもほぼ同程度の実行時間となり、1の評価のN=32のときのように10倍の差があるということもないことが分かりました。
Device側のメモリ上にある分にはアクセス速度は同程度なようです。

まとめ

今回の実験では、Unifiedメモリを用いた場合ではcudaMemcpyと比較してHtoDは遅いが、一旦Device側にデータを送りさえすればDevice側でのアクセス速度はほぼ同じであることが確認できました。 Unifiedメモリを用いた場合はHtoDが遅いですが、問題サイズによってはここの遅さは無視できそうです。

終わりに

自分でcudaMemcpyを書くことに苦がない場合はUnifiedメモリは使わないほうが良さそうですね。
余談ですが、Unifiedメモリは私が大学受験の時期に発表されたもので、大学に入って久々にCUDAを触ったら明示的なメモリコピーが不要となっていて技術の進歩に驚いた覚えがあります。

評価環境

  • GPU : NVIDIA RTX 3080
  • CPU : Intel(R) Xeon(R) E-2136 CPU @ 3.30GHz
  • RAM : DDR4 2666MT/s
  • CUDA : 11.3
記事作成日:2021-05-08