累加双精度与整型
Accumulating doubles vs ints
当将a
声明为双精度类型时,与将其声明为int类型相比,下面的代码要慢一些:
double a = 0;
int j[1000];
for(int i=0; i<1000; i++){
a += (i * j[i]);
}
双精度加法的性能下降是因为编译器选择了不同的汇编指令,而不是如果a
被声明为int会选择什么?
我试图了解CPU是否在运行时对其自身的单/双精度进行任何"转换",这在汇编和成本执行时间中无法看到?
让我们将循环中的表达式分解为各个部分。为此,我们将重写代码,使每行都是一次赋值和一次操作。当从int
版本开始时,它看起来像这样:
// not depending on a
/* 1 */ auto t1 = j[i];
/* 2 */ auto t2 = i * j
// depending on decltype(a)
/* 3 */ decltype(a+t2) t3 = static_cast<decltype(a+t2)>(t2);
/* 4 */ decltype(a+t3) t4 = static_cast<decltype(a+t3)>(a);
/* 5 */ a = t3 + t4;
前两个操作完全不依赖于a
的类型,并且在任何一种情况下都将执行完全相同的操作。
但是,从操作3开始,有一个区别。这样做的原因是为了添加a
和t2
,编译器必须首先将它们转换为通用类型。在a
是整数的情况下,操作3和4根本不做任何事情(int
+ int
产生int
,因此两次强制转换都将int
s转换为int
s)。然而,在a
是double
的情况下,t2
必须在相加之前转换为double
(int
+ double
产生double
)。
这意味着操作5中的加法类型也不同:它可以是int
或double
加法。忽略double
通常是int
的两倍这一明显的方面,这意味着计算机在这一点上需要做一些不同的事情。
对x64的影响
在使用优化编译器为现代x64机器编译此程序时,应该注意的是,当按此声明时,整个程序可能会被优化掉。假设没有发生这种情况,并且编译器没有应用任何非法优化,并且您可以使用未初始化的变量(j
的元素)引入UB,则可能发生以下情况:
// not depending on a
MOV EAX, i // copy i to EAX register
IMUL j[i] // EAX = EAX * j[i] (high 32 bits are stored in EDX and ignored)
// if a is int
ADD a, EAX // integer addition: a += EAX
// else if a is double
CVTSI2SD XMM0, EAX // convert the multiplication result to double
ADDPD a, XMM0 // double addition: a += XMM0
// endif
一个好的编译器会稍微展开循环,并交错一些循环,因为循环限制是已知的。正如您所看到的,操作和依赖链的指令至少增加了两倍。而且,第二个版本中的指令比第一个版本中的单个指令慢。
虽然我确信第二个版本可以用一个更有效的版本来表述,但应该注意的是,第一个版本的整数ADD
是在任何CPU上最快的运算之一,通常比它的浮点运算更快。
那么,回答你的问题:CPU确实执行浮点数和整数之间的转换-这在汇编中是可见的,并且具有(潜在的重大)运行成本。
单精度呢?
既然你也问了单精度,让我们检查使用float
时会发生什么:
// not depending on a
MOV EAX, i // copy i to EAX register
IMUL j[i] // EAX = EAX * j[i] (high 32 bits are stored in EDX and ignored)
// if a is float
CVTSI2SS XMM0, EAX // convert the multiplication result to float
ADDPS a, XMM0 // float addition: a += XMM0
汇编没有显示出显著的差异(我们只是用两个D
替换double
,用S
替换single
)。而且,有趣的是,性能上的差异也很小(例如,一个Haswell内核转换为double比转换为float要多花1 μ op,而加法本身显示相同的性能)。
为了验证我的说法,我已经运行了2000000次循环,并确保a
没有被优化掉。结果如下:
int : 601.1 ms
float : 2567 ms
double: 2593 ms
我忽略了你的例子不编译,数组j
没有初始化(我希望你会解决这个问题)。
浮点运算通常比整数运算慢。但是,您的代码有一个更昂贵的操作:将整数转换为浮点数。您的代码遭受双重打击(双关语)使用混合模式算术。
- C 字符串返回字符串的整数/双精度/长整型值
- 为什么在传递长整型时调用具有两个双精度类型的参数的重载函数?
- 在 C++ 中获取长整型和双精度型模数的准确方法
- 初始化时无法将长整型*转换为双精度*
- Qt SQL:如何区分双精度和整型
- 对整型变量的双精度值-意外输出
- c++中的双精度到整型转换
- g++ abs()在短整型上似乎将其转换为双精度类型
- 两个整型到一个双精度c++
- 将整型转换为双精度
- 将数组整型值转换为双精度值
- 常数表达式可以是双精度类型而不是只能是整型吗?
- 在c++中从文件中读取字符串、整型和双精度的混合
- 生成随机整型和双精度的c++模板类
- 从整型转换为双精度
- 从字符串(如整型或双精度型)中提取数字
- 累加双精度与整型
- 为什么这种双精度到整型的转换不起作用?
- 将双精度指针转换为整型指针
- visual studio 2010 -浮点/双精度随机数的小范围,适用于整型(c++, VS2010)