diff --git a/Documents/Resources/资源网站.md b/Documents/Resources/资源网站.md index 0d8a4ff..817c6ba 100644 --- a/Documents/Resources/资源网站.md +++ b/Documents/Resources/资源网站.md @@ -76,6 +76,7 @@ * [Model Zoo](https://modelzoo.co/) * [Paddle](https://www.paddlepaddle.org.cn/) * [Paddle AI Studio](https://aistudio.baidu.com/aistudio) +* [Pascal VOC Dataset Mirror](https://pjreddie.com/projects/pascal-voc-dataset-mirror/) * [Python 机器学习基础教程](https://www.92python.com/ml/) * [Pandas: 强大的 Python 数据分析支持库](https://www.pypandas.cn/docs/) * [Mathematics for Machine Learning](https://mml-book.github.io/) diff --git a/Software/Development/System/Linux/User/API/Linux_进程_线程与_CPU_的亲和性(Affinity).md b/Software/Development/System/Linux/User/API/Linux_进程_线程与_CPU_的亲和性(Affinity).md new file mode 100644 index 0000000..99b4fe1 --- /dev/null +++ b/Software/Development/System/Linux/User/API/Linux_进程_线程与_CPU_的亲和性(Affinity).md @@ -0,0 +1,263 @@ +# [Linux 进程、线程与 CPU 的亲和性(Affinity)](https://www.cnblogs.com/wenqiang/p/6049978.html) + +最近的工作中对性能的要求比较高,下面简单做一下总结: + +## 1.什么是 CPU 亲和性(Affinity) + +CPU 的亲和性, 就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,也称为 CPU 关联性;再简单的点的描述就将制定的进程或线程绑定到相应的 CPU 上;在多核运行的机器上,每个 CPU 本身自己会有缓存,缓存着进程使用的信息,而进程可能会被 OS 调度到其他 CPU 上,如此,CPU cache 命中率就低了,当绑定 CPU 后,程序就会一直在指定的 CPU 跑,不会由操作系统调度到其他 CPU 上,性能有一定的提高。 + +* 软亲和性(affinity): 就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,Linux 内核进程调度器天生就具有被称为 软 CPU 亲和性(affinity) 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。 +* 硬亲和性(affinity):简单来说就是利用 linux 内核提供给用户的 API,强行将进程或者线程绑定到某一个指定的 CPU 核运行。 + +** 解释 **:在 linux 内核中,所有的进程都有一个相关的数据结构,称为 task_struct。这个结构非常重要,原因有很多;其中与 亲和性(affinity)相关度最高的是 cpus_allowed 位掩码。这个位掩码由 n 位组成,与系统中的 n 个逻辑处理器一一对应。 具有 4 个物理 CPU 的系统可以有 4 位。如果这些 CPU 都启用了超线程,那么这个系统就有一个 8 位的位掩码。 如果为给定的进程设置了给定的位,那么这个进程就可以在相关的 CPU 上运行。因此,如果一个进程可以在任何 CPU 上运行,并且能够根据需要在处理器之间进行迁移,那么位掩码就全是 1。实际上,这就是 Linux 中进程的缺省状态;(这部分内容在这个博客中有提到一点:http://www.cnblogs.com/wenqiang/p/4802619.html) + +cpus_allowed 用于控制进程可以在哪里处理器上运行 + +* sched_set_affinity() (用来修改位掩码) +* sched_get_affinity() (用来查看当前的位掩码) + +## 2.进程与 CPU 的绑定 + +sched_setaffinity 可以将某个进程绑定到一个特定的 CPU。你比操作系统更了解自己的程序,为了避免调度器愚蠢的调度你的程序,或是为了在多线程程序中避免缓存失效造成的开销,你可能会希望这样做。 + +在进行进程与 CPU 的绑定前,我们先了解编写程序需要准备的知识点 + +```cpp +SCHED_SETAFFINITY(2) Linux Programmer's Manual SCHED_SETAFFINITY(2) + +NAME + sched_setaffinity, sched_getaffinity - set and get a process's CPU affinity mask + +SYNOPSIS + #define _GNU_SOURCE /* See feature_test_macros(7) */ + #include + + int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask); + /* 该函数设置进程为 pid 的这个进程 , 让它运行在 mask 所设定的 CPU 上.如果 pid 的值为 0, + * 则表示指定的是当前进程 , 使当前进程运行在 mask 所设定的那些 CPU 上. + * 第二个参数 cpusetsize 是 mask 所指定的数的长度.通常设定为 sizeof(cpu_set_t). + * 如果当前 pid 所指定的进程此时没有运行在 mask 所指定的任意一个 CPU 上 , + * 则该指定的进程会从其它 CPU 上迁移到 mask 的指定的一个 CPU 上运行.*/ + + int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask); + /* 该函数获得 pid 所指示的进程的 CPU 位掩码 , 并将该掩码返回到 mask 所指向的结构中. + * 即获得指定 pid 当前可以运行在哪些 CPU 上. + * 同样 , 如果 pid 的值为 0.也表示的是当前进程 */ + +RETURN VALUE + On success, sched_setaffinity() and sched_getaffinity() return 0. On error, -1 is returned, and errno is set appropriately. +``` + +设置 CPU Affinity 还需要用到一下宏函数 + +```cpp +void CPU_ZERO (cpu_set_t *set) +/* 这个宏对 CPU 集 set 进行初始化,将其设置为空集。*/ +void CPU_SET (int cpu, cpu_set_t *set) +/* 这个宏将 指定的 CPU 加入 CPU 集 set 中 */ +void CPU_CLR (int cpu, cpu_set_t *set) +/* 这个宏将 指定的 CPU 从 CPU 集 set 中删除。*/ +int CPU_ISSET (int cpu, const cpu_set_t *set) +/* 如果 CPU 是 CPU 集 set 的一员,这个宏就返回一个非零值(true),否则就返回零(false)。*/ +``` + +下面下一个具体的例子:将当前进程绑定到 0、1、2、3 号 CPU 上 + +```cpp +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +/* sysconf( _SC_NPROCESSORS_CONF ) 查看 CPU 的个数;打印用%ld 长整。 + * sysconf( _SC_NPROCESSORS_ONLN ) 查看在使用的 CPU 个数;打印用%ld 长整 */ +int main(int argc, char **argv) +{ + int cpus = 0; + int i = 0; + cpu_set_t mask; + cpu_set_t get; + + cpus = sysconf(_SC_NPROCESSORS_CONF); + printf("cpus: %d\n", cpus); + + CPU_ZERO(&mask); /* 初始化 set 集,将 set 置为空 */ + CPU_SET(0, &mask); /* 依次将 0、1、2、3 号 CPU 加入到集合,前提是你的机器是多核处理器 */ + CPU_SET(1, &mask); + CPU_SET(2, &mask); + CPU_SET(3, &mask); + + /* 设置 CPU 亲和性(affinity)*/ + if (sched_setaffinity(0, sizeof(mask), &mask) == -1) { + printf("Set CPU affinity failue, ERROR:%s\n", strerror(errno)); + return -1; + } + usleep(1000); /* 让当前的设置有足够时间生效 */ + + /* 查看当前进程的 CPU 亲和性 */ + CPU_ZERO(&get); + if (sched_getaffinity(0, sizeof(get), &get) == -1) { + printf("get CPU affinity failue, ERROR:%s\n", strerror(errno)); + return -1; + } + + /* 查看运行在当前进程的 CPU*/ + for(i = 0; i < cpus; i++) { + + if (CPU_ISSET(i, &get)) { /* 查看 CPU i 是否在 get 集合当中 */ + printf("this process %d of running processor: %d\n", getpid(), i); + } + } + sleep(3); // 让程序停在这儿,方便 top 命令查看 + + return 0; +} +``` + +运行结果如下: + +```bash +[root@localhost test]# ./test +cpus: 24 +this process 2848 of running processor: 0 +this process 2848 of running processor: 1 +this process 2848 of running processor: 2 +this process 2848 of running processor: 3 +``` + +上面代码当中用到了 syscall 这个函数,顺便也在这里做一下说明: + +syscall 是执行一个系统调用,根据指定的参数 number 和所有系统调用的接口来确定调用哪个系统调用,用于用户空间跟内核之间的数据交换 + +下面是 syscall 函数原型及一些常用的 number + +```cpp +//syscall - indirect system call +SYNOPSIS + #define _GNU_SOURCE /* See feature_test_macros(7) */ + #include + #include /* For SYS_xxx definitions */ + + int syscall(int number, ...); + +/* sysconf( _SC_PAGESIZE ); 此宏查看缓存内存页面的大小;打印用%ld 长整型。 + sysconf( _SC_PHYS_PAGES ) 此宏查看内存的总页数;打印用%ld 长整型。 + sysconf( _SC_AVPHYS_PAGES ) 此宏查看可以利用的总页数;打印用%ld 长整型。 + sysconf( _SC_NPROCESSORS_CONF ) 查看 CPU 的个数;打印用%ld 长整。 + sysconf( _SC_NPROCESSORS_ONLN ) 查看在使用的 CPU 个数;打印用%ld 长整。 + (long long)sysconf(_SC_PAGESIZE) * (long long)sysconf(_SC_PHYS_PAGES) 计算内存大小。 + sysconf( _SC_LOGIN_NAME_MAX ) 查看最大登录名长度;打印用%ld 长整。 + sysconf( _SC_HOST_NAME_MAX ) 查看最大主机长度;打印用%ld 长整。 + sysconf( _SC_OPEN_MAX ) 每个进程运行时打开的文件数目;打印用%ld 长整。 + sysconf(_SC_CLK_TCK) 查看每秒中跑过的运算速率;打印用%ld 长整。*/ +``` + +## 3.线程与 CPU 的绑定 + +线程于进程的绑定方法大体一致,需要注意的是线程绑定于进程的区别是所用函数不一样 + +线程绑定用到下面两个函数,跟进程类似就不做详细说明,下面直接贴出函数原型: + +```cpp +NAME + pthread_setaffinity_np, pthread_getaffinity_np - set/get CPU affinity of a thread + +SYNOPSIS + #define _GNU_SOURCE /* See feature_test_macros(7) */ + #include + + int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, + const cpu_set_t *cpuset); + int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, + cpu_set_t *cpuset); + + Compile and link with -pthread. + +DESCRIPTION + The pthread_setaffinity_np() function sets the CPU affinity mask of the thread thread to the CPU set pointed to by cpuset. If the call is successful, and the thread is not + currently running on one of the CPUs in cpuset, then it is migrated to one of those CPUs. + + The pthread_getaffinity_np() function returns the CPU affinity mask of the thread thread in the buffer pointed to by cpuset. + + For more details on CPU affinity masks, see sched_setaffinity(2). For a description of a set of macros that can be used to manipulate and inspect CPU sets, see CPU_SET(3). + + The argument cpusetsize is the length (in bytes) of the buffer pointed to by cpuset. Typically, this argument would be specified as sizeo(cpu_set_t). (It may be some other + value, if using the macros described in CPU_SET(3) for dynamically allocating a CPU set.) + +RETURN VALUE + On success, these functions return 0; on error, they return a nonzero error number +``` + +下面同样是个具体的例子:将当前线程绑定到 0、1、2、3 号 CPU 上 + +```cpp +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +void *testfunc(void *arg) +{ + int i, cpus = 0; + cpu_set_t mask; + cpu_set_t get; + + cpus = sysconf(_SC_NPROCESSORS_CONF); + printf("this system has %d processor(s)\n", cpus); + + CPU_ZERO(&mask); + for (i = 0; i < 4; i++) { /* 将 0、1、2、3 添加到集合中 */ + CPU_SET(i, &mask); + } + + /* 设置 CPU 亲和性 (affinity)*/ + if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) { + fprintf(stderr, "set thread affinity failed\n"); + } + + /* 查看 CPU 亲和性 (affinity)*/ + CPU_ZERO(&get); + if (pthread_getaffinity_np(pthread_self(), sizeof(get), &get) < 0) { + fprintf(stderr, "get thread affinity failed\n"); + } + + /* 查看当前线程所运行的所有 CPU*/ + for (i = 0; i < cpus; i++) { + if (CPU_ISSET(i, &get)) { + printf("this thread %d is running in processor %d\n", (int)pthread_self(), i); + } + } + sleep(3); // 查看 + + pthread_exit(NULL); +} + +int main(int argc, char *argv[]) +{ + pthread_t tid; + if (pthread_create(&tid, NULL, (void *)testfunc, NULL) != 0) { + fprintf(stderr, "thread create failed\n"); + return -1; + } + + pthread_join(tid, NULL); + return 0; +} +``` + +运行结果如下: + +```bash +[root@localhost thread]# ./test +this system has 24 processor(s) +this thread 2812323584 is running in processor 0 +this thread 2812323584 is running in processor 1 +this thread 2812323584 is running in processor 2 +this thread 2812323584 is running in processor 3 +```