Unity 改变CPU亲和度(Android)


20207.13 更新:以下内容不可行。在移动平台上,移动厂商限制了通过自行设置CPU亲和度来控制进程具体在哪个核心上调度的功能。所以以下做法在移动平台上是无效的。

定义

CPU亲和度(CPU Affinity)一种表征软件在特定处理器运作倾向的方法。比如目前的骁龙处理器一般是八个核心,由四个大核和四个小核,或者2个大核六个小核组成。在Android平台上,通过以下命令查看对应的CPU最大频率。频率高的就是大核,反之就是小核。

adb shell cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq

通常,一个应用程序默认设置为软CPU亲和度,这表示没有限制必须在哪个核心上跑这个应用程序,由调度程序来决定它当前时刻跑在哪个CPU核心上。而硬CPU亲和度指定了应用程序只能跑在特定的一个或者多个处理核心上。这种想法来源于以下几个事实:

  • 应用程序由一个核心切换到另一个核心会丢失已经缓存好的数据,导致缓存命令率下降,从而降低了性能。
  • 每次应用程序被重新调度,都可能会被分配到其他核心上。在最坏的情况下,每次调度之后应用程序都在与之前不同的CPU核心上运行,这就是“乒乓效应”。
  • 大核心的CPU性能会比小核心更好,在一些最求性能的应用程序上,将主进程绑定到特定的大核中,往往可以提升应用程序的整体性能。

读写CPU亲和度

在Android(接口和数据结构与Linux的大体一致)系统上,用数据结构cpu_set_t表示CPU亲和度,它在头文件sched.h中被定义:

#ifdef __LP64__
#define CPU_SETSIZE 1024
#else
#define CPU_SETSIZE 32
#endif

#define __CPU_BITTYPE  unsigned long int  /* mandated by the kernel  */
#define __CPU_BITS     (8 * sizeof(__CPU_BITTYPE))

typedef struct {
  __CPU_BITTYPE  __bits[ CPU_SETSIZE / __CPU_BITS ];
} cpu_set_t;

注意这个结构虽然包含一个数组,但其实是一个整体,每个bit都对应一个CPU核心,所以它其实是对应每个CPU核心的亲和度掩码。在32bit系统下,cpu_set_t的内存占用是32bit,在64bit系统下,内存占用是1024bit,分别表示最多能够表示32个核心和1024个核心的CPU亲和度。同时,这个头文件还提供了一些方法用来读写这个掩码:

#define CPU_ZERO(set)          CPU_ZERO_S(sizeof(cpu_set_t), set)
#define CPU_SET(cpu, set)      CPU_SET_S(cpu, sizeof(cpu_set_t), set)
#define CPU_CLR(cpu, set)      CPU_CLR_S(cpu, sizeof(cpu_set_t), set)
#define CPU_ISSET(cpu, set)    CPU_ISSET_S(cpu, sizeof(cpu_set_t), set)
#define CPU_COUNT(set)         CPU_COUNT_S(sizeof(cpu_set_t), set)
#define CPU_EQUAL(set1, set2)  CPU_EQUAL_S(sizeof(cpu_set_t), set1, set2)

#define CPU_AND(dst, set1, set2)  __CPU_OP(dst, set1, set2, &)
#define CPU_OR(dst, set1, set2)   __CPU_OP(dst, set1, set2, |)
#define CPU_XOR(dst, set1, set2)  __CPU_OP(dst, set1, set2, ^)

同时,这个头文件还提供两个函数,分别利用这个掩码来设置或者读取CPU的亲和度:

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);

下面就可以书写Native代码实战了:

#include <sched.h>
#include <unistd.h>

int SetCpuAffinity(void *p)
{
    cpu_set_t mask;
    CPU_ZERO(&mask);

    char *cpus = (char *)p;
    int size = 8;
    for(int i=0;i<size;i++)
    {
        char cpu = cpus[i];
        if(cpu == '1')
        {
            CPU_SET(i, &mask);
        }            
    }
    
    pid_t pid = getpid();
    if(sched_setaffinity(pid, sizeof(cpu_set_t), &mask)==-1)
        return -1;
    return 0;
}

int GetCpuAffinity(void *p)
{
    cpu_set_t mask;
    pid_t pid = getpid();
    if(sched_getaffinity(pid, sizeof(cpu_set_t), &mask) == -1)
        return -1;
    char *a = (char *)p;
    for(int i=0;i<CPU_SETSIZE;i++)
    {
        if(CPU_ISSET(i, &mask))
            a[i] = '1';
        else
            a[i] = '0';
    }
    return 0;
}

然后参考文章 Unity 与 NDK 开发 里介绍的方法,将这两个函数暴露给Unity的C#层调用。C#层可以这样使用这两个代码:

IntPtr getP = Marshal.AllocHGlobal(100 * sizeof(char));

string Test_GetCpuAffinity()
{
	GetCpuAffinity(getP);
	string cpuInfo = Marshal.PtrToStringAnsi(getP);
	Debug.Log(cpuInfo);
	return cpuInfo;
}

void Test_SetCpuAffinity()
{
	string cpus = "10000000"; // 绑定到核心0上,剩下的都为0 
	e_SetCpuAffinity(cpus);
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值