GCC 中具有不同优化级别的不同输出
Different output with different optimization levels in GCC
我正在重写上学期为大学开发的光线追踪器,我 运行 遇到以下问题:
当我编译 运行 我在 Debug 中的代码时,输出符合预期
但是当我启用更高的优化级别时,例如“-O2”结果完全不同:
而且我不确定为什么会这样。我追踪到球体相交代码
//#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
如果我在发布模式下取消注释 pragma,我会再次得到预期的结果。也许我的代码中有一些未定义的行为导致了这个?
您也可以在此处查看,因为不容易实现最小的可重现示例。 https://github.com/Yamahari/RayTracer/blob/master/rt/solid/Sphere.cpp
(您也可以克隆 repo 并使用 cmake 构建项目,您只需要 SFML 作为依赖项。
将 -DSFML_INCLUDE_DIR="include_dir" 和 -DSFML_LIB_DIR="lib_dir" 与使用所需编译器编译的 sfml 库一起使用)
std::minmax
returns 一对对其参数的引用。如果你用纯右值作为参数调用它,这对中的引用将在完整表达式结束后悬空,这意味着访问 r.first
in
if (r.first <= 0.0F)
这里会有未定义的行为。
因此将 lhv + rhv
和 lhv - rhv
存储在变量中并调用 std::minmax
或使用
std::minmax({lhv + rhv, lhv - rhv})
到select std::initializer_list
重载,其中returns一对实际值。
正如@Jarod42 在评论中指出的那样,您实际上不需要 std::minmax
这里。 rhv
是 std::sqrt
调用的结果,所以它总是非负的,使得 lhv + rhv
总是更大(或等于)的值。
我正在重写上学期为大学开发的光线追踪器,我 运行 遇到以下问题: 当我编译 运行 我在 Debug 中的代码时,输出符合预期
但是当我启用更高的优化级别时,例如“-O2”结果完全不同:
而且我不确定为什么会这样。我追踪到球体相交代码
//#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
如果我在发布模式下取消注释 pragma,我会再次得到预期的结果。也许我的代码中有一些未定义的行为导致了这个?
您也可以在此处查看,因为不容易实现最小的可重现示例。 https://github.com/Yamahari/RayTracer/blob/master/rt/solid/Sphere.cpp
(您也可以克隆 repo 并使用 cmake 构建项目,您只需要 SFML 作为依赖项。
将 -DSFML_INCLUDE_DIR="include_dir" 和 -DSFML_LIB_DIR="lib_dir" 与使用所需编译器编译的 sfml 库一起使用)
std::minmax
returns 一对对其参数的引用。如果你用纯右值作为参数调用它,这对中的引用将在完整表达式结束后悬空,这意味着访问 r.first
in
if (r.first <= 0.0F)
这里会有未定义的行为。
因此将 lhv + rhv
和 lhv - rhv
存储在变量中并调用 std::minmax
或使用
std::minmax({lhv + rhv, lhv - rhv})
到select std::initializer_list
重载,其中returns一对实际值。
正如@Jarod42 在评论中指出的那样,您实际上不需要 std::minmax
这里。 rhv
是 std::sqrt
调用的结果,所以它总是非负的,使得 lhv + rhv
总是更大(或等于)的值。