2009-06-16 3 views
11

Мы сталкиваемся с проблемами производительности, и одним из потенциальных виновников является централизованное использование волатильного синглтона. конкретный код имеет видаКакова стоимость ключевого слова 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-полосное поле, и мы видим контекст переключение на сумму 500000 в секунду. типичные системные ресурсы в порядке: 25% -ный процессор, 25% -ный доступ к памяти, низкий уровень ввода-вывода, отсутствие поискового вызова и т. д.

с использованием энергозависимого поля индуцирует барьер памяти или перезагрузку любого типа процессора? или он просто идет после основной памяти каждый раз, только для этого поля?

+0

нет, это пример foo. – kolosy

+0

Я не уверен, почему это должно быть изменчивым, поскольку вы не меняете ссылку. –

+0

Я тоже не - это чей-то код, который я отлаживаю. мой главный вопрос, однако, заключается в том, может ли использование volatile здесь вызвать проблемы конкуренции на многоядерном блоке x86. – kolosy

ответ

4

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

В соответствии с this site:

C# летучих орудия ключевого слова приобретать и освободить семантику, что предполагает барьер памяти для чтения на чтения и запись барьер памяти на записи.

+0

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

+0

Это редактирование делает всю разницу в мире :). Благодарю. – kolosy

+1

Vance Morrison также охватывает DCL в этой статье MSDN - http://msdn.microsoft.com/en-us/magazine/cc163715.aspx –

1

К сожалению, синглтон принимает плохой рэп для почти всех :)

Это не моя область знаний, но, насколько я знаю, что нет ничего особенного летучих кроме компилятора/запуска -time НЕ переупорядочивать чтение/запись (в переменную) для целей оптимизации.

Редактировать: Я стою исправлено. Мало того, что волатильность создает барьеры памяти, но то, что происходит (и, кстати, производительность), во многом зависит от конкретного процессора. См. http://dotnetframeworkplanet.blogspot.com/2008/11/volatile-field-and-memory-barrier-look.html

Вот почему вам по-прежнему нужен замок.

Вопросы, которые могут/не может быть дан ответ уже:

  1. Что вы одноточечно экземпляр на самом деле делать? Возможно, код экземпляра необходимо реорганизовать ...
  2. Какое количество потоков работает? 8-way box не поможет вам, если у вас есть аномально высокий поток.
  3. Если это выше, чем ожидалось, почему?
  4. Что еще работает в системе?
  5. Является ли проблема с производительностью последовательной?
+0

1. Не много. это контейнер для некоторых данных, которые должны быть доступны по всему миру. 2. количество потоков соответствует. переключение контекста/второе смехотворно высокое. 500,000/s 3. хороший вопрос. 4. ничего. 5. да – kolosy

0

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

Существует действительно нет необходимости заблокировать одноплодный в этом случае, вы можете просто сделать

class Foo { 
    static Foo instance = new Foo(); 
    public static Foo FooInstance() { 
    return instance ; 
    } 
} 

Ofcourse, если «экземпляр» используется в большом количестве разных потоков, то все равно придется lock() все, что изменяет этот Foo, если только все методы/свойства Foo не используются только для чтения. , например.

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 
     } 
    } 
0

Там действительно нет необходимости в использовании энергозависимого для одноточечного, так как вы устанавливаете его ровно один раз - и запирание код наборов его. См. Jon Skeet's article on singletons для получения дополнительной информации.

+0

DCL без барьера памяти на считывании «Третья версия - попытка (и неудача) безопасности потока с помощью двойной проверки блокировки .. * Без каких-либо барьеров памяти она также нарушена в спецификации ECMA CLI. * ». Код OPs нечетен тем, что он может возвращать * multiple * foo (вместо того, чтобы возвращать «экземпляр», который представляет собой * другую проблему *). В этом старом сообщении (2006) нет никаких указаний на то, что DCL * без защиты памяти * безопасно для одиночных игроков (опять же, ошибка в коде ..?) – user2864740

+0

http://benbowen.blog/post/cmmics_iii/ - см. примечание к «.Net memory model 2.0» «гарантирует». – user2864740

3

Одна вещь, которую нестабильность не будет, это вызвать контекстный переключатель. Если вы видите 500 000 переключателей контекста в секунду, это означает, что ваши потоки блокируют что-то, а волатильность - это не виновник.

0

Короткий ответ: Да, он создает барьер памяти (сбрасывает все и переходит в основную память, а не только одну переменную), но нет, это не будет причиной переключения контекста.

Кроме того, как уже упоминалось, я не считаю, что здесь требуется нестабильность.

+0

'volatile' необходим для * обеспечения * безопасности потока (это не единственный вариант, но тот, который требуется в базовом шаблоне DCL Singleton). Это предотвращает переупорядочение (и обеспечивает чтение «не-устаревших»), когда код * не * получает блокировку: если код * всегда * вступил в замок, не было бы пользы от 'volatile'. – user2864740

+0

Я согласен с вашим утверждением, но в приведенном выше примере используется блокировка, которая включает в себя барьеры памяти и предотвращает переупорядочение. Или я что-то путаю? – gbasin