如何通过sched_setaffinity设置多个cpus的亲和力

时间:2016-05-30 10:55:49

标签: c linux setthreadaffinitymask

我想在sched_affinity之间按照以下方式设置多个CPU的亲和力。

void
pin(pid_t t, int cpu)
{
  cpu_set_t cpuset;
  CPU_ZERO(&cpuset);
  CPU_SET(cpu, &cpuset);
  sched_setaffinity(t, sizeof(cpu_set_t), &cpuset);
}

我的环境是32个核心,其中存在4个CPU,单个CPU有8个核心 我希望线程0~7在同一个cpu上运行,并且线程8~15在同一个cpu上运行,依此类推。
我想知道在CPU_SET中设置变量cpu是什么 这被设置为线程id,如果核心号被分配为天真的,即cpu0具有第0个核,第1个核,第2个核,......,并且cpu1具有第8个核,第9个核,.......
一方面,如果核心号被分配为循环规则,则cpu被设置为循环规则,即cpu0具有第0核,第4核,第8核,...,和cpu1具有循环规则第1核心,第5核心......,

我应该在哪个规则中设置变量cpu,naive rule或round-robin rule?

1 个答案:

答案 0 :(得分:2)

在Linux(和其他OS')下,程序员可以设置CPU亲和性,即内核可以安排此过程所允许的CPU。在fork()时,进程继承父CPU亲缘关系。 如果想要出于某种原因限制CPU,这非常方便。

E.g。一个可能会限制

  • 某些用户只有一个CPU的进程,而其他用户只剩下CPUS(参见man 7 cpuset)。
  • 对某个“更接近”某事物的CPU的过程,例如限制与套接字上的核心进行通信的进程,该进程直接连接到网卡(NIC或HCA)。

通常,将进程/线程限制到某些核心或套接字可能是有益的,以便不让它们被OS调度 - 最大化L1 / L2高速缓存的优势(当固定到核心时)或L3 / LLC缓存(当固定到套接字时)。

关于“线程分发”的问题: 处理器开发引入了对称多线程(SMT)或超线程(英特尔称为),每个物理核心引入2个逻辑核心(例如Intel Xeon)甚至4个逻辑核心(例如Intel Knights Landing,IBM Power)。 这些逻辑核心在上面的cpuset中也表示为“CPU”。 此外,一些处理器强加了NUMA域,其中从一个内核到其“自己的”内存的内存访问速度很快,而在另一个NUMA域中访问另一个内核内存的速度较慢......

所以,正如上面的一些评论所暗示的那样:这取决于! 您的线程是否相互通信(通过共享内存),然后它们应保持在同一缓存中。 您的线程是否运用相同的功能单元(例如FPU),然后在同一物理内核(具有2个逻辑内核,即超线程)上调度两个线程可能对性能不利。

要玩游戏,请附上以下代码:

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <pthread.h>

// The following is Linux-specific
#include <syscall.h>            // For syscall to gettid()
#include <sched.h>      // sched_[gs]etaffinity require _GNU_SOURCE

#define ERROR(t, e) do { \
    const int __error = (e); \
    fprintf (stderr, "ERROR: %s error:%d [%s] errno:%d [%s]\n", \
             (t), __error, strerror(__error), errno, strerror(errno)); \
    exit(__error); \
  } while(0)

#ifndef MAX
#define MAX(a,b)  ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a,b)  ((a) < (b) ? (a) : (b))
#endif



/* Local function definitions */
void print_schedaffinity(const char * text, const cpu_set_t cpuset, const int max_cpus);
void * thread_func(void * arg);

/* Local type definitions */
struct thread_data {
  pthread_t thread;
  int max_cpu;
  int thread_num;
  void * thread_work;
};

/* The highest value for CPU to be specified in cpuset in a call to
 * sched_setaffinity -- otherwise, we get returned -1 and errno==EINVAL
 */
static int max_cpu_available = 0;


/* Local function declarations */
void print_schedaffinity(const char * text, const cpu_set_t cpuset, const int max_cpus) {
  const int max = MIN(8*sizeof(cpu_set_t), max_cpus);
  int i;

  printf("PRINT CPU AFFINITY %s:\n", text);
  printf("cpus:\t");
  for (i = 0; i < max; i++) {
    printf (" %3d", i);
    if (i % 8 == 7)
      printf(" | ");
  }

  printf("\nmask:\t");
  for (i = 0; i < max; i++) {
    if (CPU_ISSET(i, &cpuset))
      printf ("   X");
    else
      printf ("    ");

    if (i % 8 == 7)
      printf(" | ");
  }
  printf("\n");
}


void * thread_func(void * arg) {
  struct thread_data * thread_data = (struct thread_data *)arg;
  const size_t sizeof_cpuset = sizeof(cpu_set_t);
  char print_buffer[64];
  cpu_set_t cpuset;
  long tid;
  int rc;

  CPU_ZERO(&cpuset);
  CPU_SET(thread_data->thread_num % max_cpu_available, &cpuset);

  /* We set the affinity of the CALLING thread, aka 0 */
  tid = syscall(SYS_gettid);
  printf("PID:%ld tid:%ld thread_num:%d\n",
         getpid(), tid, thread_data->thread_num);
  rc = sched_setaffinity(0, sizeof_cpuset, &cpuset);
  if (0 != rc)
    ERROR("sched_setaffinity", rc);


  /* Dooo SCHTUF now */

  /* Somewhat sort the output... */
  sleep (thread_data->thread_num);

  snprintf (print_buffer, sizeof(print_buffer),
            "in thread %d after sched_setaffinity", thread_data->thread_num);

  print_schedaffinity(print_buffer, cpuset, 8);

  return NULL;
}


int main (int argc, char * argv[])
{
  const int NUM = 8;
  const pid_t pid = getpid();
  const size_t size_cpu_set = sizeof(cpu_set_t);
  cpu_set_t cpuset;
  int rc;
  int i;

  /* Get, and print the original CPU affinity setting (scheduling is not limited, i.e. all cores may run this PID) */
  CPU_ZERO (&cpuset);
  rc = sched_getaffinity(pid, size_cpu_set, &cpuset);
  if (0 != rc)
    ERROR("sched_getaffinity", rc);
  print_schedaffinity("in main", cpuset, 8);

  /* Search for the last / highest cpu being set -- claim, that this is the max cpu to be set, cough */
  for (i = 0; i < 8 * size_cpu_set; i++) {
    if (!CPU_ISSET(i, &cpuset)) {
      max_cpu_available = i;
      break;
    }
  }


  /* Limit the process to the first core, only */
  CPU_ZERO (&cpuset);
  CPU_SET (0, &cpuset);
  rc = sched_setaffinity (pid, size_cpu_set, &cpuset);
  if (0 != rc)
    ERROR("sched_setaffinity", rc);
  print_schedaffinity("in main after sched_setaffinity", cpuset, 8);


  /* Let's start NUM threads and have them limit their scheduling */
  sleep(1);
  struct thread_data * thread_data = (struct thread_data*)malloc(sizeof(struct thread_data) * NUM);
  for (i = 0; i < NUM; i++) {
    thread_data[i].thread_num = i;
    pthread_create (&thread_data[i].thread, NULL, thread_func, &thread_data[i]);
  }

  /* And wait for them to finish... */
  for (i = 0; i < NUM; i++) {
    pthread_join (thread_data[i].thread, NULL);
  }
  return 0;
}

编辑:应该澄清Apple,因为OSX 10.5(Leopard)提供亲和力,如https://developer.apple.com/library/mac/releasenotes/Performance/RN-AffinityAPI/