Re[5]: Race condition
От: remark Россия http://www.1024cores.net/
Дата: 19.01.09 10:04
Оценка: 1 (1)
Здравствуйте, Alexander G, Вы писали:

AG>Здравствуйте, remark, Вы писали:


R>>Идея такая, что если какой-то глобальный объект использует наш синглтон, то data.h должен быть подключен в этот файл, и data_initializer_ будет определен в этом файле раньше того глобального объекта.


AG>Раньше определён — раньше создан ? Почему ?


Это всегда гарантировалось — если глобальный объект определен раньше другово в единице трансляции, то он и создастся раньше. Это только между разными единицами трансляции порядок не определен.


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[5]: Race condition
От: remark Россия http://www.1024cores.net/
Дата: 19.01.09 10:09
Оценка: +1
Здравствуйте, Alexander G, Вы писали:

AG>Здравствуйте, remark, Вы писали:


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


AG>Может у меня не хватает фантазии, но я не редставляю, как компилятор может испортить. Мне кажется, что такое


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


А смысл, если _ReadWriteBarrier() ничего не стОит в ран-тайм?


AG>>>Ещё, можно ли убрать volatile и заменить его на _WriteBarrier(); перед сохранением указателя ?


R>>Нет.

R>>Ну точнее так — на x86 должно работать. А на IA-64 обязателен аппаратный барьер.

AG>http://msdn.microsoft.com/en-us/library/f20w0x5e(VS.80).aspx

AG>

AG>Marking memory with a memory barrier is similar to marking memory with the volatile (C++) keyword.

AG>?

У них, честно говоря, такая дерьмовая документация, что уже отчаялся в ней что-либо понять. Нет, что б явно и чётко всё написать, всё какими-то намёками выражаются...
По моим представлениям, если бы эти _ReadWriteBarrier/_WriteBarrier/_ReadBarrier были бы и аппаратными барьерами, то на x86 _ReadWriteBarrier должен был бы выливаться в инструкцию mfence. Этого не происходит. Поэтому я считаю, что это — только барьеры компилятора... Хотя интересно было бы поглядеть как _WriteBarrier компилируется на IA-64 — я не знаю...



1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[6]: Race condition
От: Alexander G Украина  
Дата: 19.01.09 13:10
Оценка:
Здравствуйте, remark, Вы писали:

R>У них, честно говоря, такая дерьмовая документация, что уже отчаялся в ней что-либо понять. Нет, что б явно и чётко всё написать, всё какими-то намёками выражаются...

R>По моим представлениям, если бы эти _ReadWriteBarrier/_WriteBarrier/_ReadBarrier были бы и аппаратными барьерами, то на x86 _ReadWriteBarrier должен был бы выливаться в инструкцию mfence. Этого не происходит. Поэтому я считаю, что это — только барьеры компилятора... Хотя интересно было бы поглядеть как _WriteBarrier компилируется на IA-64 — я не знаю...

x86


  int i = 1;
004131AD  mov         dword ptr [ebp-228h],1 
  _ReadWriteBarrier();
  ++i;
004131B7  mov         eax,dword ptr [ebp-228h] 
004131BD  add         eax,1 
004131C0  mov         dword ptr [ebp-228h],eax 
  _ReadWriteBarrier();


  volatile int j = 1;
004131C6  mov         dword ptr [ebp-234h],1 
  ++j;
004131D0  mov         eax,dword ptr [ebp-234h] 
004131D6  add         eax,1 
004131D9  mov         dword ptr [ebp-234h],eax




x64


  int i = 1;
000000014000214B  mov         dword ptr [rsp+248h],1 
  _ReadWriteBarrier();
  ++i;
0000000140002156  mov         eax,dword ptr [rsp+248h] 
000000014000215D  add         eax,1 
0000000140002160  mov         dword ptr [rsp+248h],eax 
  _ReadWriteBarrier();


  volatile int j = 1;
0000000140002167  mov         dword ptr [rsp+24Ch],1 
  ++j;
0000000140002172  mov         eax,dword ptr [rsp+24Ch] 
0000000140002179  add         eax,1 
000000014000217C  mov         dword ptr [rsp+24Ch],eax


никакой mfence на х86 и х64 не нужно ?
Русский военный корабль идёт ко дну!
Re[6]: Race condition
От: Аноним  
Дата: 19.01.09 13:27
Оценка:
Здравствуйте, remark, Вы писали:
...

А какой практический смысл есть в чисто "НЕ аппаратных" барьерах, если, полюбому, на процессоре всё может перемешаться, как ему угодно?
Re[7]: Race condition
От: remark Россия http://www.1024cores.net/
Дата: 19.01.09 20:14
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, remark, Вы писали:

А>...

А>А какой практический смысл есть в чисто "НЕ аппаратных" барьерах, если, полюбому, на процессоре всё может перемешаться, как ему угодно?


Есть.
С их помощью можно (1) упорядочивать между потоком и обработчиком сигнала для этого же потока, (2) упорядочивать между 2 потоками, которые выполняются на одном процессоре, (3) использовать, если упорядочивание на аппаратном уровне выполняется другими средствами (обычно в таком случае барьер компилятора стоит при всех обращениях, т.к. он бесплатный; а цена аппаратного барьера амортизируется на множество обращений; хороший пример RCU в ядре Linux).
Так же стоит отметить, что барьеры компилятора включены в C++0x — там смотри std::atomic_signal_fence(std::memory_order).


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[7]: Race condition
От: remark Россия http://www.1024cores.net/
Дата: 19.01.09 20:16
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Здравствуйте, remark, Вы писали:


R>>У них, честно говоря, такая дерьмовая документация, что уже отчаялся в ней что-либо понять. Нет, что б явно и чётко всё написать, всё какими-то намёками выражаются...

R>>По моим представлениям, если бы эти _ReadWriteBarrier/_WriteBarrier/_ReadBarrier были бы и аппаратными барьерами, то на x86 _ReadWriteBarrier должен был бы выливаться в инструкцию mfence. Этого не происходит. Поэтому я считаю, что это — только барьеры компилятора... Хотя интересно было бы поглядеть как _WriteBarrier компилируется на IA-64 — я не знаю...

AG>x86

AG>x64
AG>никакой mfence на х86 и х64 не нужно ?

На IA-32 и Intel 64 (x86-32/64) барьеры не нужны. А на IA-64 (Itanium) при volatile обращениях ты должен увидеть инструкции ld.acq/st.rel (load-acquire/store-release) вместо обычных ld/st (load/store).


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[8]: Race condition
От: Alexander G Украина  
Дата: 19.01.09 21:14
Оценка:
Здравствуйте, remark, Вы писали:

R>На IA-32 и Intel 64 (x86-32/64) барьеры не нужны. А на IA-64 (Itanium) при volatile обращениях ты должен увидеть инструкции ld.acq/st.rel (load-acquire/store-release) вместо обычных ld/st (load/store).


Ну т.е. нет никаких свидетельств что барьеры "хуже" volatile ?

(ассемблера IA64 не знаю, для IA64 не собираю)

R>
Русский военный корабль идёт ко дну!
Re[9]: Race condition
От: remark Россия http://www.1024cores.net/
Дата: 20.01.09 07:02
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Здравствуйте, remark, Вы писали:


R>>На IA-32 и Intel 64 (x86-32/64) барьеры не нужны. А на IA-64 (Itanium) при volatile обращениях ты должен увидеть инструкции ld.acq/st.rel (load-acquire/store-release) вместо обычных ld/st (load/store).


AG>Ну т.е. нет никаких свидетельств что барьеры "хуже" volatile ?


В смысле хуже?

1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[10]: Race condition
От: Alexander G Украина  
Дата: 20.01.09 07:58
Оценка:
Здравствуйте, remark, Вы писали:

R>В смысле хуже?


Я имею ввиду, что можно заменить voiatile на барьеры, как пишут в MSDN, т.к. машииный код такой же
Русский военный корабль идёт ко дну!
Re[11]: Race condition
От: remark Россия http://www.1024cores.net/
Дата: 20.01.09 15:29
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Здравствуйте, remark, Вы писали:


R>>В смысле хуже?


AG>Я имею ввиду, что можно заменить voiatile на барьеры, как пишут в MSDN, т.к. машииный код такой же


Мммм... ну у меня всё ещё остаётся подозрение, что volatile в MSVC — это и барьер компилятора и аппаратный, в барьеры — это только компилятора. Т.к. иначе не понятно почему _ReadWriteBarrier() не эмитит mfence на x86.
Плюс они, так сказать, ортогональны друг другу. В том смысле, что например при захвате мьютекса нужен барьер типа load-acquire (или #LoadLoad|#LoadStore membar в терминологии SPARC), и volatile load даёт именно это; точно же смоделировать это с помощью барьеров не получается, т.к. _ReadBarrier() и _WriteBarrier() каждый по отдельности недостаточны, а _ReadWriteBarrier() слишком тяжелый (хммм... точнее он бы слишком тяжелым, если бы эмитил mfence, а так вообще не понятно, что это такое). Честно говоря, я чем больше читаю их документацию, тем больше запутываюсь


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[12]: Race condition
От: Alexander G Украина  
Дата: 20.01.09 21:54
Оценка:
Здравствуйте, remark, Вы писали:

R>Т.к. иначе не понятно почему _ReadWriteBarrier() не эмитит mfence на x86.


Зачем там mfence ?

Кстати, почему-то __mf intrinsic только для Itanium. Видимо, кроме инлайн ассемблера никак этот самый _mfence на х86 не сделать. нужен ли он ?
Русский военный корабль идёт ко дну!
Re[13]: Race condition
От: remark Россия http://www.1024cores.net/
Дата: 21.01.09 11:19
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Здравствуйте, remark, Вы писали:


R>>Т.к. иначе не понятно почему _ReadWriteBarrier() не эмитит mfence на x86.


AG>Зачем там mfence ?


Полный барьер на x86 должен отображаться на mfence.


AG>Кстати, почему-то __mf intrinsic только для Itanium. Видимо, кроме инлайн ассемблера никак этот самый _mfence на х86 не сделать. нужен ли он ?


_mm_mfence() в заголовке <intrin.h>. Да, нужен для многих алгоритмов синхронизации.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[9]: Race condition
От: Quasi  
Дата: 30.01.09 09:08
Оценка: 30 (1)
Здравствуйте, Alexander G, Вы писали:

AG>Ну т.е. нет никаких свидетельств что барьеры "хуже" volatile ?


AG>(ассемблера IA64 не знаю, для IA64 не собираю)


R>>


Полностью согласен с remark, однако хотелось бы прояснить некоторые моменты:

Для процессоров IA-32 и Intel 64 (Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium M, Intel Xeon, P6 family, Pentium 4 и более ранних),
AMD64 и более ранних операции чтения из памяти и запись всегда имеют семантику Acquire и Release соответственно (если не считать "string operations" и "streaming stores"). Однако это не мешает и вышеописанные процессоры (кроме совсем ранних) переупорядочивают чтение и предшествующую запись, если они не относятся к одному адресу памяти.

volatile long x = 0;
volatile long y = 0;
....
//Processor A
x = 1;
long r1 = y;
....
//Processor B
y = 1;
long r2 = x;

Из-за возможного переупорядочивания в результате выполнения возможна ситуация, когда r1 = r2 = 0
Некоторые алгоритмы могут полагаться на строгую последовательность операций чтения и предшествующей записи, поэтому реализация барьера для процессора должна содержать либо инструкцию mfence, либо инструкцию с префиксом "LOCK" в случае вышеописанных процессоров. Соответсвенно, по факту функция _ReadWriteBarrier не реализует барьер памяти для процессора, а лишь барьер памяти для компилятора:

A memory barrier prevents the compiler from optimizing memory accesses across the barrier, but enables the compiler to still optimize instructions between barriers.


Единственное, что вводит в ложное заблуждение в документации Microsoft, так это:

Marking memory with a memory barrier is similar to marking memory with the volatile (C++) keyword.


По упорядочиванию процессоров:
Intel® 64 and IA-32 Architectures Software Developer’s Manual (7.2 Memory ordering)
AMD64 Architecture Programmer’s Manual (7.2 Multiprocessor Memory Access Ordering)
Intel® Itanium® Architecture Software Developer’s Manual (Part II, 2.2 Memory Ordering)
Re[10]: Race condition
От: remark Россия http://www.1024cores.net/
Дата: 30.01.09 09:51
Оценка: 4 (1) +1
Здравствуйте, Quasi, Вы писали:

Q>Для процессоров IA-32 и Intel 64 (Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium M, Intel Xeon, P6 family, Pentium 4 и более ранних),

Q>AMD64 и более ранних операции чтения из памяти и запись всегда имеют семантику Acquire и Release соответственно (если не считать "string operations" и "streaming stores"). Однако это не мешает и вышеописанные процессоры (кроме совсем ранних) переупорядочивают чтение и предшествующую запись, если они не относятся к одному адресу памяти.

АФАИК, они переупорядочивают даже обращения к одному адресу памяти. Свои обращения процессор-то, конечно, увидит в программном порядке, а вот другие процессоры могут увидеть обращения к одному адресу памяти в отличном от программного.
Единственное, что сейчас видимо не переупорядочивается (хотя АФАИК это не документировано) — это запись и чтение по одному адресу, но с разными размерами (т.н. collocation trick):

volatile char x = 0;
volatile char y = 0;

// thread 1
x = 1;
short r1 = *(volatile short*)&x;

// thread 2
y = 1;
short r2 = *(volatile short*)&x;



Q>Из-за возможного переупорядочивания в результате выполнения возможна ситуация, когда r1 = r2 = 0

Q>Некоторые алгоритмы могут полагаться на строгую последовательность операций чтения и предшествующей записи, поэтому реализация барьера для процессора должна содержать либо инструкцию mfence, либо инструкцию с префиксом "LOCK" в случае вышеописанных процессоров. Соответсвенно, по факту функция _ReadWriteBarrier не реализует барьер памяти для процессора, а лишь барьер памяти для компилятора:
Q>

Q>A memory barrier prevents the compiler from optimizing memory accesses across the barrier, but enables the compiler to still optimize instructions between barriers.


Q>Единственное, что вводит в ложное заблуждение в документации Microsoft, так это:

Q>

Marking memory with a memory barrier is similar to marking memory with the volatile (C++) keyword.


А почему это вводит в заблуждение? Ведь volatile в данном случае тоже не приведёт к добавлению mfence/lock.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[11]: Race condition
От: Quasi  
Дата: 30.01.09 10:18
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, Quasi, Вы писали:


Q>>Для процессоров IA-32 и Intel 64 (Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium M, Intel Xeon, P6 family, Pentium 4 и более ранних),

Q>>AMD64 и более ранних операции чтения из памяти и запись всегда имеют семантику Acquire и Release соответственно (если не считать "string operations" и "streaming stores"). Однако это не мешает и вышеописанные процессоры (кроме совсем ранних) переупорядочивают чтение и предшествующую запись, если они не относятся к одному адресу памяти.

R>АФАИК, они переупорядочивают даже обращения к одному адресу памяти. Свои обращения процессор-то, конечно, увидит в программном порядке, а вот другие процессоры могут увидеть обращения к одному адресу памяти в отличном от программного.

В общем, с этой точки зрения да, такое поведение документированно.


R>А почему это вводит в заблуждение? Ведь volatile в данном случае тоже не приведёт к добавлению mfence/lock.

MSDN, volatile:
This allows volatile objects to be used for memory locks and releases in multithreaded applications

В то время как, _ReadWriteBarrier (

is similar to marking memory with the volatile (C++) keyword

) никакого отношения к упорядочиванию операций в памяти процессором не имеет, по факту.


R>

Re[12]: Race condition
От: Alexander G Украина  
Дата: 02.02.09 13:14
Оценка:
Здравствуйте, Quasi, Вы писали:

R>>А почему это вводит в заблуждение? Ведь volatile в данном случае тоже не приведёт к добавлению mfence/lock.

Q>

Q>MSDN, volatile:
Q>This allows volatile objects to be used for memory locks and releases in multithreaded applications

Q>В то время как, _ReadWriteBarrier (

is similar to marking memory with the volatile (C++) keyword

) никакого отношения к упорядочиванию операций в памяти процессором не имеет, по факту.


Т.е. надо вставлять _mm_fence вручную ?

struct data_t
{
  void * p1;
  void * p2;
  int  i3;
  ...
} data = {0};

data_t* p_data = 0;

data_t* init()
{
    if (p_data)
        return p_data;
    _ReadWriteBarrier();
    data.p1 = ::GetModuleHandle(...);
    data.p2 = ::GetProcAddress(...);
    data.i3 = (int)sqrt(42*_WIN_VER);
    _mm_mfence();
    p_data = &data;
    return p_data;
}
Русский военный корабль идёт ко дну!
Re[13]: Race condition
От: Quasi  
Дата: 02.02.09 16:54
Оценка: 30 (1)
Здравствуйте, Alexander G, Вы писали:

AG>Т.е. надо вставлять _mm_fence вручную ?


AG>
AG>struct data_t
AG>{
AG>  void * p1;
AG>  void * p2;
AG>  int  i3;
AG>  ...
AG>} data = {0};

AG>data_t* p_data = 0;

AG>data_t* init()
AG>{
AG>    if (p_data)
AG>        return p_data;
AG>    _ReadWriteBarrier();
AG>    data.p1 = ::GetModuleHandle(...);
AG>    data.p2 = ::GetProcAddress(...);
AG>    data.i3 = (int)sqrt(42*_WIN_VER);
AG>    _mm_mfence();
AG>    p_data = &data;
AG>    return p_data;
AG>}
AG>


Принципиальным здесь является наличие зависимости по данным, которая гарантирует, что на всех современных процессорах чтения указателя p_data и данных по указателю не будут переупорядочены. Однако, это не налагает ограничений на переупорядочивание записи p_data и данных. В данном случае для предотвращения переупорядочивания достаточно записи указателя p_data c release семантикой (на x86, Intel 64 и AMD64 для этого ничего дополнительно не требуется). Если верить документации Microsoft по квалификатору volatile, то для переносимости на другие аппаратные платформы можно воспользоваться трюком, который описал remark:
//.....
*((data_t* volatile*)(&p_data)) = &data;
return p_data;

На других компиляторах без соответствующих acquire, release семантик для чтения, записи volatile данных можно использовать атомарную операцию, аналогичную ::InterlockedExchange в WINAPI, имеющую либо release, либо fence семантику (full memory barrier, как в случае с ::InterlockedExchange).
Re[14]: Race condition
От: Alexander G Украина  
Дата: 02.02.09 17:18
Оценка:
Здравствуйте, Quasi, Вы писали:

Q>Здравствуйте, Alexander G, Вы писали:


AG>>Т.е. надо вставлять _mm_fence вручную ?


AG>>
AG>>struct data_t
AG>>{
AG>>  void * p1;
AG>>  void * p2;
AG>>  int  i3;
AG>>  ...
AG>>} data = {0};

AG>>data_t* p_data = 0;

AG>>data_t* init()
AG>>{
AG>>    if (p_data)
AG>>        return p_data;
AG>>    _ReadWriteBarrier();
AG>>    data.p1 = ::GetModuleHandle(...);
AG>>    data.p2 = ::GetProcAddress(...);
AG>>    data.i3 = (int)sqrt(42*_WIN_VER);
AG>>    _mm_mfence();
AG>>    p_data = &data;
AG>>    return p_data;
AG>>}
AG>>


Q> В данном случае для предотвращения переупорядочивания достаточно записи указателя p_data c release семантикой (на x86, Intel 64 и AMD64 для этого ничего дополнительно не требуется).


Я не зочу volatile т.к. его смысл зависит от компилятора.
Может тогда так ?
data_t* init()
{
    if (p_data)
       return p_data;
    _ReadWriteBarrier();
    data.p1 = ::GetModuleHandle(...);
    data.p2 = ::GetProcAddress(...);
    data.i3 = (int)sqrt(42*_WIN_VER);
#if defined(_M_IX86) || defined(_M_IA64) || defined(_M_X64)
    // soft write barrier is enough
    _WriteBarrier();
    p_data = &data;
#else
    #error add a proper barrier for this architecture
#endif
    return p_data;
}
Русский военный корабль идёт ко дну!
Re[15]: Race condition
От: Quasi  
Дата: 02.02.09 17:57
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Я не зочу volatile т.к. его смысл зависит от компилятора.

AG>Может тогда так ?
AG>
AG>data_t* init()
AG>{
AG>    if (p_data)
AG>       return p_data;
AG>    _ReadWriteBarrier();
AG>    data.p1 = ::GetModuleHandle(...);
AG>    data.p2 = ::GetProcAddress(...);
AG>    data.i3 = (int)sqrt(42*_WIN_VER);
AG>#if defined(_M_IX86) || defined(_M_IA64) || defined(_M_X64)
AG>    // soft write barrier is enough
AG>    _WriteBarrier();
AG>    p_data = &data;
AG>#else
AG>    #error add a proper barrier for this architecture
AG>#endif
AG>    return p_data;
AG>}
AG>


Как раз для семейства Intel Itanium (_M_IA64) барьера компилятора не достаточно, под Windows проще всего:
    //.....
    ::InterlockedExchangePointer(reinterpret_cast<void**>(&p_data), &data);
Re[16]: Race condition
От: Quasi  
Дата: 02.02.09 18:04
Оценка:
Здравствуйте, Quasi, Вы писали:

Q>Здравствуйте, Alexander G, Вы писали:



Q>Как раз для семейства Intel Itanium (_M_IA64) барьера компилятора не достаточно, под Windows проще всего:

Q>
Q>    //.....
Q>    ::InterlockedExchangePointer(reinterpret_cast<void**>(&p_data), &data);
Q>


MSDN, InterlockedExchangePointer:

This function generates a full memory barrier (or fence) to ensure that memory operations are completed in order.

Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.