Разный вывод с разными уровнями оптимизации в GCC - PullRequest
6 голосов
/ 25 марта 2020

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

expected result

Но когда я включаю более высокие уровни оптимизации, например, "-O2", получается что-то совершенно другое:

actual result

И я не уверен, почему это происходит. Я отследил его до кода пересечения сферы

//#pragma GCC push_options
//#pragma GCC optimize("O0")

Intersection Sphere::intersect(const Ray& ray, const float previous) const
{
    const auto oc = ray.origin - center_;
    const auto lhv = -dot(ray.direction, oc);
    const auto discriminant = lhv * lhv - (oc.lensqr() - radius_ * radius_);

    if (discriminant < 0.0F)
    {
        return Intersection::failure();
    }
    float distance;
    const auto rhv = std::sqrt(discriminant);
    const auto r = std::minmax(lhv + rhv, lhv - rhv);
    if (r.first <= 0.0F)
    {
        if (r.second <= 0.0F)
        {
            return Intersection::failure();
        }
        distance = r.second;
    }
    else
    {
        distance = r.first;
    }

    const auto hit = ray.getPoint(distance);
    const auto normal = (hit - center_).normalize();

    if (0.0F <= distance && distance < previous - epsilon)
    {
        return {distance, ray, this, normal, hit};
    }
    return Intersection::failure();
}

//#pragma GCC pop_options

Если я раскомментирую прагму в режиме релиза, я снова получу ожидаемый результат. Может быть, в моем коде есть неопределенное поведение, которое приводит к этому?

Вы также можете посмотреть здесь, так как минимальный воспроизводимый пример не легко возможен. https://github.com/Yamahari/RayTracer/blob/master/rt/solid/Sphere.cpp

(Вы также можете клонировать репозиторий и построить проект с помощью cmake, в качестве зависимости вам нужен только SFML.

Использовать -DSFML_INCLUDE_DIR = "include_dir" и -DSFML_LIB_DIR = "lib_dir" с библиотекой sfml, скомпилированной с нужным компилятором)

1 Ответ

4 голосов
/ 25 марта 2020

std::minmax возвращает пару ссылок на свои аргументы. Если вы вызываете его с prvalues ​​в качестве аргументов, ссылки в этой паре будут зависать после окончания полного выражения, что означает, что доступ r.first в

if (r.first <= 0.0F)

будет иметь неопределенное поведение.

Сохраните lhv + rhv и lhv - rhv в переменных и вызовите std::minmax для них или используйте

std::minmax({lhv + rhv, lhv - rhv})

, чтобы выбрать перегрузку std::initializer_list, которая возвращает пару фактических значений .


Как отметил @ Jarod42 в комментариях, вам на самом деле не нужно std::minmax здесь. rhv является результатом вызова std::sqrt, поэтому он всегда неотрицателен, поэтому lhv + rhv всегда является большим (или равным) значением.

...