Связывание двух потоков в последующих группах потоков с одним и тем же ядром
В этом приложении у меня есть группы из N (POSIX) потоков. Первая группа запускается, создает объект A и завершает работу. Чуть позже запускается новая группа с N нитями, которая использует A для создания аналогичного объекта B и закрывается. Эта картина повторяется. Приложение очень интенсивно использует память (A и B имеют большое количество массивов malloc'а). Я бы хотел локальный доступ к памяти как можно больше. я могу использовать numactl --localalloc
чтобы добиться этого, но для того, чтобы это работало, мне также нужно убедиться, что те потоки из первой и второй группы, которые работают с одними и теми же данными, связаны с одним и тем же узлом NUMA. Я смотрел в sched_setaffinity
Интересно, существуют ли лучшие подходы?
Логика приложения такова, что решение, в котором нет отдельных групп потоков, разорвало бы логику программы. Таким образом, решение, при котором одна группа потоков управляет первым объектом A и более поздним объектом B (без наложения промежуточного значения), было бы чрезвычайно надуманным и уничтожило бы объектно-ориентированное расположение кода.
1 ответ
Связывание потоков в группе B с теми же ядрами, которые они запускали в группе A, является более строгим, чем то, что вам нужно. Современные процессоры используют выделенный кэш 1-го уровня (L1) и кэш-память 2-го уровня (L2) на ядро, поэтому привязка потоков к конкретному ядру имеет смысл только для получения данных, которые все еще "горячие" в этих кэшах. Вероятно, вы имели в виду привязку потоков группы B к тому же узлу numa, что и потоки в группе A, чтобы большие массивы находились в одной и той же локальной памяти.
Тем не менее, у вас есть два варианта:
- Вы устанавливаете сходство группы A с конкретным узлом numa и используете тот же самый узел numa, чтобы установить сходство группы B, или
- Вы выясняете, в каком узле numa находятся ваши malloc'-массивы, и устанавливаете привязку группы B к этому узлу numa.
Вариант (1) относительно прост, поэтому давайте поговорим о том, как реализовать вариант (2).
Следующий ответ SO описывает, как узнать, учитывая виртуальный адрес в вашем процессе, какой узел numa имеет эту локальную память:
Могу ли я получить узел NUMA по адресу указателя (в C на Linux)?
В -lnuma есть функция move_pages: http://linux.die.net/man/2/move_pages которая может сообщать о текущем состоянии адреса (страницы) в сопоставлениях узлов:
узлы также могут иметь значение NULL, и в этом случае move_pages() не перемещает никакие страницы, а вместо этого вернет узел, в котором в настоящий момент находится каждая страница, в массиве состояния. Получение статуса каждой страницы может быть необходимо для определения страниц, которые необходимо переместить.
Вооружившись этой информацией, вы хотите установить привязку потоков вашей группы B к этому узлу numa, чтобы узнать, как это сделать, мы переходим к этому ответу SO
Как обеспечить, чтобы std::thread создавался в многоядерном режиме?
для GNU/linux с потоками POSIX вам понадобится pthread_setaffinity_np(), в FreeBSD cpuset_setaffinity(), в Windows SetThreadAffinityMask() и т. д.