Почему общее состояние ухудшает производительность?

19

Я работал по принципу параллельного программирования без разделения ресурсов. По сути, все мои рабочие потоки имеют неизменные доступные только для чтения копии одного и того же состояния, которое никогда не передается между ними ( даже по ссылке ). Вообще говоря, это сработало очень хорошо.

Теперь кто-то ввел одноэлементный кэш без блокировки ( например, статический словарь ), к которому все потоки обращаются одновременно. Поскольку словарь никогда не изменяется после запуска, блокировки отсутствуют. Проблем с безопасностью потоков не было, но теперь наблюдается снижение производительности.

Вопрос заключается в том, что, поскольку нет блокировок, почему появление этого синглтона приводит к снижению производительности? Что именно происходит под одеялом, что могло бы объяснить это?

Чтобы подтвердить, доступ к этому новому синглтону - единственное изменение, и я могу надежно воссоздать его, просто закомментировав вызов кеша.

JoeGeeky
источник
8
Вы указали профилировщик на код?
Тимо Гюш
2
Профилирование вряд ли ответит на этот вопрос, если вы не профилируете CLR и, возможно, ядро ​​Windows (это не простая задача для обычного программиста).
Игби Крупный человек
1
@JoeGeeky Ладно, тогда, я думаю, единственное, что нужно сделать для меня, это +1 и одобрение! Это кажется странным, так как они оба имеют одинаковый уровень косвенности и все равно должны помещаться в кэш процессора и т. Д.
Макс.
2
FWIT Я породил пару потоков и запустил несколько таймеров. Я создал экземпляр класса, singleton, lockedSingleton и dict <string, string>. После первого экземпляра каждого последовательного запуска потребовалось около 2000 нс для любого данного объекта. Словарь работал в 2 раза медленнее, может быть вызван кодом конструктора ... он медленнее, чем сама блокировка. Учитывая весь сборщик мусора, обработку ОС очередей потоков и другие издержки ... не уверен, что можно действительно ответить на этот вопрос. Но, исходя из моих результатов, я не верю, что проблема связана с Singletons. Нет, если он реализован как в MSDN. Исключает оптимизацию компилятора.
P.Brian.Mackey
1
@JoeGeeky - еще одна мысль: добавляет ли использование кэша уровень косвенности? При частом доступе поиск дополнительного разыменования указателя (или эквивалента MSIL) может добавить некоторое время к локальной менее косвенной копии.
SDG

Ответы:

8

Возможно, что неизменное состояние разделяет строку кэша с чем-то изменяемым. В этом случае изменение соседнего изменяемого состояния может привести к принудительной повторной синхронизации этой строки кэша между ядрами, что может снизить производительность.

Эйдан Калли
источник
3
Это похоже на false sharingсценарий, который вы описываете. Чтобы изолировать это, мне нужно профилировать L2 Cache. К сожалению, это ссылочные типы, поэтому добавление буферного пространства не будет возможным, если это действительно происходит.
JoeGeeky
3

Я хотел бы убедиться , что Equals()и GetHashCode()методы объектов , которые используются в качестве ключей в словарь не имеют какие - либо неожиданные без поточных людей побочных эффектов. Профилирование очень помогло бы здесь.

Если по какой-либо причине ваши ключи являются строками, то, возможно, у вас есть это: ходят слухи, что строки ведут себя как неизменяемые объекты, но ради определенных оптимизаций они внутренне реализованы изменяемым образом, со всем, что это влечет за собой в отношении многопоточности ,

Я бы попытался передать словарь темам, которые используют его как обычную ссылку, а не как одиночку, чтобы увидеть, связана ли проблема с общностью или с единственностью словаря. (Устранение возможных причин.)

Я бы также попробовал использовать ConcurrentDictionaryвместо обычного Dictionaryна случай, если его использование даст неожиданные результаты. Есть много вещей, которые можно рассуждать о проблеме, если ConcurrentDictionaryокажется, что она работает намного лучше или намного хуже, чем ваша обычная Dictionary.

Если ни один из вышеперечисленных не указывает на проблему, то я бы предположил, что снижение производительности вызвано каким-то странным конфликтом между потоком сбора мусора и остальными вашими потоками, поскольку сборщик мусора пытается выяснить, объекты в вашем словаре должны быть расположены или нет, пока к ним обращаются ваши потоки.

Майк Накис
источник