Здравствуйте, YesSSS, Вы писали:
YSS>Если верить манам, то начиная с версии 2.1.91 glibc, т.е. 2000-2001 года.
Спасибо. Может в ближайшем будущем портирую программу на С++, там попробую использовать эти функции.
YSS>Кстати респект за реализацию, и вопрос вдогонку: сильно ли будут отличаться результаты при другой привязке нитей к ядрам,
Изначально моя реализация проводила 2 встречи последовательно, т.е. использовались только 2 ядра. Когда я привязывал потоки к ядрам 0 и 1, время выполнения было 4.8 сек. Когда я привязал потоки к ядрам 0 и 2, время выполнения стало 1.2 сек. Т.е. разница в 4 раза, много это или мало — судите сами.
YSS>и если так — почему тогда эти две группы ядер не объедены в numa-ноды, для выяснения их структуры есть удобный интерфейс в линуксе.
В NUMA узлы они не объединены, т.к. это не NUMA узлы. NUMA узла характеризуются различным временем доступа к памяти, для ядер в составе Q6600 это не так, т.е. время доступа к любой памяти с любого ядра одинакового. Зато отличается стоимость взаимодействия между ядрами. На последних процессорах Intel в составе ядра так же имеются 2 HT потока, это совсем другая песня. Мешать всё это в одну кучу нельзя, т.к. структура системы/связей не однородная. Допустим есть программа, в которой потоки мало взаимодействуют между собой, но зато постоянно работают с различными объектами в памяти; для такой программы необходимо определить структру NUMA узлов, а ядра и HT потоки не интересны. В другой программе потоки постоянно взаимодействуют между собой, и этой программе уже нужно различать ядра. Другая программа производит вычисления и с фиксированной точкой и с плавающей, ей было бы полезно знать какие логические процессоры являются HT потоками-близнецами, а ядра и NUMA узлы ей не нужны.
Почему будет не достаточно введение лишь одной метрики (абстрактной «удалённости» между логическими процессорами)? Традиционная оптимизация для NUMA машин — это создание пулов памяти для каждого NUMA узла. Если мы будем знать только абстрактную удалённость между процессорами, то нам придётся создавать больше пулов, чем это необходимо. Соответственно будет больше потребление памяти и больше издержки на передачу блоков памяти между пулами. Другой пример — программа производит вычисления и с фиксированной точкой и с плавающей, для неё целесообразно проводить разнотипные вычисления на HT потоках-близнецах; если же HT потоков нет в системе, то наоборот сгруппировать вычисления по типу, т.к. однотипные вычисления работают с одними данными (допустим на одном многоядерном процессоре — целочисленные, а на другом — с плавающей точкой). Очевидно, что это не укладывается в модель лишь с одной метрикой удалённости.
YSS>Просто как-то логично привязываться к структуре памяти, а вот к структуре кэшей — imho перебор.
Это зависит.
Во-первых, различие при неправильной привязке я уже озвучил.
Во-вторых, кто-то может вам совершенно законно сказать — просто как-то логично оптимизировать IO, а вот привязываться к структуре памяти — перебор.
С т.з. производительности систему можно представить иерархически (в смысле издержек) следующим образом:
IO (disk/network) ↔ Memory ↔ Cache ↔ Processor
Первая задача при оптимизации — выявить наивысший уровень, который является слабым звеном. Почему наивысший? Потому что там можно получить наибольший выигрыш, или даже правильнее сказать — потому что только там и можно получить хоть какой-то выигрыш. Бессмысленно оптимизировать под процессор (распределение регистров, спец. команды), когда есть проблемы с IO. Бессмысленно оптимизировать требования по пропускной способности кэш↔процессор, когда требования по пропускной способности память↔кэш слишком высоки. Зато для каких-то функций обработки массивов данных самый подходящий уровень оптимизации может быть даже не кэш, а процессор.
В общем, нет такого, что всегда надо оптимизировать Х. Оптимизировать надо то, что является узким местом и за счёт чего можно получить выигрыш. Для данной задачи — это латентность взаимодействия между ядрами.
В-третьих, для некоторых задач/программ привязка к структуре памяти не даст вообще ничего, а привязка к кэшам даст. Т.е. строго говоря тут даже нет строгого отношения "превосходства" между ними, в смысле что вначале надо оптимизировать привязку к памяти и только потом — кэши.