C言語でsched_setaffinity/sched_getaffinity/sched_getcpu関数の使い方を教えて!
こういった悩みにお答えします.
本記事の信頼性
- リアルタイムシステムの研究歴12年.
- 東大教員の時に,英語でOS(Linuxカーネル)の授業.
- 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校(UNC)コンピュータサイエンス学部で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
- プログラミング歴15年以上,習得している言語: C/C++,Python,Solidity/Vyper,Java,Ruby,Go,Rust,D,HTML/CSS/JS/PHP,MATLAB,Assembler (x64,ARM).
- 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」をGitHubにオープンソースとして公開.
- 2020年1月~現在はアメリカのノースカロライナ州チャペルヒルにあるGuarantee Happiness LLCのCTOとしてECサイト開発やWeb/SNSマーケティングの業務.2022年6月~現在はアメリカのノースカロライナ州チャペルヒルにあるJapanese Tar Heel, Inc.のCEO兼CTO.
- 最近は自然言語処理AIとイーサリアムに関する有益な情報発信に従事.
- (AI全般を含む)自然言語処理AIの論文の日本語訳や,AIチャットボット(ChatGPT,Auto-GPT,Gemini(旧Bard)など)の記事を50本以上執筆.アメリカのサンフランシスコ(広義のシリコンバレー)の会社でプロンプトエンジニア・マネージャー・Quality Assurance(QA)の業務委託の経験あり.
- (スマートコントラクトのプログラミングを含む)イーサリアムや仮想通貨全般の記事を200本以上執筆.イギリスのロンドンの会社で仮想通貨の英語の記事を日本語に翻訳する業務委託の経験あり.
こういった私から学べます.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!
本記事では,以下の記事の内容を理解していることを前提とします.
目次
sched_setaffinity/sched_getaffinity/sched_getcpu関数
sched_setaffinity/sched_getaffinity/sched_getcpu関数を紹介します.
1 2 3 4 |
int sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *mask); int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask); |
sched_setaffinity/sched_getaffinity関数は,スレッドのCPU affinity(親和度)マスクを設定,取得します.
スレッドの CPU affinityマスクは,そのスレッドが実行を許可されているCPUの集合を決定します.
マルチコアプロセッサでは,CPU affinityマスクを設定することで性能上のメリットを得られる可能性があります.
例えば,特定のスレッドを1つのCPUで専有することで,そのスレッドの性能を最大化できます.
具体的には,そのスレッドのaffinityマスクを1つのCPU に設定し,他の全てのスレッドのaffinityマスクからそのCPUを除外します.
※他の全てのスレッドのaffinityマスクからそのCPUを除外したい場合は,isolcpusを設定します(詳細な手順はこちら).
また,あるスレッドの実行を1つのCPUに限定することで,1つのCPUでの実行を停止してから別のCPUで実行を再開するときに発生するキャッシュ汚染(cache pollution)による性能低下を避けることもできます.
CPU affinityマスクは,「CPUの集合」を表すcpu_set_t構造体で表現され,cpu_set_tへのポインタmaskで指定されます.
sched_setaffinity関数は,スレッドIDがpidのスレッドのCPU affinityマスクをmaskで指定された値に設定します.
pidが0の場合,呼び出し元スレッドが使われます.
cpusetsize引数にはmaskが指すデータの長さ(バイト単位)です.
pidで指定されたスレッドがmaskで指定されたCPUのいずれかで現在実行されていない場合,そのスレッドはmaskで指定されたCPUのいずれかに移動します.
sched_getaffinity関数は,スレッドIDがpidのスレッドのaffinityマスクをmaskが指すcpu_set_t構造体に書き込みます.
cpusetsize引数にはmaskの(バイト単位の)大きさを指定します.
1 |
int sched_getcpu(void); |
sched_getcpu関数は,呼び出したスレッドが現在実行されている CPU IDを返します.
sched_setaffinity/sched_getaffinity/sched_getcpu関数の使い方
sched_setaffinity/sched_getaffinity/sched_getcpu関数の使い方は以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <sched.h> #include <unistd.h> #include <sys/sysinfo.h> #include <sys/types.h> int migrate_thread_to_cpu(pid_t tid, int target_cpu) { cpu_set_t *cpu_set; size_t size; int nr_cpus; int ret; if ((nr_cpus = get_nprocs()) == -1) { perror("get_nprocs()"); exit(1); } if (target_cpu < 0 || target_cpu >= nr_cpus) { fprintf(stderr, "Error: target_cpu %d is in [0,%d).\n", target_cpu, nr_cpus); exit(2); } if ((cpu_set = CPU_ALLOC(nr_cpus)) == NULL) { perror("CPU_ALLOC"); exit(3); } size = CPU_ALLOC_SIZE(nr_cpus); CPU_ZERO_S(size, cpu_set); CPU_SET_S(target_cpu, size, cpu_set); if ((ret = sched_setaffinity(tid, size, cpu_set)) == -1) { perror("sched_setaffinity"); exit(4); } CPU_FREE(cpu_set); return ret; } int main(int argc, char *argv[]) { int target_cpu; cpu_set_t cpu_set; pid_t tid = gettid(); if (argc != 2) { fprintf(stderr, "Usage: %s [CPU ID]\n", argv[0]); exit(1); } target_cpu = atoi(argv[1]); migrate_thread_to_cpu(tid, target_cpu); printf("sched_getcpu() = %d\n", sched_getcpu()); if (sched_getaffinity(tid, sizeof(cpu_set_t), &cpu_set) == -1) { perror("sched_getaffinity"); exit(2); } printf("cpu_set = 0x%016lx\n", *((unsigned long *) &cpu_set)); return 0; } |
実行結果は以下になります.
1 2 3 4 5 6 7 8 9 10 |
$ gcc sched_setaffinity.c $ a.out 0 sched_getcpu() = 0 cpu_set = 0x0000000000000001 $ a.out 1 sched_getcpu() = 1 cpu_set = 0x0000000000000002 $ a.out 2 sched_getcpu() = 2 cpu_set = 0x0000000000000004 |
参考:tasksetコマンドでスレッドを特定のCPUで実行
tasksetコマンドでスレッドを特定のCPUで実行する方法を紹介します.
tasksetコマンドにより,先述したsched_setaffinity関数を利用したmigrate_thread_to_cpu関数のような処理をコマンドラインで実行できます.
tasksetコマンドの使い方は以下になります.(詳細な使い方はこちら)
mask(16進数)でcommandのaffinityマスクを設定します.
1 |
$ taskset mask command |
tasksetコマンドを利用するコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #define _GNU_SOURCE #include <stdio.h> #include <sched.h> int main(int argc, char *argv[]) { printf("sched_getcpu() = %d\n", sched_getcpu()); return 0; } |
tasksetコマンドでプログラムをCPU 0,CPU 1,CPU 2でそれぞれ実行した場合の実行結果は以下になります.
1 2 3 4 5 6 7 |
$ gcc taskset.c $ taskset 0x1 a.out sched_getcpu() = 0 $ taskset 0x2 a.out sched_getcpu() = 1 $ taskset 0x4 a.out sched_getcpu() = 2 |
まとめ
sched_setaffinity/sched_getaffinity/sched_getcpu関数の使い方を紹介しました.
スレッドを特定のCPU IDで実行したい時に便利ですので,是非使いましょう!
sched_setaffinity/sched_getcpu関数は,以下の記事で利用しています.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!