Следующий код тестирования работает правильно в VS с отладкой или выпуском, а также в GCC. Это также верно для ICC с отладкой, но не при включенной оптимизации ( -O2
).
#include <cstdio>
class tClassA{
public:
int m_first, m_last;
tClassA() : m_first(0), m_last(0) {}
~tClassA() {}
bool isEmpty() const {return (m_first == m_last);}
void updateFirst() {m_first = m_first + 1;}
void updateLast() {m_last = m_last + 1;}
void doSomething() {printf("should not reach here\r\n");}
};
int main() {
tClassA q;
while(true) {
while(q.isEmpty()) ;
q.doSomething();
}
return 1;
}
Предполагается, что он остановится на while (q.isEmpty ())
. Однако, когда -O2
включен в ICC (выпуск), он начинает «делать что-то» бесконечно.
Поскольку это однопоточная программа и isEmpty ()
следует оценивать как true
, я не могу найти причину, по которой ICC должен вести себя таким образом? Я что-то пропускаю?
Есть ли вероятность, что в фактическом коде, который вы создали и запустили, отсутствовала точка с запятой после while (q.isEmpty ())
? Это обязательно приведет к бесконечному вызову следующей строки.
Думаю, это могла быть ваша версия gcc. Я скомпилировал вашу прогу под 4.4.2 и она заработала именно так, как должна.
Поскольку цикл while (q.isEmpty()) ;
не содержит ни одного утверждения, которое могло бы вызвать видимый извне побочный эффект, весь цикл оптимизируется. Это та же причина, по которой:
for (int i = 0; i < 10; i++)
;
может быть оптимизирована до тех пор, пока i
не будет volatile
(хранение volatile
объектов является частью "внешне видимых" эффектов программы).
В языке Си, на самом деле, это нерешенный вопрос о том, можно ли оптимизировать бесконечный цикл таким образом (я не знаю, как обстоит дело с C++). Насколько я знаю, консенсус по этому вопросу так и не был достигнут - умные и знающие люди занимают обе стороны.
Звучит как ошибка. Вот (довольно безумное) предположение о том, какие рассуждения могли привести к этому ...
После встраивания он видит:
while (q.m_first == q.m_last) /* do nothing */ ;
do_something();
и любая последовательность ничего не делают повторно; do something
можно перевести как просто «делать что-то». Это падает, если повторяющаяся часть бесконечна (как в этом случае). Но, возможно, они не тестируют свою компиляцию на примерах, которые намеренно имеют бесконечный цикл ;-).
Лучше всего включить в него полученный двоичный шаг, дизассемблировать основную функцию и посмотреть, какая сборка была сгенерирована. Я не говорю, что вы сможете увидеть ошибку, но вы можете увидеть, было ли что-то оптимизировано.
Немного в сторону, эта версия icc делает то, что вы хотите. То есть он никогда не вызывает doSomething ()
.
[9:41am][wlynch@computer /tmp] icc --version
icc (ICC) 11.0 20081105
Стандарт C ++ позволяет удалять циклы без побочных эффектов, даже если они не завершаются:
Обычно считается, что это важно разрешить трансформацию потенциально незавершенных петель (например, путем слияния двух петель, повторять то же самое потенциально бесконечное множество, или исключив цикл без побочных эффектов), даже если это не может быть иным образом оправдано в случай, когда первый цикл никогда заканчивается. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2429.htm
См. Обсуждение здесь: http://blog.regehr.org/archives/161