C/C++ Linux/Unix 讓執行緒跑在指定 CPU 的方法 pthread_setaffinity_np

本篇 ShengYu 介紹 C/C++ Linux/Unix 執行緒設定 CPU 的方法 pthread_setaffinity_np()

主執行緒設定 CPU

主執行緒要設定跑在哪顆 CPU 的話,可以直接在 main 裡的主執行緒使用 pthread_setaffinity_np() 設定即可,pthread_setaffinity_np() 的第一個參數為 pthread_t,
以我的電腦來說是單 CPU 4 核心,所以有 CPU0~CPU3 可以選擇,這邊示範選擇跑在 CPU3,

cpp-pthread_setaffinity_np.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// g++ cpp-pthread_setaffinity_np.cpp -o a.out -pthread
#include <stdio.h>
#include <sched.h> // for sched_getcpu()
#include <pthread.h>

int main() {
int cpu_id = 3; // set thread to cpu3
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpu_id, &cpuset);

printf("pthread_setaffinity_np cpu%d\n", cpu_id);
pthread_t th = pthread_self();
pthread_setaffinity_np(th, sizeof(cpuset), &cpuset);

printf("sched_getcpu = %d\n", sched_getcpu());

return 0;
}

也可以使用 sched_setaffinity() 來設定 main 主執行緒跑在哪個 CPU。

在 pthread 執行緒設定 CPU

pthread 建立出來的執行緒要設定 CPU 的話,方式如下,
對於 pthread 建立執行緒不熟悉的話可以看我之前的這篇介紹

cpp-pthread_setaffinity_np2.cpp
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
// g++ cpp-pthread_setaffinity_np2.cpp -o a.out -pthread
#include <stdio.h>
#include <iostream>
#include <sched.h>
#include <pthread.h>
#include <unistd.h>
using namespace std;

void * foo(void *arg) {
printf("t1 thread sched_getcpu = %d\n", sched_getcpu());
usleep(1);
printf("t1 thread sched_getcpu = %d\n", sched_getcpu());
return NULL;
}

int main() {
pthread_t t1;
if (pthread_create(&t1, NULL, foo, NULL) != 0) {
fprintf(stderr, "Error: pthread_create\n");
}

int cpu_id = 3; // set thread to cpu3
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpu_id, &cpuset);

printf("pthread_setaffinity_np cpu%d\n", cpu_id);
pthread_setaffinity_np(t1, sizeof(cpuset), &cpuset);

//printf("main thread sched_getcpu = %d\n", sched_getcpu());

pthread_join(t1, NULL);

return 0;
}

輸出結果如下,可以發現 foo() 函式裡可能需要加個延遲,以便讓 pthread_setaffinity_np() 的設定生效,

1
2
3
pthread_setaffinity_np cpu3
t1 thread sched_getcpu = 1
t1 thread sched_getcpu = 3

上述方法也許有點不完美,也可以在 foo() 函式裡執行 pthread_setaffinity_np() 就可免去延遲的程式碼。

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
// g++ cpp-pthread_setaffinity_np3.cpp -o a.out -pthread
#include <stdio.h>
#include <iostream>
#include <sched.h>
#include <pthread.h>
#include <unistd.h>
using namespace std;

void * foo(void *arg) {
int cpu_id = 3; // set thread to cpu3
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpu_id, &cpuset);

printf("pthread_setaffinity_np cpu%d\n", cpu_id);
pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);

printf("t1 thread sched_getcpu = %d\n", sched_getcpu());

return NULL;
}

int main() {
pthread_t t1;
if (pthread_create(&t1, NULL, foo, NULL) != 0) {
fprintf(stderr, "Error: pthread_create\n");
}

//printf("main thread sched_getcpu = %d\n", sched_getcpu());

pthread_join(t1, NULL);

return 0;
}

輸出結果如下,

1
2
pthread_setaffinity_np cpu3
t1 thread sched_getcpu = 3

在 C++ std::thread 執行緒設定 CPU

要在 C++ std::thread 設定跑在哪顆 CPU 的話可以用 thread::native_handle() 取得 pthread_t 再傳入 pthread_setaffinity_np() 的第一個參數,因為 t1 thread 是 std::thread 建構時就開始執行了,所以下面 pthread_setaffinity_np() 設定太慢的話,就會比較慢生效,所以有可能 foo 執行到一半時才被設定 CPU,因此這邊先加個延遲 1 microseconds,確保一開始執行時就被設定 CPU,

cpp-pthread_setaffinity_np-std-thread.cpp
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
// g++ cpp-pthread_setaffinity_np-std-thread.cpp -o a.out -std=c++11 -pthread
#include <iostream>
#include <thread>
#include <chrono>
#include <sched.h>
#include <pthread.h>
using namespace std;

void foo() {
std::this_thread::sleep_for(std::chrono::microseconds(1));

printf("t1 thread sched_getcpu = %d\n", sched_getcpu());
}

int main() {
thread t1(foo);

int cpu_id = 3; // set thread to cpu3
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpu_id, &cpuset);

printf("pthread_setaffinity_np cpu%d\n", cpu_id);
pthread_setaffinity_np(t1.native_handle(), sizeof(cpuset), &cpuset);

//printf("main thread sched_getcpu = %d\n", sched_getcpu());

if (t1.joinable()) {
t1.join();
}

return 0;
}

上述方法也許有點不完美,也可以改成在執行 thread 的函式裡去設定自身 thread 的跑在哪顆 CPU,這樣可以省去延遲的程式碼,

cpp-pthread_setaffinity_np-std-thread2.cpp
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
// g++ cpp-pthread_setaffinity_np-std-thread2.cpp -o a.out -std=c++11 -pthread
#include <iostream>
#include <thread>
#include <sched.h>
#include <pthread.h>
using namespace std;

void foo() {
int cpu_id = 3; // set thread to cpu3
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpu_id, &cpuset);

printf("pthread_setaffinity_np cpu%d\n", cpu_id);
pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);

printf("t1 thread sched_getcpu = %d\n", sched_getcpu());
}

int main() {
thread t1(foo);

//printf("main thread sched_getcpu = %d\n", sched_getcpu());

if (t1.joinable()) {
t1.join();
}

return 0;
}

其它參考
c++ - Set CPU affinity when create a thread - Stack Overflow
https://stackoverflow.com/questions/24645880/set-cpu-affinity-when-create-a-thread
pthread_setaffinity_np(3) - Linux manual page
https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html
C++11 threads, affinity and hyperthreading - Eli Bendersky’s website
https://eli.thegreenplace.net/2016/c11-threads-affinity-and-hyperthreading/
SetThreadAffinityMask function (winbase.h) - Win32 apps | Microsoft Docs
https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setthreadaffinitymask

其它相關文章推薦
C/C++ 新手入門教學懶人包
C/C++ Linux/Unix 讓執行緒跑在指定 CPU 的方法 sched_setaffinity
C/C++ Linux/Unix pthread 建立多執行緒用法與範例
C++ std::thread 建立多執行緒用法與範例