代码之家  ›  专栏  ›  技术社区  ›  GManNickG

强制编译器不优化无副作用语句

  •  18
  • GManNickG  · 技术社区  · 17 年前

    float int ,掩码符号位,转换回绝对值,而不仅仅是调用 fabs()

    void float_to_int(float f)
    {
        int i = static_cast<int>(f); // has no side-effects
    }
    

    i += 10 仍然没有副作用,因此无法解决问题。

    int dummy; dummy += i ,因此的价值 i 使用。但我觉得这个虚拟操作会妨碍我想要的结果。

    我使用的是Visual Studio 2008/G++(3.4.4)。

    编辑

    为了澄清,我希望将所有优化都达到最大,以获得良好的配置文件结果。问题是,有了这个,没有副作用的陈述将被优化,因此情况就是这样。

    再次编辑

    要再次澄清,请阅读以下内容:

    它们没有用。只是单纯的好奇心。当然,如果我不知道这些老黑客对现代CPU的表现如何,生活可能会继续下去,但知道这一点永远不会有什么坏处。

    知道

    过早引用高德纳的话是所有烦恼的根源。

    11 回复  |  直到 6 年前
        1
  •  11
  •   finnw    17 年前

    volatile 变量shold永远不会被优化掉,所以这可能会给你想要的结果:

    static volatile int i = 0;
    
    void float_to_int(float f)
    {
        i = static_cast<int>(f); // has no side-effects
    }
    
        2
  •  7
  •   Sam Harwell    17 年前

    从定义上讲,你是在歪曲结果。

    以下是如何解决试图分析您为测试而编写的“虚拟”代码的问题: 对于分析,将结果保存到全局/静态数组中,并在程序结束时将数组的一个成员打印到输出中。编译器将无法进行优化 在数组中放置值的任何计算,但您仍然会得到它可以进行的任何其他优化,以使代码更快。

        3
  •  6
  •   George Phillips    17 年前

    int float_to_int(float f)
    {
       return static_cast<int>(f);
    }
    

    然后,您的调用代码可以使用printf来执行它,以确保它不会对其进行优化。还要确保float_to_int在一个单独的编译单元中,这样编译器就不会耍任何花招。

    extern int float_to_int(float f)
    int sum = 0;
    // start timing here
    for (int i = 0; i < 1000000; i++)
    {
       sum += float_to_int(1.0f);
    }
    // end timing here
    printf("sum=%d\n", sum);
    

    现在将其与一个空函数进行比较,例如:

    int take_float_return_int(float /* f */)
    {
       return 1;
    }
    

    时间上的差异应该能让你了解你试图衡量的费用。

        4
  •  5
  •   peterchen    17 年前

    到目前为止,我使用的所有编译器都能正常工作:

    extern volatile int writeMe = 0;
    
    void float_to_int(float f)
    {    
      writeMe = static_cast<int>(f); 
    }
    

    请注意,这会扭曲结果,boith方法应该写入 writeMe .

    volatile 告诉编译器“可以在没有通知的情况下访问该值”,因此编译器不能省略计算并丢弃结果。要阻止输入常量的传播,您可能还需要通过extern volatile运行它们:

    extern volatile float readMe = 0;
    extern volatile int writeMe = 0;
    
    void float_to_int(float f)
    {    
      writeMe = static_cast<int>(f); 
    }
    
    int main()
    {
      readMe = 17;
      float_to_int(readMe);
    }
    

    尽管如此,读取和写入之间的所有优化都可以“全力”应用。在检查生成的程序集时,对全局变量的读写操作通常是很好的“防线”。

    没有 extern 编译器可能会注意到,对变量的引用从未被引用,因此确定它不能是易失性的。从技术上讲,使用链接时间代码生成可能还不够,但我还没有找到编译器 咄咄逼人。(对于确实删除访问的编译器,需要将引用传递给运行时加载的DLL中的函数)

        5
  •  4
  •   anon anon    17 年前

    int float_to_int(float f)
    {
        return static_cast<int>(f); // has no side-effects
    }
    

    正如其他人所建议的那样,您需要检查资产管理器输出,以检查这种方法是否真的有效。

        6
  •  3
  •   Tom Leys    17 年前

    你只需要跳到你学到一些东西并阅读已发表文章的部分 Intel CPU optimisation manual .

    这些非常清楚地表明,在float和int之间进行强制转换是一个非常糟糕的主意,因为它需要从int寄存器到内存进行存储,然后加载到float寄存器中。这些操作会在管道中产生气泡,浪费许多宝贵的循环。

        7
  •  2
  •   Toad    17 年前

    函数调用会产生相当大的开销,所以无论如何我都会删除它。

    添加一个虚拟值+=i;没问题,只要你在备用配置文件中也保留这段相同的代码。(因此,您将其与之进行比较的代码)。

    最后但同样重要的是:生成asm代码。即使你不能用asm编码,生成的代码通常也是可以理解的,因为它后面会有标签和注释的C代码。所以你知道(排序)会发生什么,以及保留了哪些位。

    R

    p.s.也发现了这一点:

     inline float pslNegFabs32f(float x){
           __asm{
             fld  x //Push 'x' into st(0) of FPU stack
             fabs
             fchs   //change sign
             fstp x //Pop from st(0) of FPU stack
            }
           return x;
     } 
    

    据说也很快。你可能也想分析一下。(尽管它几乎不是可移植代码)

        8
  •  1
  •   Stack Overflow is garbage    17 年前

    返回值?

    int float_to_int(float f)
    {
        return static_cast<int>(f); // has no side-effects
    }
    

    然后在调用站点,您可以将所有返回值相加,并在基准测试完成后打印出结果。通常的做法是确保你依赖结果。

    您可以使用全局变量,但这似乎会产生更多的缓存未命中。通常,只需将值返回给调用者(并确保调用者确实对其做了一些事情)就可以了。

        9
  •  1
  •   Sahil Singh Jed Burke    6 年前

    如果你使用的是微软的编译器- cl.exe ,您可以使用以下语句在每个函数级别打开/关闭优化 [link to doc] .

    #pragma optimize("" ,{ on |off })
    

    关闭当前行之后定义的函数的优化:

    #pragma optimize("" ,off)
    

    重新启用优化:

    #pragma optimize("" ,on)
    

    例如,在下图中,您可以注意到3件事。

    1. 编译器优化标志已设置- /O2 将得到优化 .
    2. 优化是 关闭 对于第一个功能- square() ,以及 转动 < 返回 之前 square2() 定义。
    3. 为第一个函数生成的汇编代码量较高。在第二个函数中,没有为生成汇编代码 int i = num; 代码中的语句。

    compiler explorer https://godbolt.org/z/qJTBHg 在编译器资源管理器上获取此代码的链接。

    类似的指令也适用于 gcc 我也是- https://gcc.gnu.org/onlinedocs/gcc/Function-Specific-Option-Pragmas.html

        10
  •  0
  •   Will    17 年前

    围绕这一声明的微观基准并不能代表在真实场景中使用这种方法;周围的指令及其对管道和缓存的影响通常与任何给定的语句本身一样重要。

        11
  •  0
  •   greyfade    17 年前

    GCC 4现在做了很多GCC 3.4从未做过的微优化。GCC4包括一个树矢量器,它可以 非常 很好地利用了SSE和MMX。它还使用GMP和MPFR库来帮助优化对以下内容的调用 sin() , fabs() 等,以及优化对其FPU、SSE或3D Now的此类调用!等效物。

    我知道英特尔编译器也非常擅长这类优化。

    我的建议是不要担心这样的微观优化——在相对较新的硬件(过去5年或6年内构建的任何硬件)上,它们几乎完全没有意义。

    编辑:在最近的CPU上,FPU fabs int fsin 指令通常比预先计算表格或外推泰勒级数更快。例如,你在“游戏编程大师的技巧”中发现的许多优化都是完全没有意义的,正如另一个答案所指出的那样,可能比FPU和SSE上的指令慢。

    有关所有详细信息,请查看AMD和Intel处理器编程手册。