67

I'd like to specify the CPU-affinity of a particular pthread. All the references I've found so far deal with setting the CPU-affinity of a process (pid_t) not a thread (pthread_t). I tried some experiments passing pthread_t's around and as expected they fail. Am I trying to do something impossible? If not, can you send a pointer please? Thanks a million.

red0ct
  • 4,840
  • 3
  • 17
  • 44

5 Answers5

73

This is a wrapper I've made to make my life easier. Its effect is that the calling thread gets "stuck" to the core with 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);
}
Eduardo Bezerra
  • 1,923
  • 1
  • 17
  • 32
  • 13
    For future reference: It's needed to add #define _GNU_SOURCE and #include to work on gcc 4.7.2. Worked perfectly on arch linux, tested with oprofile and pthread. – JohnTortugo May 19 '13 at 04:40
  • 2
    Also, `#include ` is needed for `sysconf` with `gcc 4.8.1`. – Brandon Amos Sep 13 '13 at 16:36
  • For some reason it works on my computer with two cores, however on my other computer with 4 cores it gives the following error:
    Segmentation fault (core dumped)
    – oneiros Sep 14 '13 at 22:49
  • Nice. Rather than failing when core_id > num_cores, another parameter could specify the default in that case: `core_id = default_core`; -1 as the default could mean to fail. – Brent Faust Feb 18 '14 at 18:01
  • what is better to use this code or `sched_setaffinity` from @nos answer below? – Oleg Vazhnev Sep 19 '14 at 12:30
  • @javapowered: It's the same either way--`pthread_setaffinity_np` is just a wrapper around `sched_setaffinity()` anyway. – John Zwinck May 03 '16 at 06:47
  • I am not getting core dumped but if I execute priority threads on a single core results are inconsistent. – Moose Sep 07 '18 at 08:14
51

Assuming linux:

The interface to setting the affinity is - as you've probably already discovered:

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

Passing 0 as the pid, and it'll apply to the current thread only, or have other threads report their kernel pid with the linux-specific call pid_t gettid(void); and pass that in as the pid.

Quoting the man page

The affinity mask is actually a per-thread attribute that can be adjusted independently for each of the threads in a thread group. The value returned from a call to gettid(2) can be passed in the argument pid. Specifying pid as 0 will set the attribute for the calling thread, and passing the value returned from a call to getpid(2) will set the attribute for the main thread of the thread group. (If you are using the POSIX threads API, then use pthread_setaffinity_np (3) instead of sched_setaffinity().)

Kevin
  • 16,549
  • 8
  • 60
  • 74
nos
  • 223,662
  • 58
  • 417
  • 506
  • 3
    "If you are using the POSIX threads API, then use pthread_setaffinity_np (3) instead of sched_setaffinity()". How should I know if i'm using POSIX API? How to select use `sched_setaffinity` or `pthread_setaffinity_np`? – Oleg Vazhnev Nov 24 '14 at 10:41
  • in RHEL 7 this is what man says `If pid is zero, then the calling process is used.` (process, not thread) – Oleg Vazhnev Nov 26 '14 at 19:19
  • @javapowered That sentence in the man page is wrong. Read the NOTES section too. – nos Nov 26 '14 at 20:43
  • I am having the same problem but I'm using OS X. Is there a similar method? – Raghav Apr 09 '15 at 03:43
  • @Raghav OS X doesn't expose the functionality to pin threads to specific cores. – zgerd Apr 16 '20 at 01:26
30
//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;
}

In POSIX environment you can use cpusets to control which CPUs can be used by processes or pthreads. This type of control is called CPU affinity.

The function 'sched_setaffinity' receives pthread IDs and a cpuset as parameter. When you use 0 in the first parameter, the calling thread will be affected

ismaia
  • 461
  • 4
  • 4
-5

Please find the below example program to cpu-affinity of a particular pthread.

Please add appropriate libs.

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

Compile above program with -D_GNU_SOURCE flag.

wickedone
  • 542
  • 1
  • 6
  • 18
  • 4
    Your program will work, but there are several issues that I see: 1) pthread_setaffinity_np takes a cpu_set_t, not an unsigned long. One should use the CPU_SET, CPU_ZERO, etc. macros to manipulate the masks before passing to the affinity functions 2) Finally, you don't need to launch a new thread with pthread_create to run the main part of your code – J Teller Apr 27 '12 at 21:25
-7

The scheduler will change the cpu affinity as it sees fit; to set it persistently please see cpuset in /proc file system.

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

Or you can write a short program that sets the cpu affinity periodically (every few seconds) with sched_setaffinity

MichaelMoser
  • 3,172
  • 1
  • 26
  • 26