【问题标题】:Disable optimization on specific MSVC version禁用特定 MSVC 版本的优化
【发布时间】:2015-12-19 13:20:52
【问题描述】:

我有一个使用 CMake 的项目,但由于编译器错误(模板解析、优化和异常处理之间的交互)而无法在 VS 2015 上编译 this code

可以通过禁用优化来避免该错误 - 虽然这会产生次优代码,但至少项目可以编译。

如何在 ReleaseRelWithDebInfo 构建中将 MSVC 2015 的默认优化级别更改为 /O0

我的幼稚实现将是CMAKE_CXX_FLAGS 中的条件替换——未来安全吗?

另一种方法是在有问题的标头中使用#pragma

【问题讨论】:

  • 顺便说一句,我敢说,如果您的代码使用模板的程度会破坏编译器,那么您不希望对其进行非优化。即使是项目编译器,生成的代码也很有可能会在性能上造成大约 2-3 倍的损失。
  • @SergeyA,有问题的模板代码相当简单——添加了一个指向 pastebin 的链接。
  • 我能够提出问题,尝试解决方案,并提供我自己会选择的答案。我正在使用 VS2015,其中 Windows C++11 工作/实验是有序的,所以这个问题让我很感兴趣。
  • 未来最安全的方法——如果不清楚哪个编译器版本存在或将存在错误——在我看来是使用 CMake 模块CheckCXXSourceCompiles。然后您可以检查结果并相应地调整优化级别。
  • @Florian,问题在于源代码确实可以编译。它是崩溃的链接器(我只是因为我在 VS2015 中尝试过这个才发现的)。当然,OP 的帖子并没有说清楚。

标签: c++ visual-c++ cmake


【解决方案1】:

这个编译器错误使链接器产生了垃圾问题,尽我所能追查问题(VS2015 是新的,在撰写本文时还没有发布补丁/更新)。崩溃发生在链接器中。编译器似乎认为一切顺利。

当这个错误被“激发”时,问题出在使用生成的模板对象的代码中。换句话说,即使您在标头中禁用优化,但在正文中重新启用优化,它仍然会使链接器崩溃。 “有效”的是禁用对实例化和使用模板对象的成员函数的代码的优化(您可以为该目标之外的所有代码保留优化)。

例如,在问题中发布的代码中,将优化保留在整个标题中。在使用模板的函数上,执行:

#pragma optimize( "", off )


void test()
{
    two<one<int> > obj;
    obj.func();
}

#pragma optimize( "", on )

这隔离了引发问题的代码的优化损失,并且链接器成功。

当然,pragma 本身可以使用条件定义或其他一些机制进行包装,一旦发布了修复问题的 VS2015 补丁,您就可以禁用这些机制。

通过这种方式,可以使用代码而无需考虑构建配置(这意味着它适用于 CMAKE 构建和 IDE 构建),而不必给代码的后续用户增加负担(除了定义来控制是否不是优化被禁用)。

如果碰巧适合您的情况,您也可以尝试以下方法:

template<typename T>
struct two
{
    T t;

    void func()
    {
        typedef typename T::type type;

        type i = std::numeric_limits<type>::min();
        type j = std::numeric_limits<type>::epsilon();

        t.t1 = i / 2 + j;
        t.t2 = t.t1;
    }
};

这种重述代码绕过了错误,并且在不崩溃链接器的情况下编译。

另外,这也绕过了问题:

   void func()
    {
        typedef typename T::type type;

        t.t1 = std::numeric_limits<type>::min() / 2 + std::numeric_limits<type>::epsilon();
        t.t2 = t.t1;
    }

另外,这对我来说很好奇,代表了更好的设计(因为它不需要一个类来摆弄另一个类的成员)

template<typename T>
struct one
{
    typedef T type;

    T t1, t2;

    void set( const T & i ) { t1 = t2 = i; }
};



template<typename T>
struct two
{
    T t;

    void func()
    {
        typedef typename T::type type;

        t.set( std::numeric_limits<type>::min() / 2 + std::numeric_limits<type>::epsilon() );

    }
};

好消息是有一个解决方案不涉及更改项目构建规则、启用优化并且实际上是相同的。

看来罪魁祸首实际上是t.t1 = t.t2 = ... 子句。为什么这样的赋值会触发链接器崩溃,但等价的表达式在其他方面对 Microsoft 来说并不是一个谜,但实际上,该解决方案似乎会产生更好看的代码。

【讨论】:

  • 对于 VS2015,选项只有默认、/LTCG:incremental(快速链接时间码生成)、/LTCG(只是链接时间码生成)和引导版本。我已经尝试了所有三个(不是指导的),结果相同,无论是它崩溃还是简单地调整代码都会绕过崩溃。
【解决方案2】:

这将隔离 #pragma 仅适用于 Release 和 VS 2015

#if ( NDEBUG && _MSC_VER == 1900 )
#pragma optimize ("d", on) // same as /Od
#endif

【讨论】:

    【解决方案3】:

    为了补充使用基于 C++ 的解决方法的答案,这里是一个使用 CMake 的 CHECK_CXX_SOURCE_COMPILES() 宏的基于 CMake 的实现示例:

    cmake_minimum_required(VERSION 3.2)
    
    include(CheckCXXSourceCompiles)
    
    project(TestCompile CXX)
    
    if (MSVC AND MSVC_VERSION GREATER 1899)
        set(CMAKE_TRY_COMPILE_CONFIGURATION "Release")
        CHECK_CXX_SOURCE_COMPILES(
            "
            #include <limits>
    
            template<typename T>
            struct one
            {
                typedef T type;
                T t1, t2;
            };
    
            template<typename T>
            struct two
            {
                T t;
                void func()
                {
                    typedef typename T::type type;
                    t.t1 = t.t2 = std::numeric_limits<type>::min() / 2 + std::numeric_limits<type>::epsilon();
                }
            };
    
            void main()
            {
                two<one<int> > obj;
                obj.func();
            }
            "
            CAN_LINK_MY_TEMPLATES_EXAMPLE
        )
        if (NOT CAN_LINK_MY_TEMPLATES_EXAMPLE)
            add_compile_options("/Od")
        endif()
    endif()
    

    我认为您不必更改特定构建配置的标志,通常使用add_compile_options("/Od") 禁用它就足够了。

    如果您想按配置执行此操作,您可以替换/删除CMAKE_CXX_FLAGS_... 变量中的/O2 标志(如herehere)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多