如何设置特定pthread的CPU亲和性?

68
我想要指定一个特定 pthread 的 CPU 亲和性。到目前为止,我找到的所有参考资料都是关于设置进程(pid_t)的 CPU 亲和性,而不是线程(pthread_t)。我尝试了一些实验,传递 pthread_t,但如预期的那样失败了。我是在试图做一些不可能的事情吗?如果不是,请您给我一个指针好吗?非常感谢。
5个回答

75

这是我做的一个包装,以使我的生活更加轻松。它的作用是将调用线程“粘”在具有id core_id的核心上:

// core_id = 0, 1, ... n-1, where n is the system's number of cores

int stick_this_thread_to_core(int core_id) {
   int num_cores = sysconf(_SC_NPROCESSORS_ONLN);
   if (core_id < 0 || core_id >= num_cores)
      return EINVAL;

   cpu_set_t cpuset;
   CPU_ZERO(&cpuset);
   CPU_SET(core_id, &cpuset);

   pthread_t current_thread = pthread_self();    
   return pthread_setaffinity_np(current_thread, sizeof(cpu_set_t), &cpuset);
}

13
供以后参考:在使用gcc 4.7.2时需要添加#define _GNU_SOURCE和#include <sched.h>。在arch linux上可以完美运行,经过oprofile和pthread测试。 - JohnTortugo
2
另外,在使用gcc 4.8.1时需要包含#include <unistd.h>以支持sysconf - Brandon Amos
由于某种原因,它在我的双核电脑上可以运行,但是在我的其他四核电脑上会出现以下错误:<pre>Segmentation fault (core dumped)</pre> - oneiros
不错。与其在core_id > num_cores时失败,另一个参数可以指定在这种情况下的默认值:core_id = default_core; -1作为默认值可能意味着失败。 - Brent Faust
使用这段代码还是从@nos的答案中使用sched_setaffinity更好? - Oleg Vazhnev
显示剩余2条评论

52

假设您使用的是Linux:

设置CPU亲和性的接口——就像你可能已经发现的那样,是:

int sched_setaffinity(pid_t pid,size_t cpusetsize,cpu_set_t *mask);

将0作为pid传递,它将仅适用于当前线程,或者使用Linux特定的调用pid_t gettid(void);使其他线程报告其内核pid并将其作为pid传入。

引用man页面

亲和力掩码实际上是每个线程的属性,可以独立地为线程组中的每个线程调整。调用gettid(2)的返回值可以作为pid参数传递。指定pid为0将设置调用线程的属性,而传递调用getpid(2)的返回值将设置线程组的主线程的��性。(如果您使用POSIX线程API,则应使用pthread_setaffinity_np(3)代替sched_setaffinity()。)


3
如果您正在使用POSIX线程API,则应使用pthread_setaffinity_np(3)而不是sched_setaffinity()。要确定是否正在使用POSIX API,可以查看您的代码中是否包含头文件pthread.h。在选择使用sched_setaffinitypthread_setaffinity_np时,建议优先选择后者,因为它支持更多的平台和实现方式。 - Oleg Vazhnev
在RHEL 7中,man页面上指出:如果pid为零,则使用调用进程(而非线程)。 - Oleg Vazhnev
@javapowered,man页面中的那个句子是错误的。也要阅读NOTES部分。 - nos
我遇到了同样的问题,但我正在使用OS X操作系统。有类似的方法吗? - Raghav
@Raghav OS X没有暴露将线程固定到特定核心的功能。 - zgerd

31
//compilation: gcc -o affinity affinity.c -lpthread

#define _GNU_SOURCE
#include <sched.h>   //cpu_set_t , CPU_SET
#include <pthread.h> //pthread_t
#include <stdio.h>

void *th_func(void * arg); 

int main(void) {
  pthread_t thread; //the thread

  pthread_create(&thread,NULL,th_func,NULL); 

  pthread_join(thread,NULL);   

  return 0;
}


void *th_func(void * arg)
{  
  //we can set one or more bits here, each one representing a single CPU
  cpu_set_t cpuset; 

  //the CPU we whant to use
  int cpu = 2;

  CPU_ZERO(&cpuset);       //clears the cpuset
  CPU_SET( cpu , &cpuset); //set CPU 2 on cpuset


  /*
   * cpu affinity for the calling thread 
   * first parameter is the pid, 0 = calling thread
   * second parameter is the size of your cpuset
   * third param is the cpuset in which your thread will be
   * placed. Each bit represents a CPU
   */
  sched_setaffinity(0, sizeof(cpuset), &cpuset);

  while (1);
       ; //burns the CPU 2

  return 0;
}

在POSIX环境下,您可以使用cpusets来控制进程或pthread可以使用哪些CPU。 这种控制被称为CPU亲和性。

函数'sched_setaffinity'接收pthread ID和cpuset作为参数。 当您在第一个参数中使用0时,调用线程将受到影响。


-5
请查看下面的示例程序,以设置特定pthread的CPU亲和性。
请添加适当的库。
double waste_time(long n)
{

    double res = 0;
    long i = 0;
    while (i <n * 200000) {
        i++;
        res += sqrt(i);
    }
    return res;
}

void *thread_func(void *param)
{

    unsigned long mask = 1; /* processor 0 */

    /* bind process to processor 0 */
    if (pthread_setaffinity_np(pthread_self(), sizeof(mask),
        &mask) <0) {
        perror("pthread_setaffinity_np");
    }

    /* waste some time so the work is visible with "top" */
    printf("result: %f\n", waste_time(2000));

    mask = 2;   /* process switches to processor 1 now */
    if (pthread_setaffinity_np(pthread_self(), sizeof(mask),
        &mask) <0) {
        perror("pthread_setaffinity_np");
    }

    /* waste some more time to see the processor switch */
    printf("result: %f\n", waste_time(2000));
}


int main(int argc, char *argv[])
{

    pthread_t my_thread;

    if (pthread_create(&my_thread, NULL, thread_func, NULL) != 0) {
        perror("pthread_create");
    }
    pthread_exit(NULL);
}

使用 -D_GNU_SOURCE 标志编译上述程序。


4
您的程序可以运行,但我发现几个问题: 1)pthread_setaffinity_np需要一个cpu_set_t类型的参数,而不是unsigned long类型。在传递给亲和力函数之前,应使用CPU_SET、CPU_ZERO等宏来操作掩码。 2)最后,您不需要使用pthread_create启动新线程运行代码的主要部分。 - J Teller

-7

调度程序会根据需要更改CPU亲和性;要持久设置,请参见/ proc文件系统中的cpuset。

http://man7.org/linux/man-pages/man7/cpuset.7.html

或者您可以编写一个短程序,使用sched_setaffinity周期性地(每几秒钟)设置CPU亲和性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接