Вопрос про Thread.MemoryBarrier
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 24.09.11 05:23
Оценка:
Привет всем.

Есть два метода
object m_Options;

string get_data()
{
 var opt=m_Options;

 Thread.MemoryBarrier();

 if(opt==null)
  return null;

 return opt.ToString();
}//get_data

void set_data(object Options)
{
 Interlocked.Exchange(ref m_Options,Options)
}

Я правильно понимаю, что вызов MemoryBarrier предотвращает возможность трансформации get_data (в процессе компиляции с оптимизацией) в код вида
string get_data()
{
 if(m_Options==null)
  return null;

 return m_Options.ToString();
}//get_data

?
Заранее благодарю за ответы
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Вопрос про Thread.MemoryBarrier
От: drol  
Дата: 24.09.11 07:15
Оценка: -1
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Я правильно понимаю, что вызов MemoryBarrier предотвращает возможность трансформации get_data (в процессе компиляции с оптимизацией) в код вида


Нет, Вы всё понимаете абсолютно неправильно.

MemoryBarrier не имеет (почти) никакого отношения к оптимизациям компилятора. Более того, в данном примере совершенно очевидно, что "код вида" однопоточно неэквивалентен исходному коду — бо там два обращения к m_Options против одного в оригинале. И поэтому никакая оптимизация — хоть компилятора, хоть процессора — такую трансформацию сделать не может.
Re[2]: Вопрос про Thread.MemoryBarrier
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 24.09.11 07:32
Оценка:
Здравствуйте, drol, Вы писали:

КД>>Я правильно понимаю, что вызов MemoryBarrier предотвращает возможность трансформации get_data (в процессе компиляции с оптимизацией) в код вида


D>Нет, Вы всё понимаете абсолютно неправильно.


D>MemoryBarrier не имеет (почти) никакого отношения к оптимизациям компилятора. Более того, в данном примере совершенно очевидно, что "код вида" однопоточно неэквивалентен исходному коду — бо там два обращения к m_Options против одного в оригинале. И поэтому никакая оптимизация — хоть компилятора, хоть процессора — такую трансформацию сделать не может.


Ага. Ну вообщем, я так тоже думал... Но потом подумал — а вдруг ...

Смотрю в FW примеры с использованием Thread.MemoryBarrier() и мой моск воспринимает это именно как борьбу с "оптимизацией" в виде удаления локальных переменных

Например.
//System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
    ConcurrentDictionary<TKey, TValue>.Node[] buckets = this.m_buckets;
    for (int i = 0; i < buckets.Length; i++)
    {
        ConcurrentDictionary<TKey, TValue>.Node node = buckets[i];
        Thread.MemoryBarrier();
        while (node != null)
        {
            yield return new KeyValuePair<TKey, TValue>(node.m_key, node.m_value);
            node = node.m_next;
        }
    }
    yield break;
}


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

Содержимое m_Options константно.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: Вопрос про Thread.MemoryBarrier
От: drol  
Дата: 24.09.11 12:47
Оценка: 10 (1) +1
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Например.


Долго смотрел на исходный код ConcurrentDictionary — даже баг в ReSharper'е встретил, но зачем там прикручены вызовы MemoryBarrier так и не понял.

Содержимое массива на который ссылаются this.m_buckets\buckets может изменяться на ходу, да. Но обращение к потрохам элемента — node.m_key, node.m_value, node.m_next — зависимые операции, и без предварительного чтения buckets[i] они невозможны. Зачем тут явно задавать порядок ???

Вобщем надо товарища remark'а звать.

КД>Мне, вообщем-то, нужна гарантия что значение из m_Options сначала будет прочитана в локальную переменную, а потом мы будем работать именно с этой локальной переменной.


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

КД>Содержимое m_Options константно.


А это неважно. Важно кто и как обращается к m_Options. Если туда из разных потоков ходят, то всё нужно очень внимательно посмотреть на предмет обеспечения корректной синхронизации.
Re[4]: Вопрос про Thread.MemoryBarrier
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 24.09.11 14:33
Оценка:
Здравствуйте, drol, Вы писали:

D>Я полагаю, что на самом деле Вам нужно совсем другое Поэтому лучше озвучьте задачу, которую Вы пытаетесь решить.


Ну могу продублировать код еще раз
object m_Options;

string get_data()
{
 var opt=m_Options;

 Thread.MemoryBarrier();

 if(opt==null)
  return null;

 return opt.ToString();
}//get_data

void set_data(object Options)
{
 Interlocked.Exchange(ref m_Options,Options)
}

Мне нужно запретить превращение get_data в код вида
string get_data()
{
 if(m_Options==null)
  return null;

 return m_Options.ToString();
}//get_data

Я смотрел в дизассемблере релизный оптимизированный код. Там все нормально и без MemoryBarrier. Но чего-то меня сомнения терзают

Была мысль завести какую нибудь неинлайновую функцию вида
T return_arg<T>(T x)
{
 return x;
}

И заюзать её так
string get_data()
{
 var opt=return_arg(m_Options);

 if(opt==null)
  return null;

 return opt.ToString();
}//get_data


Обнаружил готовую функцию — Thread.VolatileRead(ref Object). Но, блин, нет VolatileRead<T>(ref T)

Если явно указать
volatile object m_Options;


То компилятор ругается на Interlocked.Exchange — "ссылка на изменяемое поле не будет считаться изменяемой"

----
Поэтому решил спросить здесь по поводу MemoryBarrier
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[5]: Вопрос про Thread.MemoryBarrier
От: drol  
Дата: 24.09.11 17:50
Оценка: :)
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Ну могу продублировать код еще раз


Ну а я могу Вам ещё раз повторить, что дело не в m_Options\get_data\set_data, а в том кто и как их будет использовать. И только зная сие можно понять, что должно быть написано в потрохах.

КД>Была мысль завести какую нибудь неинлайновую функцию вида

КД>
КД>...
КД>
КД>Обнаружил готовую функцию — Thread.VolatileRead(ref Object).

Вы сейчас занимаетесь разновидностью архитектурной астронавтики вперемешку с гаданием на кофейной гуще...

КД>То компилятор ругается на Interlocked.Exchange — "ссылка на изменяемое поле не будет считаться изменяемой"


Здесь это ложное срабатывание. Модификатор volatile требует от компилятора специальной схемы кодогенерации для чтения\записи поля — с отключением\ограничением оптимизаций\реордеринга, добавлением барьеров и т.п. Если же передавать поле в качестве ref-аргумента некоторому методу, то в общем случае внутри этого метода кодогенерация для такого аргумента будет обычная. Однако семантика конкретно Interlocked.Exchange() включает в себя соответствующую семантику volatile для аргументов, и посему всё нормально.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.