pthread mutex lockとAtomic型修飾子の比較

提供: C言語入門
移動: 案内検索
スポンサーリンク

C言語のマルチスレッドプログラミングでよく使われるpthreadでは、アトミックな処理のために mutex を使用してロックします。また、C言語では、_Atomic型修飾子を利用して、アトミックな処理ができます。ここでは、pthread_mutex_lockを利用した場合と _Atomic を利用したアトミックなプログラムのスピードの比較を行います。

読み方

pthread
ぴーすれっど
アトミック
あとみっく

概要

ロックなしは、ロックがないため、期待した結果をしないプログラムです。なぜか、コンパイラのコンパイルオプションの -O2 で最適化した場合は、結果が 0 になり、期待した結果になっていました。

mutex と _Atomic の比較では、 _Atomic > mutex lock という結果になりました。

ロックなし、pthread_mutex_lock, _Atomicの比較
タイプ user system cpu total
ロックなし 0.04s 0.01s 189% 0.024
mutex 0.26s 0.16s 157% 0.265
_Atomic 0.28s 0.00s 196% 0.142

mutex のロックは、コストが高い、ということでしたが、アトミックを使ったほうが、今回の単純なケースでは、速い、ということが解りました。 また、_Atomicを利用した場合のほうが、ソースコードも簡単になります。 pthread_mutex_lockを使うと pthread_mutex_unlock の呼び出しも必要になり、コードが汚くなってしまいます。

ヘッダファイル

#include <stdatomic.h>

サンプルコードの意味

  • 2つのスレッドを同時に実行します。
  • 片方の1つのスレッドは、グローバル変数 shared_data をインクリメントし続けます。
  • もう1つのスレッドは、グローバル変数 shared_data をデクリメントし続けます。
  • 最終的に 0 になることを期待します。
    • ロックなしのコードは、アトミックに処理できないので、結果は不定です(0かもしれないし、0以外かもしれない)。

pthread_test1_no_block.c

このコードでも -O2 のオプションをつけると、結果が 0 になります。

ソースコード

// Last update: 2016/02/13 15:52:56
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <err.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
 
#include <pthread.h>
 
int shared_data = 0;
 
unsigned long long int max = 3000000;
 
void
func1 (int x)
{
        unsigned long long int  i;
 
        for (i = 0; i < max; ++i)
        {
                ++shared_data;
        }
}
 
void
func2 (int x)
{
        unsigned long long int  i;
 
        for (i = 0; i < max; ++i)
        {
                --shared_data;
        }
}
 
void
test ()
{
        pthread_t       t1, t2;
 
        pthread_setconcurrency (2);
        pthread_create (& t1, NULL, (void *) func1, (void *) 10);
        pthread_create (& t2, NULL, (void *) func2, (void *) 20);
        pthread_join (t1, NULL);
        pthread_join (t2, NULL);
}
 
int
main (int argc, char **argv)
{
        int     i;
 
        test ();
        printf ("%u\n", shared_data);
        printf ("END\n");
 
        exit (EXIT_SUCCESS);
}

pthread_test1_mutex

ソースコード

// Last update: 2016/02/13 15:52:56
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <err.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
 
#include <pthread.h>
 
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
 
int shared_data = 0;
 
unsigned long long int max = 3000000;
 
void
func1 (int x)
{
        unsigned long long int  i;
 
        for (i = 0; i < max; ++i)
        {
                int r;
                r = pthread_mutex_lock(&m);
                if (r != 0) {
                        errc(EXIT_FAILURE, r, "can not lock");
                }
                ++shared_data;
                r = pthread_mutex_unlock(&m);
                if (r != 0) {
                        errc(EXIT_FAILURE, r, "can not unlock");
                }
        }
}
 
void
func2 (int x)
{
        unsigned long long int  i;
 
        for (i = 0; i < max; ++i)
        {
                int r;
                r = pthread_mutex_lock(&m);
                if (r != 0) {
                        errc(EXIT_FAILURE, r, "can not lock");
                }
                --shared_data;
                r = pthread_mutex_unlock(&m);
                if (r != 0) {
                        errc(EXIT_FAILURE, r, "can not unlock");
                }
        }
}
 
void
test ()
{
        pthread_t       t1, t2;
 
        pthread_setconcurrency (2);
        pthread_create (& t1, NULL, (void *) func1, (void *) 10);
        pthread_create (& t2, NULL, (void *) func2, (void *) 20);
        pthread_join (t1, NULL);
        pthread_join (t2, NULL);
}
 
int
main (int argc, char **argv)
{
        int     i;
 
        test ();
        printf ("%u\n", shared_data);
        printf ("END\n");
 
        exit (EXIT_SUCCESS);
}

pthread_test1_atomic.c

ソースコード

// Last update: 2016/02/13 15:52:56
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <err.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
 
#include <pthread.h>
#include <stdatomic.h>
 
_Atomic int shared_data = ATOMIC_VAR_INIT(0);
 
unsigned long long int max = 3000000;
 
void
func1 (int x)
{
        unsigned long long int  i;
 
        for (i = 0; i < max; ++i)
        {
                ++shared_data;
        }
}
 
void
func2 (int x)
{
        unsigned long long int  i;
 
        for (i = 0; i < max; ++i)
        {
                --shared_data;
        }
}
 
void
test ()
{
        pthread_t       t1, t2;
 
        pthread_setconcurrency (2);
        pthread_create (& t1, NULL, (void *) func1, (void *) 10);
        pthread_create (& t2, NULL, (void *) func2, (void *) 20);
        pthread_join (t1, NULL);
        pthread_join (t2, NULL);
}
 
int
main (int argc, char **argv)
{
        int     i;
 
        test ();
        printf ("%u\n", shared_data);
        printf ("END\n");
 
        exit (EXIT_SUCCESS);
}

コンパイル

cc -pthread  pthread_test1_atomic.c  -o pthread_test1_atomic
cc -pthread  pthread_test1_mutex.c  -o pthread_test1_mutex
cc -pthread  pthread_test1_no_block.c  -o pthread_test1_no_block

実行例

薫 $ time ./pthread_test1_no_block
509710
END
./pthread_test1_no_block  0.04s user 0.01s system 189% cpu 0.024 total
薫 $ time ./pthread_test1_mutex
0
END
./pthread_test1_mutex  0.26s user 0.16s system 157% cpu 0.265 total
薫 $ time ./pthread_test1_atomic
0
END
./pthread_test1_atomic  0.28s user 0.00s system 196% cpu 0.142 total

関連項目




スポンサーリンク