trunk函数很慢

Is the trunc function very slow?

本文关键字:函数 trunk      更新时间:2023-10-16

tl;dr:double b=a-(size_t)(a)double b=a-trunc(a)

我正在为图像实现一个旋转函数,我注意到trunc函数似乎非常慢。

在对图像进行循环代码时,像素的实际影响会被注释掉,以便进行性能测试,所以我甚至不访问像素。

double sina(sin(angle)), cosa(cos(angle));
int h = (int) (_in->h*cosa + _in->w*sina);
int w = (int) (_in->w*cosa + _in->h*sina);
int offsetx = (int)(_in->h*sina);
SDL_Surface* out = SDL_CreateARGBSurface(w, h); //wrapper over SDL_CreateRGBSurface
SDL_FillRect(out, NULL, 0x0);//transparent black
for (int y = 0; y < _in->h; y++)
    for (int x = 0; x < _in->w; x++){
            //calculate the new position
            const double destY = y*cosa + x*sina;
            const double destX = x*cosa - y*sina + offsetx;

这是使用trunc 的代码

size_t tDestX = (size_t) trunc(destX);
size_t tDestY = (size_t) trunc(destY);
double left = destX - trunc(destX);
double top = destY - trunc(destY);

这是更快的等效

size_t tDestX = (size_t)(destX);
size_t tDestY = (size_t)(destY);
double left = destX - tDestX;
double top = destY - tDestY;

答案建议在转换回积分时不要使用trunc,所以我也尝试过这种情况:

size_t tDestX = (size_t) (destX);
size_t tDestY = (size_t) (destY);
double left = destX - trunc(destX);
double top = destY - trunc(destY);

快速版本似乎平均需要30ms才能浏览完整图像(2048x1200(,而使用trunc的慢速版本对同一图像大约需要135ms。只有两次调用trunc的版本仍然比没有调用的版本慢得多(约100ms(。

就我对C++规则的理解而言,两个表达式应该总是返回相同的东西。我是不是遗漏了什么?dextXdestY被声明为const,因此只应对trunc函数进行一次调用,即使这样,它本身也无法解释慢三倍以上的因素。

我正在使用带有优化(/O2(的Visual Studio 2013进行编译。有什么理由使用trunc函数吗?即使是使用整数来获得分数部分似乎也更快。

按照您使用它的方式,根本没有理由使用trunc函数。它将一个二重转换为一个二重,然后将其转化为一个积分并丢弃。事实上,替代方案更快,这并不奇怪。

在现代x86 CPU上,int<->浮点转换非常快——通常会为转换生成内联SSE代码,成本大约为几个指令周期1

然而,对于trunc,需要函数调用,并且函数调用开销本身几乎肯定大于内联float->int转换的开销。此外,trunc函数本身可能相对昂贵-它必须完全符合IEEE-754,因此必须正确处理浮点值的整个范围,边缘情况也必须正确处理,如NaN、INF、分母、超出范围的值等。因此,总的来说,我预计trunc的成本约为数十个指令周期,即比内联float->int转换的成本大一个数量级左右。


1.请注意,float<->int转换并不总是便宜的——其他CPU系列,甚至更旧的x86 CPU,可能不支持ISA进行这种转换,在这种情况下,通常会使用库函数,其成本与trunc类似。在这方面,现代x86 CPU是一个特例。