Хобрук: Ваш путь к мастерству в программировании

Оптимизация компилятора Visual Studio C++ нарушает код?

У меня здесь особая проблема, которая происходит как с VS2005, так и с 2010. У меня есть цикл for, в котором вызывается встроенная функция, по сути что-то вроде этого (С++, только для иллюстративных целей):

inline double f(int a)
{
  if (a > 100)
  {
    // This is an error condition that shouldn't happen..
  }

  // Do something with a and return a double
}

А затем цикл в другой функции:

for (int i = 0; i < 11; ++i)
{
  double b = f(i * 10);
}

Теперь происходит то, что в отладочной сборке все работает нормально. В релизной сборке со всеми включенными оптимизациями это, судя по дизассемблированию, скомпилировано так, что i используется напрямую без * 10, а сравнение a > 100 превращается в a > 9, а я предполагаю, что должно быть a > 10. Есть ли у вас какие-либо сведения о том, что может заставить компилятор думать, что a > 9 - правильный путь? Интересно, что даже незначительное изменение (например, отладочная распечатка) в окружающем коде заставляет компилятор использовать i * 10 и сравнивать это с буквальным значением 100.

Я знаю, что это несколько расплывчато, но я был бы признателен за любую старую идею.

ИЗМЕНИТЬ:

Вот, надеюсь, воспроизводимый случай. Я не считаю его слишком большим, чтобы вставлять сюда, так что вот:

__forceinline int get(int i)
{
  if (i > 600)
    __asm int 3;

  return i * 2;
}

int main()
{
  for (int i = 0; i < 38; ++i)
  {
    int j = (i < 4) ? 0 : get(i * 16);
  }

  return 0;
}

Я протестировал это с VS2010 на своей машине, и, похоже, он ведет себя так же плохо, как и исходный код, с которым у меня проблемы. Я скомпилировал и запустил это с пустым шаблоном проекта С++ по умолчанию в среде IDE в конфигурации выпуска. Как видите, брейк ни в коем случае нельзя делать (37 * 16 = 592). Обратите внимание, что удаление i < 4 делает эту работу такой же, как и в исходном коде.


  • Как насчет предоставления минимального примера, который компилируется и воспроизводит проблему? 16.09.2010
  • Вы пишете, что язык ассемблера, созданный для оптимизированной сборки, не соответствует вашим ожиданиям. Но не совсем понятно, что оптимизированная программа работает некорректно. Можете ли вы подтвердить, что это так, и рассказать нам, чем отличаются результаты от двух версий? 16.09.2010
  • На самом деле я быстро попытался воспроизвести это как совершенно отдельный случай, прежде чем даже задать этот вопрос, но пока не смог скомпилировать его таким же образом. Я не могу скопировать и вставить исходный код, но я посмотрю, смогу ли я получить автономную неконтекстную версию проблемной части. 16.09.2010

Ответы:


1

Кому интересно, это оказалось ошибкой компилятора VS. Подтверждено Microsoft и исправлено в пакете обновления после отчета.

01.03.2013

2

Во-первых, было бы неплохо, если бы вы разместили достаточно кода, чтобы мы могли воспроизвести проблему. В противном случае вы просто просите психической отладки.

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

Но не видя вашего фактического кода, я не могу быть более конкретным.

16.09.2010

3

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

Как уже говорилось, трудно иметь какое-либо точное представление без дополнительного кода.

16.09.2010

4

Во-первых, встроенная сборка предотвращает определенные оптимизации, вы должны использовать встроенную функцию __debugbreak() для установки точек останова int3. Компилятор видит, что встроенная функция не имеет никакого эффекта, кроме точки останова, поэтому он делит 600 на 16 (примечание: на это влияет усечение целых чисел), поэтому он оптимизирует отладку для запуска с 38 > i >= 37. Таким образом, кажется работать с этой целью

16.09.2010
  • Это было убрано из версии, которая действительно что-то делала с возвращаемым результатом. Это воспроизводилось даже с этим и с использованием чего-то другого, кроме встроенного ассемблера, чтобы сигнализировать о том, что значение i было неожиданным. Хотя, возможно, я сделал что-то еще не так. 16.09.2010
  • Проблема здесь в целочисленном усечении 600/16, из-за чего он срабатывает на 37 вместо 37,75. Если бы функция не была встроена, этого бы не произошло, так как компилятор не сделал бы предварительное деление. Это может работать правильно, если используются поплавки (нужно проверить это) 16.09.2010
  • Новые материалы

    Решения DBA Metrix
    DBA Metrix Solutions предоставляет удаленного администратора базы данных (DBA), который несет ответственность за внедрение, обслуживание, настройку, восстановление базы данных, а также другие..

    Начало работы с Блум
    Обзор и Codelab для генерации текста с помощью Bloom Оглавление Что такое Блум? Некоторые предостережения Настройка среды Скачивание предварительно обученного токенизатора и модели..

    Создание кнопочного меню с использованием HTML, CSS и JavaScript
    Вы будете создавать кнопочное меню, которое имеет состояние наведения, а также позволяет вам выбирать кнопку при нажатии на нее. Финальный проект можно увидеть в этом Codepen . Шаг 1..

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

    Классы в JavaScript
    class является образцом java Script Object. Конструкция «class» позволяет определять классы на основе прототипов с чистым, красивым синтаксисом. // define class Human class Human {..

    Как свинг-трейдеры могут использовать ИИ для больших выигрышей
    По мере того как все больше и больше профессиональных трейдеров и активных розничных трейдеров узнают о возможностях, которые предоставляет искусственный интеллект и машинное обучение для улучшения..

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