Установите сродство к процессору при создании потока

Я хочу создать поток С++ 11, который я хочу, чтобы он работал на моем первом ядре. Я нахожу, что pthread_setaffinity_np и sched_setaffinity могут изменять близость процессора к потоку и переносить его на указанный CPU. Однако эта спецификация слияния изменяется после запуска потока.

Как создать поток С++ 11 с определенным сродством к процессору (объект cpu_set_t)?

Если невозможно определить сродство при инициализации потока С++ 11, , как я могу это сделать с помощью pthread_t в C?

Моя среда - g++ на Ubuntu. Кусок кода оценен.

Ответ 1

Я сожалею, что здесь был "мифологический бастер", но установление привязанности потоков имеет большое значение, и со временем оно становится все более значительным, поскольку системы, которые все мы используем, становятся все более и более NUMA (неравномерной архитектурой памяти) по своей природе, Даже тривиальный сервер с двойным гнездом в эти дни имеет ОЗУ, подключенную отдельно к каждому сокету, и существенное различие в доступе к памяти от сокета к его собственному ОЗУ и к соседнему процессорному сокету (удаленному ОЗУ). В ближайшем будущем процессоры попадают на рынок, в котором внутренний набор ядер является NUMA сам по себе (отдельные контроллеры памяти для отдельных групп ядер и т.д.). Мне не нужно повторять работу других здесь, просто ищите "NUMA и нить сродство" онлайн, и вы можете узнать из многолетнего опыта других инженеров.

Не задавая привязку нити, она фактически равна "надежде", что планировщик ОС правильно справится с привязкой потоков. Позволь мне объяснить: У вас есть система с некоторыми узлами NUMA (области обработки и памяти). Вы начинаете поток, и поток выполняет некоторые вещи с памятью, например. malloc некоторая память, а затем процесс и т.д. Современная ОС (по крайней мере, Linux, другие, вероятно, тоже) делает хорошую работу до сих пор, память по умолчанию выделяется (если доступна) из того же домена CPU, где работает поток, Придите время, OS с разделением времени (вся современная ОС) поставит поток спать. Когда поток возвращается в рабочее состояние, он может быть запущен на любом из ядер в системе (так как вы не установили для него маску сродства), и чем больше ваша система, тем выше вероятность того, что она будет "проснуться" на CPU, который удален от ранее выделенной или используемой памяти. Теперь все ваши обращения к памяти будут удалены (не уверен, что это означает для вашей производительности приложения). Узнайте больше о доступе к удаленной памяти в системах NUMA в Интернете)

Итак, чтобы подвести итог, интерфейсы настройки аффинности ОЧЕНЬ важны при запуске кода в системах с более чем тривиальной архитектурой, которая в наши дни быстро становится "любой системой". Некоторые среды среды выполнения /libs позволяют управлять этим во время выполнения без какого-либо конкретного программирования (см. OpenMP, например, в версии Intel для реализации переменной среды KMP_AFFINITY), и было бы правильным, чтобы разработчики С++ 11 включали аналогичные механизмы в их исполняемые библиотеки и языковые параметры (и до тех пор, если ваш код предназначен для использования на серверах, я настоятельно рекомендую вам внедрить управление сродством в вашем коде)

Ответ 2

В С++ 11 вы не можете установить сродство потока при создании потока (если только функция, выполняемая в потоке, сама по себе), но как только поток создается, вы можете установить сродство через любой родной интерфейс, который вы получили, получив собственный дескриптор потока (thread.native_handle()), поэтому для Linux вы можете получить идентификатор pthread с помощью:

pthread_t my_thread_native = my_thread.native_handle();

Затем вы можете использовать любой из вызовов pthread, проходящих в my_thread_native, где ему нужен идентификатор потока pthread.

Обратите внимание, что большинство объектов потока специфичны для реализации, то есть pthreads, потоки Windows, собственные потоки для других ОС, все имеют свой собственный интерфейс и набирают эту часть вашего кода, не будут очень переносимыми.

Ответ 3

Да, есть способ сделать это. Я сталкивался с этим методом в этом блоге ссылка

И все остальные ответы на этот вопрос не имеют примера. Вот как написать код

  std::vector<std::thread> threads(num_threads);
  for (unsigned i = 0; i < num_threads; ++i) {
    threads[i] = std::thread([&iomutex, i] {
    std::this_thread::sleep_for(std::chrono::milliseconds(900));
      }
    });

    // Create a cpu_set_t object representing a set of CPUs. Clear it and mark
    // only CPU i as set.
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(i, &cpuset);
    int rc = pthread_setaffinity_np(threads[i].native_handle(),
                                    sizeof(cpu_set_t), &cpuset);
    if (rc != 0) {
      std::cerr << "Error calling pthread_setaffinity_np: " << rc << "\n";
    }
  }

Весь кредит должен быть отдан автору блога Эли Эндерскому, и ссылка была вставлена выше.

Ответ 4

После некоторого времени поиска кажется, что мы не можем установить сродство к процессору при создании С++ thread.

Причина в том, что при создании потока существует НЕТ НЕТ, чтобы указать сродство. Итак, зачем делать это на языке.

Скажем, мы хотим, чтобы рабочая нагрузка f() была привязана к CPU0. Мы можем просто изменить близость к CPU0 прямо перед реальной рабочей нагрузкой, вызвав pthread_setaffinity_np.

Однако мы CAN указываем сродство при создании потока на C. (спасибо комментарию от Tony D). Например, следующий код выводит "Hello pthread".

void *f(void *p) {
  std::cout<<"Hello pthread"<<std::endl;
}

cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset);
pthread_attr_t pta;
pthread_attr_init(&pta);
pthread_attr_setaffinity_np(&pta, sizeof(cpuset), &cpuset);
pthread_t thread;
if (pthread_create(&thread, &pta, f, NULL) != 0) {
    std::cerr << "Error in creating thread" << std::endl;
}
pthread_join(thread, NULL);
pthread_attr_destroy(&pta);