Какова стоимость ключевого слова volatile в многопроцессорной системе?
мы сталкиваемся с проблемами производительности, и одним из потенциальных виновников является централизованное использование волатильного синглтона. конкретный код имеет вид
class foo {
static volatile instance;
static object l = new object();
public static foo Instance {
if (instance == null)
lock(l) {
if (instance == null)
instance = new foo();
}
return foo();
}
}
это работает на 8-way box, и мы видим, что переключение контекста на 500 000 в секунду. типичные системные ресурсы в порядке - 25% процессор, 25% памяти, низкий уровень ввода-вывода, отсутствие поискового вызова и т.д.
использует ли измененное поле барьер памяти или перезагрузка любого типа процессора? или он просто идет после основной памяти каждый раз, только для этого поля?
Ответы
Ответ 1
lock
вызывает барьер памяти, поэтому, если вы всегда получаете доступ к экземпляру в блокировке, вам не нужна летучая способность.
Согласно этот сайт:
Ключевое слово С# volatile реализует семантику получения и выпуска, что подразумевает барьер чтения с чтением и барьер памяти записи при записи.
Ответ 2
Одна вещь, которую volatile не будет делать, это вызвать контекстный переключатель. Если вы видите 500 000 переключателей контекста в секунду, это означает, что ваши потоки блокируют что-то, а изменчивость не является виновником.
Ответ 3
К сожалению, синглтон берет плохой рэп почти для всего:)
Это не моя область знаний, но, насколько я знаю, ничего особого в volatile, кроме компилятора/времени выполнения, не переупорядочивает чтение/запись (для переменной) для целей оптимизации.
Изменить: Я стою исправлено. Мало того, что волатильность создает барьеры памяти, но то, что происходит (и, кстати, производительность), во многом зависит от конкретного процессора.
См. http://dotnetframeworkplanet.blogspot.com/2008/11/volatile-field-and-memory-barrier-look.html
Вот почему вам все равно нужен замок.
Вопросы, на которые может/не быть ответили уже:
- Каков ваш экземпляр oneton на самом деле? Возможно, код экземпляра нужно реорганизовать...
- Каков счетчик потока запущенного процесса? 8-way box не поможет вам, если у вас есть аномально высокий уровень потока.
- Если это выше, чем ожидалось, почему?
- Что еще работает в системе?
- Является ли проблема производительности согласованной?
Ответ 4
В вашем примере здесь volatile не должен быть предметом какого-либо "замедления".
Однако lock() может включать в себя огромное количество обращений к ядру, особенно если для блокировки существует много конфликтов.
В этом случае нет необходимости блокировать ваш синглтон, вы могли бы просто
do
class Foo {
static Foo instance = new Foo();
public static Foo FooInstance() {
return instance ;
}
}
Конечно, если "экземпляр" используется во множестве разных потоков, вам все равно придется блокировать() все, что изменяет этот Foo, если только все методы/свойства Foo не являются readonly.
например.
class Foo {
static Foo instance = new Foo();
object l = new object();
int doesntChange = 42;
int canChange = 123;
public static Foo FooInstance() {
return instance ;
}
public void Update(int newVal) {
lock(l) { // you'll get a lot of trouble without this lock if several threads accesses the same FOO. Atleast if they later on read that variable
canChange = newVal;
}
public int GetFixedVal() {
return doesntChange; //no need for a lock. the doesntChange is effectivly read only
}
}
Ответ 5
На самом деле нет необходимости использовать volatile для singleton, поскольку вы устанавливаете его ровно один раз - и блокировка кода устанавливает его. См. статья Джона Скита о одиночных играх для получения дополнительной информации.
Ответ 6
Короткий ответ: Да, он создает барьер памяти (сбрасывает все и переходит в основную память, а не только одну переменную), но нет, это не будет причиной переключения контекста.
Кроме того, как отмечали другие, я не считаю, что здесь требуется нестабильность.