Переходите на C, выбросьте уже этого динозавра
Или на fasm.
Так уже, им и пользуюсь. Практически доволен.
Нужны, если команда использует чтение+запись, даже если она одна.
Тут я говорил про чтение счётчиков для вывода статистики, не для их изменения. Читать типы поддерживаемые аппаратно (те .укладывающиеся в одну команду
записи) можно без блокировок. Всё равно всё сведётся к обмену строками кэша, а операции записи в строку кэша командами делаются атомарно (внутри процессора), даже когда записи от нескольких команд объединяются в пакеты. Правда если записываемые без lock данные пересекают границу строк кэша уже не уверен что будет ... Скорее всего глюк.
Компилятор добавляет lock так как данные глобальные и выполняется операция
записи, тут без него никак. Чтение всегда можно делать без блокировки (ну, пока работает MOESI протокол кэшей конечно). Правда знает ли об этом компилятор я не в курсе.
Ваше желание убрать lock понятно, хотя иногда может привести к глюкам. Именно из-за того что inc/dec mem транслируются в 4 микрокоманды (на моём проце), не 1. И не вклинится ли чужая микрокоманда чтения между родной чтения и родной же записи - неизвестно. Но для 8/16/32/64 битов это не слишком принципиально (если данные не пересекают границу линии кэша!), ведь будет всего лишь прочитано старое значение счётчика и потеряется одно увеличение ...
Возможностей для глюка я вижу две:
а) если слово пересекает 64 байт границу, тогда части слова могут в какой-то момент оказаться от старого и нового значения счётчика, что эквивалентно временному выбросу вверх/вниз на 256/65536/16777216 единиц счётчика, очень редко, но возможно, если в этот момент влезет команда модификации из другого потока будет совсем каша;
б) нарушится последовательность микрокоманд чтения и записи в разных потоках, будет не чтение-запись-чтение-запись, а чтение-чтение-запись-запись, тут сбой всего на 1.
И они могут произойти и одновременно ...
Потому всё что
пишет в глобальную память из
нескольких потоков (оба выделенных слова критичны) - должно выполняться либо атомарно (lock), либо через арбитраж (семафоры, критические секции, чего там ещё). А так как арбитраж это не атомарная запись, то под него приходится убирать и чтения тоже.
Подозреваю что Вы это всё знаете, но пусть будет для остальных.
И опять же, для локальных для процесса данных lock не нужен и добавляться не должен (если не встроен в команду типа xchg, но вы их не используете), так что решение с локальными счётчиками должно быть рабочим. Не слишком удобным, да.
Замедление вдвое от счётчиков производительности это конечно нонсенс. Может стоит выкинуть 90% из них и профилировать частями, запуская много раз, для разных кусков кода? Или речь про онлайн (на лету) профилирование, прямо в процессе? Можно попытаться выделить самым нагруженным счётчикам по аппаратному регистру (компиляторы C вроде можно заставить не использовать регистры по списку, хотя не уверен). Или разместить счётчики или в отдельной области памяти (4к страницам) или наоборот поближе к данным что обрабатываются вокруг них, в те же строки кэша, усложнив структуру данных (внеся счётчики прямо в неё).
Извините, но я больше ничем помочь не могу.
Судя по рассказу, Вы знаете побольше моего. Даже захотелось вопросики по GPU позадавать ...
-- 23.11.2024, 22:57 --Это я тоже пробовал. Как и комбинации с volatile. Все равно компилятор часто разбивает операцию, если мы несколько раз подряд обращаемся к одной и той же переменной - пытается уменьшить кол-во обращений к памяти.
С volatile насколько знаю не имеет права, они всегда должны писаться в память и только оттуда читаться, для каждой операции, никаких кэширований в регистрах. Именно ради многопоточности (и модификаций в прерываниях или вообще аппаратно).
-- 23.11.2024, 23:09 --Что бы из-за когерентной синхронизации кешей последние минимально бы сбрасывались. Это дало почти +50% производительности.
Ещё это меня поразило, я сколько ни прикидывал (до прямых опытов не доходило), но время чтения всего L2 даже из памяти составляло доли процента от тика переключения задач (18мс или пусть даже 1мс), порядка десятка мкс. Может у вас очень много данных и в L2 не влезают? Или много обращений к ОС и L2 перегружается много-много (более сотни) раз за тик? Странно. Нельзя ли обойтись без вызовов ОС подольше ...