Требуется ли энергозависимость здесь? - PullRequest
5 голосов
/ 16 октября 2010

Отредактировал и уточнил мой вопрос после ценного ответа Йоханнеса

bool b = true;
volatile bool vb = true;    
void f1() { }
void f2() { b = false; }

void(* volatile pf)() = &f1; //a volatile pointer to function

int main()
{
   //different threads start here, some of which may change pf 
   while(b && vb)
   {
      pf();
   }
}

Итак, давайте на время забудем о синхронизации.Вопрос в том, должен ли b быть объявлен изменчивым.Я прочитал стандарт и вроде как знаю формальное определение изменчивой семантики (я даже почти понимаю их, слово почти является ключевым).Но давайте будем здесь неформальными.Если компилятор видит, что в цикле нет способа изменить b, то, если b не является изменчивым, он может оптимизировать его и предположить, что он эквивалентен while(vb).Вопрос в том, что в этом случае pf сам по себе является изменчивым, поэтому разрешено ли компилятору предполагать, что b не изменится в цикле, даже если b не является изменчивым?

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

Надеюсь, мой вопрос ясен.TIA

Редактирование еще раз:
что по этому поводу?

bool b = true;
volatile bool vb = true;
void f1() {}
void f2() {b = false;}
void (*pf) () = &f1;

#include <iosrteam>
int main()
{
   //threads here

   while(b && vb)
   {
      int x;
      std::cin >> x;
      if(x == 0)
         pf = &f1;
      else
         pf = &f2;    
      pf();
   } 
}

Есть ли принципиальная разница между двумя программами.Если да, в чем разница?

Ответы [ 3 ]

3 голосов
/ 16 октября 2010

Вопрос в том, что в этом случае pf сам по себе является энергозависимым, поэтому разрешено ли компилятору предполагать, что b не изменится в цикле, даже если b не является энергозависимым?

Это невозможно, потому что вы говорите, что pf может быть изменено другими потоками, и это косвенно меняет b, если pf вызывается затем циклом while.Таким образом, хотя теоретически не требуется читать b в обычном режиме, на практике он должен прочитать его, чтобы определить, должно ли оно произойти короткое замыкание (когда b становится false, он не должен читать vb в другой раз).


Ответ на вторую часть

В этом случае pf больше не является энергозависимым, поэтому компилятор может избавиться от него и увидеть, что f1 имеетпустое тело и f2 устанавливает b в ложь.Он может оптимизировать main следующим образом

int main()
{
   // threads here (which you say can only change "vb")

   while(vb)
   {
      int x;
      std::cin >> x;
      if(x != 0)
         break;    
   } 
}

Ответ на более раннюю ревизию

Одно условие для компилятора, которому разрешено оптимизировать цикл, эточто цикл не имеет доступа к изменчивому объекту и не изменяет его (см. [stmt.iter] p5 в n3126).Вы делаете это здесь, поэтому он не может оптимизировать цикл.В C ++ 03 компилятору не разрешалось оптимизировать даже энергонезависимую версию этого цикла (но компиляторы все равно это делали).

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

2 голосов
/ 17 октября 2010

Точные требования к volatile в текущем стандарте C ++ в подобном случае, насколько я понимаю, не совсем четко определены стандартом, поскольку стандарт на самом деле не касается многопоточности. Это в основном подсказка компилятора. Поэтому вместо этого я расскажу о том, что происходит в типичном компиляторе.

Сначала предположим, что компилятор компилирует ваши функции независимо, а затем связывает их вместе. В любом примере у вас есть цикл, в котором вы проверяете переменную и вызываете указатель на функцию. В контексте этой функции компилятор понятия не имеет, что будет делать функция, стоящая за указателем на функцию, и поэтому он всегда должен повторно загружать b из памяти после вызова. Таким образом, volatile здесь не имеет значения.

Расширяя это до вашего первого фактического случая, и позволяя компилятору выполнять оптимизацию всей программы, поскольку pf является изменчивым, компилятор все еще не знает, на что он будет указывать (он даже не может предположить, что это либо f1 или f2!), И, следовательно, также не может делать какие-либо предположения о том, что будет неизменным при вызове указателя функции - и поэтому volatile на b все еще не имеет значения.

Ваш второй случай на самом деле проще - vb в нем красная сельдь. Если вы устраните это, вы увидите, что даже в полностью однопоточной семантике вызов функции-указателя может изменить b. Вы ничего не делаете с неопределенным поведением, и поэтому программа должна работать правильно без volatile - помните, что, если вы не рассматриваете ситуацию с настройками внешних потоков, volatile - это запрет. Следовательно, без vb на рисунке вам, возможно, не понадобится volatile, и совершенно очевидно, что добавление vb ничего не меняет.

Таким образом, в итоге: вам не нужно volatile в любом случае. Разница между ними заключается в том, что в первом случае, если fp не является энергозависимым, достаточно продвинутый компилятор может оптимизировать b, тогда как во втором случае он не может работать даже без энергозависимости в программе во втором случае. , На практике я не ожидаю, что какие-либо компиляторы действительно выполнят эту оптимизацию.

0 голосов
/ 16 октября 2010

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

В вашем случае вы сказали, что эти переменные могут быть изменены другими потоками. Чтение кода - это мое предположение, когда я вижу изменчивость, поэтому, с точки зрения сопровождающего, это хорошо - это дает мне дополнительную информацию (что правда).

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

Не использовать volatile, если предполагается, что это приводит к некорректному поведению, поскольку оптимизации меняют смысл кода.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...