现代硬件上的浮点与整数计算 我正在C++中做一些性能关键的工作,我们现在使用整数计算来解决那些天生就浮点数的问题,因为它“更快”。这会导致许多恼人的问题,并添加许多恼人的代码
现在,我记得在386天左右的时间里读到了关于浮点计算是如何如此缓慢的,我相信(IIRC)有一个可选的协处理器。但现在,随着CPU的复杂度和功能越来越强大,浮点运算和整数运算的“速度”毫无差别吗?特别是因为实际计算时间与导致管道暂停或从主内存中提取某些内容相比非常小 我知道正确的答案是在目标硬件上进行基准测试,什么是测试这个的好方法?我编写了两个小型C++程序,并比较了它们在Linux上的运行时间和“时间”,但是实际运行时间太过变量(不利于我在虚拟服务器上运行)。除了整天运行数百个基准测试、绘制图表等,我还能做些什么来合理测试相对速度吗?有什么想法吗?我完全错了吗 我使用的程序如下所示,它们在任何方面都不相同:现代硬件上的浮点与整数计算 我正在C++中做一些性能关键的工作,我们现在使用整数计算来解决那些天生就浮点数的问题,因为它“更快”。这会导致许多恼人的问题,并添加许多恼人的代码,c++,x86,floating-point,x86-64,C++,X86,Floating Point,X86 64,现在,我记得在386天左右的时间里读到了关于浮点计算是如何如此缓慢的,我相信(IIRC)有一个可选的协处理器。但现在,随着CPU的复杂度和功能越来越强大,浮点运算和整数运算的“速度”毫无差别吗?特别是因为实际计算时间与导致管道暂停或从主内存中提取某些内容相比非常小 我知道正确的答案是在目标硬件上进行基准测试,什么是测试这个的好方法?我编写了两个小型C++程序,并比较了它们在Linux上的运行时间和“时间”,但是实际运行时间太过变量(不利于我在虚拟服务器上运行)。除了整天运行数百个基准测试、绘制图
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <time.h>
int main( int argc, char** argv )
{
int accum = 0;
srand( time( NULL ) );
for( unsigned int i = 0; i < 100000000; ++i )
{
accum += rand( ) % 365;
}
std::cout << accum << std::endl;
return 0;
}
#包括
#包括
#包括
#包括
int main(int argc,字符**argv)
{
int accum=0;
srand(时间(空));
对于(无符号整数i=0;i<100000000;++i)
{
累计+=兰特()%365;
}
std::cout除非您正在编写每秒调用数百万次的代码(例如,在图形应用程序中为屏幕画一条线),否则整数与浮点运算很少是瓶颈
对于效率问题,通常的第一步是分析代码以了解运行时间的实际花费。用于此问题的linux命令是gprof
编辑:
虽然我认为您可以始终使用整数和浮点数来实现线条绘制算法,但请多次调用它,看看它是否有区别:
添加速度比兰德快得多,因此您的程序(尤其)毫无用处
您需要确定性能热点并以增量方式修改您的程序。听起来您的开发环境有问题,需要首先解决。在PC上运行您的程序是否不可能只解决一个小问题集
一般来说,尝试使用整数运算的FP作业速度很慢。基于这一非常可靠的“我听说的事情”,在过去,整数计算速度大约是浮点运算速度的20到50倍,而现在它的速度还不到浮点运算速度的两倍。唉,我只能给你一个“视情况而定”的答案
根据我的经验,影响性能的变量很多…特别是整数和浮点运算。处理器之间的差异很大(即使在同一系列中,如x86),因为不同的处理器具有不同的“管道”长度。此外,一些操作通常非常简单(如加法)并且有一条通过处理器的加速路线,而其他的(比如除法)则需要更长的时间
另一个大变量是数据驻留的位置。如果您只需要添加几个值,那么所有数据都可以驻留在缓存中,并可以在缓存中快速发送到CPU。一个非常非常慢的浮点操作(缓存中已经有数据)将比需要从s复制整数的整数操作快很多倍系统内存
我假设您提出这个问题是因为您正在处理一个性能关键型应用程序。如果您正在为x86体系结构开发,并且需要额外的性能,您可能希望研究使用SSE扩展。这可以大大加快单精度浮点运算的速度,因为可以执行相同的操作一次对多个数据进行rmed,另外还有一个单独的*寄存器组用于SSE操作(我注意到在您的第二个示例中,您使用了“float”而不是“double”,这让我觉得您使用的是单精度数学)
*注意:使用旧的MMX指令实际上会减慢程序的速度,因为这些旧指令实际上使用了与FPU相同的寄存器,因此不可能同时使用FPU和MMX。我运行了一个测试,只在数字上添加了1而不是rand()。结果(在x86-64上)是:
- 短片:4.260s
- 内部:4.020s
- 长:3.350秒
- 浮动:7.330
- 双倍:7.210秒
定点和浮点运算在实际速度上可能存在显著差异,但ALU与FPU的理论最佳情况吞吐量完全无关。相反,整数和浮点寄存器的数量(实寄存器,而不是寄存器名)在您的体系结构上,您的计算不使用这些数据(例如,用于循环控制),适合缓存线的每种类型的元素数量,考虑整数和浮点数学的不同语义可能进行的优化——这些影响将占主导地位。算法的数据依赖性在这里起着重要作用,因此没有一般的比较可以预测问题的性能差距
例如,整数加法是可交换的,因此如果编译器看到一个类似于您用于基准测试的循环(假设随机数据是预先准备好的,这样就不会模糊结果),它可以展开循环并计算不依赖项的部分和,然后在循环终止时添加它们。但是使用浮点,编译器必须按照您要求的顺序执行操作(其中包含序列点,因此编译器必须保证sa
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <time.h>
int main( int argc, char** argv )
{
float accum = 0;
srand( time( NULL ) );
for( unsigned int i = 0; i < 100000000; ++i )
{
accum += (float)( rand( ) % 365 );
}
std::cout << accum << std::endl;
return 0;
}
short add/sub: 1.005460 [0]
short mul/div: 3.926543 [0]
long add/sub: 0.000000 [0]
long mul/div: 7.378581 [0]
long long add/sub: 0.000000 [0]
long long mul/div: 7.378593 [0]
float add/sub: 0.993583 [0]
float mul/div: 1.821565 [0]
double add/sub: 0.993884 [0]
double mul/div: 1.988664 [0]
short add/sub: 0.553863 [0]
short mul/div: 12.509163 [0]
long add/sub: 0.556912 [0]
long mul/div: 12.748019 [0]
long long add/sub: 5.298999 [0]
long long mul/div: 20.461186 [0]
float add/sub: 2.688253 [0]
float mul/div: 4.683886 [0]
double add/sub: 2.700834 [0]
double mul/div: 4.646755 [0]
#include <stdio.h>
#ifdef _WIN32
#include <sys/timeb.h>
#else
#include <sys/time.h>
#endif
#include <time.h>
#include <cstdlib>
double
mygettime(void) {
# ifdef _WIN32
struct _timeb tb;
_ftime(&tb);
return (double)tb.time + (0.001 * (double)tb.millitm);
# else
struct timeval tv;
if(gettimeofday(&tv, 0) < 0) {
perror("oops");
}
return (double)tv.tv_sec + (0.000001 * (double)tv.tv_usec);
# endif
}
template< typename Type >
void my_test(const char* name) {
Type v = 0;
// Do not use constants or repeating values
// to avoid loop unroll optimizations.
// All values >0 to avoid division by 0
// Perform ten ops/iteration to reduce
// impact of ++i below on measurements
Type v0 = (Type)(rand() % 256)/16 + 1;
Type v1 = (Type)(rand() % 256)/16 + 1;
Type v2 = (Type)(rand() % 256)/16 + 1;
Type v3 = (Type)(rand() % 256)/16 + 1;
Type v4 = (Type)(rand() % 256)/16 + 1;
Type v5 = (Type)(rand() % 256)/16 + 1;
Type v6 = (Type)(rand() % 256)/16 + 1;
Type v7 = (Type)(rand() % 256)/16 + 1;
Type v8 = (Type)(rand() % 256)/16 + 1;
Type v9 = (Type)(rand() % 256)/16 + 1;
double t1 = mygettime();
for (size_t i = 0; i < 100000000; ++i) {
v += v0;
v -= v1;
v += v2;
v -= v3;
v += v4;
v -= v5;
v += v6;
v -= v7;
v += v8;
v -= v9;
}
// Pretend we make use of v so compiler doesn't optimize out
// the loop completely
printf("%s add/sub: %f [%d]\n", name, mygettime() - t1, (int)v&1);
t1 = mygettime();
for (size_t i = 0; i < 100000000; ++i) {
v /= v0;
v *= v1;
v /= v2;
v *= v3;
v /= v4;
v *= v5;
v /= v6;
v *= v7;
v /= v8;
v *= v9;
}
// Pretend we make use of v so compiler doesn't optimize out
// the loop completely
printf("%s mul/div: %f [%d]\n", name, mygettime() - t1, (int)v&1);
}
int main() {
my_test< short >("short");
my_test< long >("long");
my_test< long long >("long long");
my_test< float >("float");
my_test< double >("double");
return 0;
}
short add: 0.822491
short sub: 0.832757
short mul: 1.007533
short div: 3.459642
long add: 0.824088
long sub: 0.867495
long mul: 1.017164
long div: 5.662498
long long add: 0.873705
long long sub: 0.873177
long long mul: 1.019648
long long div: 5.657374
float add: 1.137084
float sub: 1.140690
float mul: 1.410767
float div: 2.093982
double add: 1.139156
double sub: 1.146221
double mul: 1.405541
double div: 2.093173
short add: 1.369983
short sub: 1.235122
short mul: 1.345993
short div: 4.198790
long add: 1.224552
long sub: 1.223314
long mul: 1.346309
long div: 7.275912
long long add: 1.235526
long long sub: 1.223865
long long mul: 1.346409
long long div: 7.271491
float add: 1.507352
float sub: 1.506573
float mul: 2.006751
float div: 2.762262
double add: 1.507561
double sub: 1.506817
double mul: 1.843164
double div: 2.877484
short add: 1.999639
short sub: 1.919501
short mul: 2.292759
short div: 7.801453
long add: 1.987842
long sub: 1.933746
long mul: 2.292715
long div: 12.797286
long long add: 1.920429
long long sub: 1.987339
long long mul: 2.292952
long long div: 12.795385
float add: 2.580141
float sub: 2.579344
float mul: 3.152459
float div: 4.716983
double add: 2.579279
double sub: 2.579290
double mul: 3.152649
double div: 4.691226
short add: 1.094323
short sub: 1.095886
short mul: 1.356369
short div: 4.256722
long add: 1.111328
long sub: 1.079420
long mul: 1.356105
long div: 7.422517
long long add: 1.057854
long long sub: 1.099414
long long mul: 1.368913
long long div: 7.424180
float add: 1.516550
float sub: 1.544005
float mul: 1.879592
float div: 2.798318
double add: 1.534624
double sub: 1.533405
double mul: 1.866442
double div: 2.777649
short add: 3.396932
short sub: 3.530665
short mul: 3.524118
short div: 15.226630
long add: 3.522978
long sub: 3.439746
long mul: 5.051004
long div: 15.125845
long long add: 4.008773
long long sub: 4.138124
long long mul: 5.090263
long long div: 14.769520
float add: 6.357209
float sub: 6.393084
float mul: 6.303037
float div: 17.541792
double add: 6.415921
double sub: 6.342832
double mul: 6.321899
double div: 15.362536
g++ -fpermissive -O3 -o benchmark-pc benchmark-pc.c
short add: 0.773049
short sub: 0.789793
short mul: 0.960152
short div: 3.273668
int add: 0.837695
int sub: 0.804066
int mul: 0.960840
int div: 3.281113
long add: 0.829946
long sub: 0.829168
long mul: 0.960717
long div: 5.363420
long long add: 0.828654
long long sub: 0.805897
long long mul: 0.964164
long long div: 5.359342
float add: 1.081649
float sub: 1.080351
float mul: 1.323401
float div: 1.984582
double add: 1.081079
double sub: 1.082572
double mul: 1.323857
double div: 1.968488
short add: 1.235603
short sub: 1.235017
short mul: 1.280661
short div: 5.535520
int add: 1.233110
int sub: 1.232561
int mul: 1.280593
int div: 5.350998
long add: 1.281022
long sub: 1.251045
long mul: 1.834241
long div: 5.350325
long long add: 1.279738
long long sub: 1.249189
long long mul: 1.841852
long long div: 5.351960
float add: 2.307852
float sub: 2.305122
float mul: 2.298346
float div: 4.833562
double add: 2.305454
double sub: 2.307195
double mul: 2.302797
double div: 5.485736
short add: 1.040745
short sub: 0.998255
short mul: 1.240751
short div: 3.900671
int add: 1.054430
int sub: 1.000328
int mul: 1.250496
int div: 3.904415
long add: 0.995786
long sub: 1.021743
long mul: 1.335557
long div: 7.693886
long long add: 1.139643
long long sub: 1.103039
long long mul: 1.409939
long long div: 7.652080
float add: 1.572640
float sub: 1.532714
float mul: 1.864489
float div: 2.825330
double add: 1.535827
double sub: 1.535055
double mul: 1.881584
double div: 2.777245