Re[19]: [2]: : volatile: а можно примеры?
От: MaximE Великобритания  
Дата: 24.01.05 20:34
Оценка: 32 (4)
c-smile wrote:

[]

> Ты пойми, никакого формального же описания "барьеров" в документации VC++ просто нет. Собственно как и ни в каком другом компиляторе.

> Единственное что есть это volatile.

Нашел немного.

http://www.microsoft.com/whdc/driver/kernel/MP_issues.mspx

[q]The volatile Keyword

The volatile keyword in C indicates that the value associated with a variable can be changed by something outside the control of the current thread.
The compiler reads the value of a volatile variable from memory each time the variable is referenced and writes the value of the variable to memory each time it is assigned. Without the volatile keyword, the compiler might optimize access to the variable by combining read or write operations or by reordering references to the variable in code.
...
Details of how to implement the volatile keyword are not specified in the ANSI language standards, but are left to compiler developers. Therefore, implementations may vary from compiler to compiler. Never assume that every C compiler—or even every version of a single manufacturer’s C compiler—implements volatile in the same way.
The Microsoft C compiler generates code for each assignment to or reference of a volatile variable, even if the code appears to have no effect. If you declare a variable as volatile, the compiler considers it volatile for the lifetime of the variable. You can also cast a single reference as volatile; in this case, the compiler is guaranteed to generate code for that reference only.
...
If you look at the sample drivers shipped with the Windows DDK, you will see that volatile appears infrequently. In general, volatile is of limited use in driver code for the following reasons:
• Using volatile prevents optimization only of the volatile variables themselves. It does not prevent optimizations of nonvolatile variables relative to volatile variables. For example, a write to a nonvolatile variable that precedes a read from a volatile variable in the source code might be moved to execute after the read.
• Using volatile does not prevent the reordering of instructions by the processor hardware.
• Using volatile correctly is not enough on a multiprocessor system to guarantee that all CPUs see memory accesses in the same order.
Windows synchronization mechanisms are more useful in preventing all these potential problems.

...
Windows Synchronization Mechanisms
...
• The InterlockedXxx routines perform common arithmetic and logical operations atomically, ensuring correct results on multiprocessor systems. Whenever possible, drivers should use these routines because they are fast. Most of them are native processor instructions that are implemented inline by the compiler and can be called at any IRQL.
...
Memory Barriers and Hardware Reordering
In certain situations on some hardware architectures, the processor can reorder read and write instructions. Furthermore, on multiprocessor architectures, the sequence in which read and write operations are executed can appear different from the perspective of different processors. To prevent hardware reordering, the executable code must contain memory barriers.
If your driver uses only the standard Windows locking mechanisms, you don’t need to be concerned about hardware reordering or memory barriers. All of the standard Windows locking mechanisms (spin locks, mutexes, kernel events, and resources managed by the executive resource package) protect against processor reordering by inserting memory barriers where required in executable code. So do the InterlockedXxx and ExInterlockedXxx functions.
This feature is an important reason to use the Windows mechanisms and to avoid creating your own locks. As a general rule, a driver should never create its own locks except in the rare situations where it has special requirements that make using the Windows mechanisms impractical or undesirable. Driver-created locks must insert memory barriers to prevent hardware reordering in certain conditions. These conditions can be difficult to detect and the problems resulting from them can be extremely tricky to debug.
...
[/q]

И на закуску сладенькое фанатам volatile:

Memory Barrier Semantics
A memory barrier is a processor instruction that preserves the ordering of read and/or write operations from the perspective of any other processor. Memory barriers include processor instructions with acquire, release, and fence semantics. These semantics describe the order in which results of an operation become visible.
• Acquire semantics mean that the results of the operation are visible before the results of any operation that appears after it in code.
• Release semantics mean that the results of the operation are visible after the results of any operation that appears before it in code.
• Fence semantics combine acquire and release semantics. The results of an operation with fence semantics are visible before those of any operation that appears after it in code and after those of any operation that appears before it.
...
On x86 and x64-based hardware (including AMD64 and the Intel Extended Memory 64 Technology), the InterlockedXxx and ExInterlockedXxx functions have both acquire and release semantics by default. The Intel Itanium architecture, however, can execute operations that have either acquire or release semantics (and not both) faster than those that have both. On Windows “Longhorn,” Microsoft plans to provide versions of the InterlockedXxx and ExInterlockedXxx functions that support either acquire or release semantics.
In addition, Microsoft CL 14.0.0 and later versions tighten the restrictions on the types of reordering that can occur around volatile variables. In these compilers, reads from volatile locations are treated as acquires and writes to volatile locations are treated as releases on hardware architectures that support these semantics.


--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9
Re[20]: [2]: : volatile: а можно примеры?
От: MaximE Великобритания  
Дата: 24.01.05 20:50
Оценка:
MaximE wrote:

И postы от Doug Harrison Microsoft MVP — Visual C++
http://groups-beta.google.com/group/microsoft.public.vc.mfc/msg/37c71acb8f8a6feb?dmode=source

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9
Re[8]: volatile у переменной класса
От: maq Россия http://www.maqdev.com
Дата: 21.02.05 09:40
Оценка:
>>> А что на нее глядеть? Она пользуется извне только для отладочных целей (соотв, когда никакой оптимизации и нет).

ME>>Код ф-ций EnterCriticalSection et al компилировался с этим определением ст-ры.


AS>Это заявление сделано на основании анализа исходников, доступных в сети, или же просто домыслы?
<RSDN@Home ME> FooBar2k: silent
Re[19]: [2]: : volatile: а можно примеры?
От: MaximE Великобритания  
Дата: 20.06.05 07:26
Оценка:
Здравствуйте, c-smile, Вы писали:

[]

CS>Ты пойми, никакого формального же описания "барьеров" в документации VC++ просто нет. Собственно как и ни в каком другом компиляторе.

CS>Единственное что есть это volatile.

CS>"Барьер" это некая абстракция верхнего уровня которая имплементируется везде где я только это видел на volatile переменных.



http://msdn2.microsoft.com/library/z055s48f(en-us,vs.80).aspx
http://msdn2.microsoft.com/library/65tt87y8(en-us,vs.80).aspx

The Visual C++ compiler is free to perform any optimization that preserves the meaningful outputs of the program in single-threaded execution. These barriers are provided to block optimization of reads and writes at specific points in a program. This is similar to marking that memory "volatile" only more performant (when usable) because it only forces reads and writes to complete at specific points in a program, rather than globally.


--
Maxim Yegorushkin
Re[20]: [2]: : volatile: а можно примеры?
От: Alexey_ch Швейцария  
Дата: 20.06.05 13:09
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>

ME>The Visual C++ compiler is free to perform any optimization that preserves the meaningful outputs of the program in single-threaded execution. These barriers are provided to block optimization of reads and writes at specific points in a program. This is similar to marking that memory "volatile" only more performant (when usable) because it only forces reads and writes to complete at specific points in a program, rather than globally.


Через полгода мнение насчет volatile не изменилось?
... << RSDN@Home 1.1.4 beta 7 rev. 0>>
Re[14]: volatile у переменной класса
От: Erop Россия  
Дата: 20.06.05 14:27
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>Этот код — некорректный многопоточный код, так как он не применяет синхронизацию для доступа к переменной.

А зачем нужна синхронизация, для этого случая? Ну максимум может быть нужно читать/писать флаг Interkocked функциями.
Но при чём тут синхронизация и волотайл?

А вот читать оно действительно не сможет, пока компилятор точно в память не положит переменную.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[21]: [2]: : volatile: а можно примеры?
От: AndreyT  
Дата: 20.06.05 21:20
Оценка:
Может, я чего в данной теме и просмотрел.
Но хотелось бы напомнить высокому собранию, что volatile растёт из MMIO для PDP-11.
Те, кто DDJ внимательно читают, видимо помнят.
Остальным могу привести ссылку, там в тексте есть вставка под названием "volatile: A Brief History"

http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004.pdf
Re[2]: volatile у переменной класса
От: Аноним  
Дата: 20.06.05 21:29
Оценка: +1 -1
Здравствуйте, MaximE, Вы писали:

ME>Рихтер ошибался.


ME>volatile не имеет никакого отношения к multithreading, поэтому его применение в этой ситуации не только бесполезно, но может быть и вредно, так как с volatile компилятор не сможет соптимизировтать доступ к этой переменной. Но если один из потоков изменяет переменную, синхронизация при помощи мютексов обязательна.



Тут Максим вы не правы. Рихтер не ошибался. И синхронизировать не обящательно. Всё от ситуации зависит.

пример. Имеем два потока один чего то делает и по завершению операции устанавливает переменную m_bDone в true.
второй поток пытается синхронизироваться с первым и крутит пустой цикл до тех пор пока m_bDone != true;

Если переменная не объявлена как volatile то цикл
while(!m_bDone); будет всю жизнь крутиться. Так как компилятор не дурак. Он один раз прочтет m_bDone в регистр (кэш процессора) и потом каждый раз будет его проверять. И то что m_bDone уже давно была установлена другим потоком никогда не узнает.

Если переменая объявлена как volatile то компилятору это команда что каждый раз перед обращением к этой переменной её нужно считать из памяти.

======================================================
И мютексы не нужны, и volatile нужны.


George.
Re[3]: : volatile: а можно примеры?
От: Аноним  
Дата: 20.06.05 21:43
Оценка:
Здравствуйте, MaximE, Вы писали:


ME>С использованием средств синхронизации никаких проблем без volatile нет и быть не должно.


Ну это две разные вещи volatile и синхронизация.

Синхронизация применяется когда два потока могут одновременно изменить значение переменной.
volatile говорит компилятору что надо всегда считать значение переменной из памяти и не кэшировать её.

Ну чем синхронизация поможет если компилятор закэшировал значение в регистр.
пример (псевдокод):

LOCK(m_a)
m_a = 5;
UNLOCK(m_a)

while(true)
{
LOCK(m_a)
if( m_a != 5 )
break;
UNLOCK(m_a)
}

Вот этот цикл будет всю жизнь крутиться даже если второй thread установил m_a = 6.

И всё синхронизированно вроде.


Так что не путайте людей пожалуйста. В MSDN всё хорошо написано.

George.
Re[9]: Изменение errno
От: Аноним  
Дата: 20.06.05 21:58
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>MaximE wrote:


>> Мы все еще ждем от тебя примеров кода, подтверждающие, что при использовании синхронизации нужен еще и volatile.


ME>... или признаем твою неправоту по таймауту


Хоть я и не тот от кого ты ждал примера но я его всё равно привел
http://rsdn.ru/forum/message.1232106.aspx

Снова посторюсь. volatile и Синхронизация две разные вещи. И применять одно или другое никак не связано друг с другом.

George.
Re[13]: [2]: : volatile: а можно примеры?
От: Erop Россия  
Дата: 21.06.05 00:22
Оценка:
Здравствуйте, alnsn, Вы писали:

A>
A>LONG InterlockedExchange(
A>  LONG volatile* Target,
A>  LONG Value
A>);
A>


A>Указатель здесь не volatile. Он всего-лишь указывает на LONG volatile.

A>Здесь это не страшно, но если нужно не только LONG значение менять, но и указатель иногда перекидывать на другое значение, то надо делать так:
A>
LONG volatile* volatile p;

A>Но здесь одним вызовом InterlockedExchange уже не обойдешься.

Интересно, и как эе ты собираешься "перекидывать" формальный параметр функции?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: volatile у переменной класса
От: execve  
Дата: 21.06.05 03:48
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>volatile не имеет никакого отношения к multithreading, поэтому его применение в этой ситуации не только бесполезно, но может быть и вредно, так как с volatile компилятор не сможет соптимизировтать доступ к этой переменной. Но если один из потоков изменяет переменную, синхронизация при помощи мютексов обязательна.


bool flag;

// thread 1
...
flag = false;
while(!flag) {
    ...
    sleep(1);
}
...

//thread 2
...
flag = true;
...


VC6 в процессе оптимизации переносил переменную flag в первом потоке в регистр, в результате чего цикл оказывался вечным.
И был вобщем-то прав.

Можешь добавить мьютексов по вкусу — проблему это не решит.
Re[4]: : volatile: а можно примеры?
От: Alexey_ch Швейцария  
Дата: 21.06.05 07:24
Оценка:
Здравствуйте, <Aiiiei>, Вы писали:

Aii>Синхронизация применяется когда два потока могут одновременно изменить значение переменной.

Aii>volatile говорит компилятору что надо всегда считать значение переменной из памяти и не кэшировать её.

Aii>Ну чем синхронизация поможет если компилятор закэшировал значение в регистр.

Aii>пример (псевдокод):

Volatile придумали давно, когда программисты были умнее компиляторов. На самом деле приведенный тобой код будет работать сегодня (например на VC 7.1) корректно даже без "volatile". Хотя если беспокоиться о портабельности исходников, то лучше это ключевое слово использовать там, где память доступна нескольким потокам одновременно.
... << RSDN@Home 1.1.4 beta 7 rev. 0>>
Re[5]: : volatile: а можно примеры?
От: Аноним  
Дата: 21.06.05 11:35
Оценка:
Здравствуйте, Alexey_ch, Вы писали:

A_>Volatile придумали давно, когда программисты были умнее компиляторов. На самом деле приведенный тобой код будет работать сегодня (например на VC 7.1) корректно даже без "volatile". Хотя если беспокоиться о портабельности исходников, то лучше это ключевое слово использовать там, где память доступна нескольким

потокам одновременно.

Не понял. А почему он будет работать?
Из моего опыта он работать не будет если включена оптимизация.

George.
Re[6]: : volatile: а можно примеры?
От: Alexey_ch Швейцария  
Дата: 21.06.05 12:10
Оценка: +1
Здравствуйте, <Aiiiei>, Вы писали:

Aii>Не понял. А почему он будет работать?

Aii>Из моего опыта он работать не будет если включена оптимизация.

Если LOCK и UNLOCK макросы для системных функций синхронизации, то VC 7.1 всегда генерит корректных код. Он допускает, что внешняя функция может изменить глобальную переменную.

А без функций в циклах оно работает неверно. Может быть VC 6.0 в данной ситуации делает вечный цикл, я не знаю .

Вот мой тестовый пример.

// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//

#pragma once


#include <iostream>
#include <tchar.h>
#include <process.h>
#include <windows.h>
#include <string>



// sync_err_without_volatile.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

int  /*volatile*/ g_counter  = 0;
bool /*volatile*/ g_stopFlag = false;

CRITICAL_SECTION g_cs;

void __inline sub1();
void __inline sub2();

unsigned __stdcall Thread1(void* param)
{
  for (auto int i = 0; i < 100000000; i++)
  {
    g_counter++;
    //sub1();
  }

  EnterCriticalSection(&g_cs);
  g_stopFlag = true;
  fprintf(stdout, "\nThread1 is ended: g_counter = %u", g_counter);
  LeaveCriticalSection(&g_cs);

  return 0;
}

unsigned __stdcall Thread2(void* param)
{
  for (auto int i = 0; (i < 1000000000) && (!g_stopFlag); i++)
  {
    g_counter++;
    //sub2();
  }

  EnterCriticalSection(&g_cs);
  fprintf(stdout, "\nThread2 is ended: g_counter = %u", g_counter);
  LeaveCriticalSection(&g_cs);

  return 0;
}

void __inline sub1 (void)
{
  EnterCriticalSection(&g_cs);
  g_counter++;
  LeaveCriticalSection(&g_cs);
}

void __inline sub2 (void)
{
  EnterCriticalSection(&g_cs);
  g_counter++;
  LeaveCriticalSection(&g_cs);
}


int _tmain(int argc, _TCHAR* argv[])
{
  InitializeCriticalSection(&g_cs);

  unsigned tr_id1, tr_id2;

  HANDLE h1 = (HANDLE) _beginthreadex(NULL, 0, &Thread1, NULL, CREATE_SUSPENDED, &tr_id1);
  HANDLE h2 = (HANDLE) _beginthreadex(NULL, 0, &Thread2, NULL, CREATE_SUSPENDED, &tr_id2);

  ResumeThread(h2);
  ResumeThread(h1);
  
  getchar();
  DeleteCriticalSection(&g_cs);
  return 0;
}
... << RSDN@Home 1.1.4 beta 7 rev. 0>>
Re[3]: volatile у переменной класса
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.06.05 19:26
Оценка:
Здравствуйте, <Аноним>, Вы писали:

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


ME>>Рихтер ошибался.


ME>>volatile не имеет никакого отношения к multithreading, поэтому его применение в этой ситуации не только бесполезно, но может быть и вредно, так как с volatile компилятор не сможет соптимизировтать доступ к этой переменной. Но если один из потоков изменяет переменную, синхронизация при помощи мютексов обязательна.



А>Тут Максим вы не правы. Рихтер не ошибался. И синхронизировать не обящательно. Всё от ситуации зависит.


Имхо, тут как раз не правы вы.

А>пример. Имеем два потока один чего то делает и по завершению операции устанавливает переменную m_bDone в true.

А>второй поток пытается синхронизироваться с первым и крутит пустой цикл до тех пор пока m_bDone != true;

Немного странный способ синхронизации, не находите?

А>Если переменная не объявлена как volatile то цикл

А>while(!m_bDone); будет всю жизнь крутиться. Так как компилятор не дурак. Он один раз прочтет m_bDone в регистр (кэш процессора) и потом каждый раз будет его проверять. И то что m_bDone уже давно была установлена другим потоком никогда не узнает.
А>Если переменая объявлена как volatile то компилятору это команда что каждый раз перед обращением к этой переменной её нужно считать из памяти.

Это уже обсуждалось внутри темы: Реальный пример использования volatile
Автор: What
Дата: 18.01.05
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[3]: volatile у переменной класса
От: Alex Alexandrov США  
Дата: 21.06.05 20:06
Оценка: 48 (3) +2
Здравствуйте, <Аноним>, Вы писали:

А>Если переменная не объявлена как volatile то цикл

А>while(!m_bDone); будет всю жизнь крутиться. Так как компилятор не дурак. Он один раз прочтет m_bDone в регистр (кэш процессора) и потом каждый раз будет его проверять. И то что m_bDone уже давно была установлена другим потоком никогда не узнает.

А>Если переменая объявлена как volatile то компилятору это команда что каждый раз перед обращением к этой переменной её нужно считать из памяти.


А>======================================================

А>И мютексы не нужны, и volatile нужны.

Ох, зря эту ветку подняли... Рекомендую прочитать все перед высказыванием мыслей.

Вкратце:

1. Синхронизация через глобальную переменную без использования примитивов синхронизации или атомарных операций в общем случае некорректна. Казалось бы, какие проблемы могут быть в случае простого применения булевой переменной volatile? Ведь она либо есть, либо ее нет! Могут. Если потоки будут исполняться на разных процессорах одновременно, и процессор обладает способностью и желанием переупорядочивать операции чтения-записи в память, то второй процессор может увидеть изменения не в том порядке, в котором он ожидает. В частности, флажок может быть увиден установленным перед тем, как изменятся синхронизируемые таким способом данные (ой-ой-ой...). Так что volatile недостаточен.

2. Как только вы вставляете в свой многопоточный код вызовы функций синхронизации WaitFor... или Interlocked... они начинают служить барьером времени компиляции для компилятора, форсируя его перезагружать глобальные переменные в регистры процессора, поскольку вызовы этих функций для компилятора — черный ящик и он не может делать предположений о том, меняют они содержимое регистров или нет. Соответственно, WaitFor.../Interlocked... служит заодно двусторонним барьером памяти для процессора.

3. Таким образом, при использовании функций синхронизации, что highly appreciated, можно обойтись без volatile. Главным аргументом сторонников volatile в ветке было возможное "поумнение" компилятора, при котором он вдруг сможет понять, что регистры на самом деле функциями синхронизации не затрагивались и, значит, регистры перезагружать нельзя. Честно говоря, не уверен, что это произойдет когда-нибудь.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
It's kind of fun to do the impossible (Walt Disney)
Re[4]: volatile у переменной класса
От: Аноним  
Дата: 21.06.05 20:12
Оценка:
Здравствуйте, eao197, Вы писали:

E>Имхо, тут как раз не правы вы.


Ну я так понимаю что "Имхо" это очевидно. А я говорю что Очевидно что рихтер прав а MaximE не прав. И что?
то слово очевидно поменяло. Может теперь подерёмся чтоб выяснить прав рихтер или нет (Пытаюсь обяснить что без примеров слова "очевидно" ничего не дают.)

E>Немного странный способ синхронизации, не находите?


Не нахожу. Программы пишуться разными людьми и о вкусах не спорят.

E>Это уже обсуждалось внутри темы: Реальный пример использования volatile
Автор: What
Дата: 18.01.05


что то почитал я понял что люди пришли к согласию что volatile и синхронизация разные вещи.

George.
Re[5]: volatile у переменной класса
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.06.05 21:02
Оценка:
Здравствуйте, <Аноним>, Вы писали:

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


E>>Немного странный способ синхронизации, не находите?


А>Не нахожу. Программы пишуться разными людьми и о вкусах не спорят.


E>>Это уже обсуждалось внутри темы: Реальный пример использования volatile
Автор: What
Дата: 18.01.05


А>что то почитал я понял что люди пришли к согласию что volatile и синхронизация разные вещи.


См. Re[3]: volatile у переменной класса
Автор: Alex Alexandrov
Дата: 22.06.05
-- там очень классное резюме для всей ветки сделано.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[13]: volatile у переменной класса
От: Михаил  
Дата: 22.06.05 03:41
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>В будущем для Intel и в настоящем для многих других процессоров, volatile точно не поможет, так как на Intel он опирается на две processor dependent фичи: на memory ordering и когерентность кешей (cache snooping выше).


Значит, появятся другие фичи. Например, ключи в компиляторе. Понятие volatile есть, и оно обязано быть реализовано. Реализовано так, как написано в MSDN, исходя из чего и применяется в реальных программах. А тонкости стандарта большинство разработчиков не интересует. Никто не будет выпускать проц, под который придется переписывать софт. Тем более Intel. В данном случае истина — мнение большинства.
...А отсюда наливаем, когда рецепт написан совсем неразборчиво...
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.