0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

マルチスレッドでMKLを使う際のスレッドセーフ

Last updated at Posted at 2025-03-05

C#のMKLのスレッドセーフ

Parallel.Forなどで、マルチスレッド処理を行い、その処理にいMKL(dgemm等)を用いていると結果が不安定(シーケンシャルと異なる結果)になることがある。これは、MKLも一部の関数がマルチスレッドを使用しているためである。不安定にならないように、スレッドセーフで行うための方法例を記載する。

lockを使う

処理を排他的にするためにlockする。
コード例
var syncObj = new object();
Parallel.For(0, n, i =>
{
    lock (syncObj) 
    {
        MKL Processes
    }
});

この方法は、メモリアクセスのスレッドセーフと同じで、lock内の処理を他のスレッドで実行できないようにlockするための方法である。ただし、MKLを使った処理がメインとなる並列処理の場合は、マルチスレッドの効果が十分にないので、その場合はシーケンシャルで処理するほうがよい。
MKL使用時のバッファーが不安定になる原因にもなるので、後述のバッファーを解放させることも必要である。

MKLのスレッド数を制限する

MKLが使用するスレッド数を制限する。
コード例
[DllImport(mkl_rt.2.dll, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, SetLastError = false)]
internal static extern int mkl_set_num_threads_local(ref int num);
[DllImport(mkl_rt.2.dll, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, SetLastError = false)]
internal static extern void mkl_set_dynamic(ref int flag);
[DllImport(mkl_rt.2.dll, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, SetLastError = false)]
internal static extern int mkl_get_dynamic();

Parallel.For(0, n, i =>
{
    // Get original setting
    int oriDynamics = mkl_get_dynamic();
    int dynamics = 0;
    // Off dynamical threads
    mkl_set_dynamic(ref dynamics);
    int threads = 1;
    // Set number of thread and get last threads number
    int threads_last = mkl_set_num_threads_local(ref threads);
    
    MKL Processes

    // Set previous threads number
    threads = mkl_set_num_threads_local(ref threads_last);
    // Set previous setting
    mkl_set_dynamic(ref oriDynamics);
});

MKLのサポート関数にて、MKLを使用時にスレッド数を一つにして、メモリが競合しないようにする。mkl_set_num_threads_localで制限してもdynamicがオンの状態だと、十分に制限できないことがあるようなので、dynamicをオフにしておく。この記述したスレッドでしか有効でないために、各スレッド毎に記述する必要がある。
スレッド制限するため、1スレッドあたりの処理は若干遅くなるが、大きく影響はしなかった。

大規模データの場合

大規模データは、計算後のバッファーも大きいので、影響を及ぼすことがあった。対策として、以下のようにスレッドの最後やMKLの演算後に不要になるタイミングで、バッファーを解放し、ガベージ コレクションを強制実行し、少しでもバッファーの影響をなくす。
バッファー解放
            mkl_thread_free_buffers();
            GC.Collect();

mkl_thread_free_buffersは、使用しているスレッドのバッファーの解放である。

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?