0

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 を何に設定するのだろうか。
コア番号が単純に割り当てられている場合、つまり、cpu0 には 0 番目のコア、1 番目のコア、2 番目のコア、...、cpu1 には 8 番目のコア、9 番目のコア、... がある場合、これはスレッド ID として設定されます。
一方、コア数がラウンドロビン規則で割り当てられている場合、つまり、cpu0 には 0 番目のコア、4 番目のコア、8 番目のコア、...、および cpu1 には次のコアがあります。 1コア目、5コア目…。

変数 cpu、ナイーブ ルール、またはラウンド ロビン ルールのどのルールを設定する必要がありますか?

4

1 に答える 1

2

Linux (およびその他の OS) では、プログラマは CPU アフィニティ、つまりカーネルがこのプロセスをスケジュールできる許可された CPU を設定できます。fork() 時に、プロセスは親の CPU アフィニティを継承します。これは、何らかの理由で CPU を制限したい場合に非常に便利です。

たとえば、制限する場合があります

  • 特定のユーザーのプロセスを 1 つの CPU のみに割り当て、他のユーザーには残りの CPU を任せます (man 7 cpuset を参照)。
  • 何かに「近い」CPU へのプロセス。たとえば、ネットワーク カード (NIC、または HCA) に直接接続されているソケット上のコアと通信するプロセスを制限します。

一般に、プロセス/スレッドを特定のコアまたはソケットに制限して、OS によってスケジュールされないようにすることが有益な場合があります。これにより、L1/L2 キャッシュ (コアにピニングする場合) または L3 キャッシュの利点が最大化されます。 /LLC キャッシュ (ソケットに固定する場合)。

「スレッド分散」に関するあなたの質問について: プロセッサー開発は、2 つの論理コア (例: Intel Xeon) または 4 つの論理コア (例: Intel Knights Landing、IBM Power ) 物理コアあたり。これらの論理コアは、上記の cpuset では「CPU」としても表されます。さらに、一部のプロセッサは NUMA ドメインを強制します。この場合、1 つのコアからその「独自の」メモリへのメモリ アクセスは高速ですが、別の NUMA ドメイン内の別のコア メモリへのアクセスは低速です...

したがって、上記のコメントのいくつかが示唆するように、それは依存します! スレッドが (共有メモリを介して) 相互に通信する場合、それらは同じキャッシュ内で近くに保持する必要があります。スレッドが同じ機能ユニット (FPU など) を実行している場合、同じ物理コア (2 つの論理コア、つまりハイパースレッド) で 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;
}

編集: OSX 10.5 (Leopard) はhttps://developer.apple.com/library/mac/releasenotes/Performance/RN-AffinityAPI/のようにアフィニティを提供するため、Apple を明確にする必要があります。

于 2016-05-31T12:43:00.580 に答える