【问题标题】:Question about performance when doing math in c在c中做数学时关于性能的问题
【发布时间】:2021-03-04 03:14:05
【问题描述】:

代码 X:

float result;
int a, b;

result = (float)a + (float)b;

代码 Y:

float result, a, b;

result = a + b;

哪种代码最快且使用的资源更少?

【问题讨论】:

  • 差别很小,你可以忽略它。
  • 这两个代码做不同的事情。尽管变量未初始化,但两者都有未定义的行为。
  • 这个问题没有上下文是没有意义的。在任何一种情况下,ab 之前都必须被赋予一些值。因此,有一个先前的代码,在一种情况下,与int 一起为ab 生成int 值,在另一种情况下,与float 一起为@ 生成float 值987654331@ 和b。因此,使用 Code X 或 Code Y 的两个程序之间的差异不仅仅是 Code X 或 Code Y,因此它们执行的时间差异不仅仅是由于 Code X 或 Code Y。回答哪个更快必须取决于在程序的其他部分也是如此。
  • @EugeneSh.:在其他代码之前显示声明是显示变量类型的常见约定,并不意味着程序中的代码实际上只包含那些紧跟在显示的其他代码之后的声明。你现在应该知道了。

标签: c performance math optimization


【解决方案1】:

这个C代码:

float test(int a, int b)
{
  float result = (float)a + (float)b;
  return result;
}

float test1(float a, float b)
{
  float result = a + b;
  return result;
}

可能会被优化编译器编译成这样的东西:

test:
        cvtsi2ss        xmm1, edi
        cvtsi2ss        xmm0, esi
        addss   xmm0, xmm1
        ret
test1:
        addss   xmm0, xmm1
        ret

所以你可以看到test1test 做的少,这是正常的,因为这两个函数做的事情不一样。在test1 中,所有内容都是float,因此无需进行任何转换。在 test 中,ints 需要在添加完成之前转换为浮点数。

【讨论】:

    【解决方案2】:

    您使用的是未初始化的变量,由于精度损失,两段代码并不完全相同,但我将忽略这一点,让我们从概念上讲。

    我假设在这两种情况下 'a' 和 'b' 都是整数值,无论它们的类型如何。

    首先,当谈到浮点运算时,它总是依赖于架构。没有任何处理器以相同的方式支持浮点运算。

    不过没关系,假设您在谈论 x86_64。

    接下来是优化。请注意(float)a + (float)b(float)(a + b) 不同,因为浮点转换可能会丢失精度(尽管对于 int 可能不会,但很长时间肯定会丢失精度)。所以这个不能优化或者至少在某些情况下不能优化。

    所以优化器可能不会对这两种情况产生太大影响。

    现在,还要注意从 int 到 float 和 back 的转换是单独的汇编指令(例如从 int 到 long 不同),因为整数和浮点数具有不同的 in-mem 格式,这是cvtsi2ss 指令。

    结论: 第一种情况进行相同数量的计算,但它也有转换。所以它非常慢

    请参见此处说明差异的反汇编:

    案例一:https://godbolt.org/z/fsMjoE

    案例2:https://godbolt.org/z/v1xzr1

    【讨论】:

    • Pay attention that (float)a + (float)b is not the same as (float)(a + b), because floating point conversion may lose precision 问题出在其他地方。
    【解决方案3】:

    假设没有溢出,Y 会稍微快一些,因为将int 转换为float 会产生开销。

    但是差异会非常小,除非性能至关重要,否则良好的做法是用最“自然”的数据类型而不是最高效的数据类型来表示 ab。那是除非它们有小数部分(本质上)float 是不合适的,即使某些计算有 float 结果。

    还请注意,float 可以表示精确整数的范围可能比int 更窄。在一个典型的平台上,int 是 32 位,float 也是 32 位,但使用一些位来编码指数。

    在某些平台上double 可能更快,因为float 在内部转换为double 并返回。 除非空间很重要(例如托管环境或微型嵌入式设备上的数百万个值),否则通常建议使用double

    【讨论】:

      【解决方案4】:

      这两个例子完全不同,比较它们没有什么意义。为什么?

      第一个例子添加了两个转换为浮点数的整数。编译器必须将两者都转换为浮点数,然后再添加它们。

      第二个例子只是添加了两个浮点数,没有任何转换。

      相信你想比较

      float foo(int a, int b) {
          return (float)a + (float)b;
      }
      
      float bar(int a, int b) {
          return a + b;
      }
      

      https://godbolt.org/z/xvaarY

      The resulting code 
              pxor    xmm0, xmm0
              pxor    xmm1, xmm1
              cvtsi2ss        xmm0, edi
              cvtsi2ss        xmm1, esi
              addss   xmm0, xmm1
              ret
      bar:
              add     edi, esi
              pxor    xmm0, xmm0
              cvtsi2ss        xmm0, edi
              ret
      

      功能栏效率更高,但如果两个整数相加溢出(先相加,然后将结果转换为浮点数)调用UB,则可能不会产生相同的结果。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-28
        • 1970-01-01
        • 2022-09-24
        • 1970-01-01
        • 1970-01-01
        • 2020-10-24
        • 1970-01-01
        • 2013-10-24
        相关资源
        最近更新 更多