【Linux系统编程】| Linux性能优化——CPU亲缘性

一、CPU亲和性

CPU亲和性又称CPU关联,可以映射到一个或多个CPU上。该技术基于对称多处理机操作系统中的native central queue调度算法。
队列(queue)中的每一个任务(进程或线程)都有一个标签(tag)来指定它们倾向的CPU。在分配处理器的阶段,每个任务就会
分配到它们所倾向的CPU上;

    在多核运行的机器上,每个CPU本身自己会有缓存,在缓存中存着进程使用的数据,而没有绑定CPU的话,进程可能会被操作系
统调度到其他CPU上,如此CPU cache(高速缓冲存储器)命中率就低了,也就是说调到的CPU缓存区没有这类数据;
	- 【1】要先把内存或硬盘的数据载入缓存,而当缓存区绑定CPU后,程序就会一直在指定的CPU执行,不会被操作系统调度到其	
		他CPU,性能上会有一定的提高;
	- 【2】使用CPU绑定考虑的是将关键的进程隔离开,对于部分实时进程调度优先级提高,可以将其绑定到一个指定CPU核上,可
	 	以保证实时进程的调度,也可以避免其他CPU上进程被该实时进程干扰;

调度算法对于处理器亲和性的支持各不相同。有些调度算法在它认为合适的情况下会允许把一个任务调度到不同的处理器上。比如当
两个计算密集型的任务(A和B)同时对一个处理器具有亲和性时,另外一个处理器可能就被闲置了。这种情况下许多调度算法会把
任务B调度到第二个处理器上,使得多处理器的利用更加充分;

处理器亲和性能够有效地解决一些高速缓存的问题,但却不能缓解负载均衡的问题。而且,在异构系统中,处理器亲和性问题会变
得更加复杂;
CPU的亲和性分为两种:软亲和性和硬亲和性;

- 软CPU亲和性:进程要在指定的CPU上尽量长时间地运行而不被迁移到其他处理器上运行;Linux内核的自身特性,意味着进程通
常不会在处理器之间频繁迁移,以避免这种迁移对于计算能力的消耗,以达到最佳的平衡性;

- 硬CPU亲和性的机制:这个机制让开发人员可以实现硬CPU亲和性。这意味着可以显式指定进程在哪个(或哪些)处理器上运行;
	以此提高性能;
1.1 使用场景

保持高CPU缓存命中率
SMP、NUMA、MPP体系结构介绍

目前主流的服务器配置都是SMP架构,在SMP的环境下,每个CPU本身自己会有缓存,缓存着进程使用的信息,如果一个给定的进程迁移到其他地方去了,那么它就失去了利用 CPU 缓存的优势。实际上,如果正在使用的 CPU 需要为自己缓存
一些特殊的数据,那么所有其他 CPU 都会使这些数据在自己的缓存中失效。因此,如果有多个线程都需要相同的数据,那么将这些
线程绑定到一个特定的 CPU 上是非常有意义的,这样就确保它们可以访问相同的缓存数据(或者至少可以提高缓存的命中率)。否
则,这些线程可能会在不同的 CPU 上执行,这样会频繁地使其他缓存项失效;

测试复杂的应用程序

考虑一个需要进行线性可伸缩性测试的应用程序。有些产品声明可以在使用更多硬件时执行得更好。 我们不用购买多台机器(为每
种处理器配置都购买一台机器),而是可以:1.购买一台多处理器的机器;2.不断增加分配的处理器;3.测量每秒的事务数;4.评估
结果的可伸缩性;

二、使用接口

#inclde <sched.h>

int sched_setaffinity(pid_t pid, size_t cpusetsize,
                             cpu_set_t *mask);
/**
@func: 将ID为pid的进程的CPU亲和性掩码设置为mask指定的值;
	若pid为0,则使用当前调用进程;
	如果pid指定的进程当前不在掩码中指定的cpu上运行,那么该进程将迁移到掩码中指定的一个cpu上掩码中指定的cpu;
@param cpusetsize: sizeof(cpu_set_t);
return: 成功返回0,失败返回-1;
*/

int sched_getaffinity(pid_t pid, size_t cpusetsize,
                             cpu_set_t *mask);
/**
@func: 获取pid的亲和性掩码,存入mask中;
return: 成功返回0,失败返回-1;
*/

/** 清除set */
void CPU_ZERO(cpu_set_t *set);
/** 设置 */
void CPU_SET(int cpu, cpu_set_t *set);
/** 移除 */
void CPU_CLR(int cpu, cpu_set_t *set);
/** 判断是否设置 */
int  CPU_ISSET(int cpu, cpu_set_t *set);
/** CPU数量 */
int  CPU_COUNT(cpu_set_t *set);

void CPU_AND(cpu_set_t *destset, cpu_set_t *srcset1, cpu_set_t *srcset2);
void CPU_OR(cpu_set_t *destset, cpu_set_t *srcset1, cpu_set_t *srcset2);
void CPU_XOR(cpu_set_t *destset, cpu_set_t *srcset1, cpu_set_t *srcset2);
int  CPU_EQUAL(cpu_set_t *set1, cpu_set_t *set2);

cpu_set_t *CPU_ALLOC(int num_cpus);
void CPU_FREE(cpu_set_t *set);
size_t CPU_ALLOC_SIZE(int num_cpus);


/*/usr/include/bits/sched.h*/


# define __CPU_SETSIZE  1024
# define __NCPUBITS (8 * sizeof (__cpu_mask))

/* Type for array elements in 'cpu_set'.  */
typedef unsigned long int __cpu_mask;

typedef struct
{
  __cpu_mask __bits[__CPU_SETSIZE / __NCPUBITS];
} cpu_set_t;
// 每一个bit都表示一个cpu状态

测试

========》Centos下几种CPU查看使用率的常用命令《=========

#include <stdint.h>
#include <stdio.h>
#include <sched.h>
#include <pthread.h>
#include <stdlib.h>

static inline void print_cpu_mask(cpu_set_t cpu_mask)
{
    unsigned char flag = 0;
    printf("Cpu affinity is ");
    for (unsigned int i = 0; i < sizeof(cpu_set_t); i ++)
    {
        if (CPU_ISSET(i, &cpu_mask))
        {
            if (flag == 0)
            {
                flag = 1;
                printf("%d", i);
            }
            else
            {
                printf(",%d", i);
            }
        }
    }
    printf(".\n");
}

static inline void get_cpu_mask(pid_t pid, cpu_set_t *mask)
{
    if (sched_getaffinity(pid, sizeof(cpu_set_t), mask) == -1)
    {
        perror("get cpu affinity failed.\n");
        abort();
    }
}

static inline void set_cpu_mask(pid_t pid, cpu_set_t *mask)
{
    if (sched_setaffinity(pid, sizeof(cpu_set_t), mask) == -1)
    {
        perror("set cpu affinity failed.\n");
        abort();
    }
}

void *thread_func(void *param)
{
    cpu_set_t cpu_mask;
    get_cpu_mask(0, &cpu_mask);
    printf("Slave thread ");
    print_cpu_mask(cpu_mask);

    CPU_ZERO(&cpu_mask);
    CPU_SET(1, &cpu_mask);
    set_cpu_mask(0, &cpu_mask);
    get_cpu_mask(0, &cpu_mask);
    printf("Slave thread ");
    print_cpu_mask(cpu_mask);

    for (;;)
    {
        ;
    }
}

int main(int argc, char *argv[])
{
    unsigned int active_cpu = 0;
    cpu_set_t cpu_mask;
    pthread_t thread;

    get_cpu_mask(0, &cpu_mask);
    print_cpu_mask(cpu_mask);

    CPU_ZERO(&cpu_mask);
    CPU_SET(active_cpu, &cpu_mask);
    set_cpu_mask(0, &cpu_mask);

    get_cpu_mask(0, &cpu_mask);
    printf("Master thread ");
    print_cpu_mask(cpu_mask);

    if (pthread_create(&thread, NULL, thread_func, NULL) != 0)
    {
        perror("pthread_create failed.\n");
    }
    pthread_join(thread, NULL);

    return 0;
}
本次测试使用1号cpu,使用mpstat命令查看即可确认是否设置成功;

在这里插入图片描述

参考文章
关于CPU亲和性,这篇讲得最全面
Linux CPU亲缘性详解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jxiepc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值