我有一个很大的功能,需要在某一点上从浮点数转换为整数。如果没有这种转换,该功能在我的机器上需要11-12 ns /循环。转换需要大约400 ns / loop。
经过一些阅读后,我发现了一种使用内联汇编加速转换的方法。我的函数的第一次迭代如下:
inline int FISTToInt (float f)
{
int i;
asm("fld %1;"
"fistp %0;"
:"=r" ( i )
:"r" ( f )
:
);
return i;
}
当我编译时,我得到以下错误:
src/calcRunner.cpp: Assembler messages:
src/calcRunner.cpp:43: Error: operand type mismatch for `fld'
src/calcRunner.cpp:43: Error: operand type mismatch for `fistp'
有点想法提供了答案,我忘了指令后缀,所以我将功能改为如下:
inline int FISTToInt (float f)
{
int i;
asm("flds %1;"
"fistps %0;"
:"=r" ( i )
:"r" ( f )
:
);
return i;
}
然而,这并没有解决问题,相反,我得到了这个:
src/calcRunner.cpp: Assembler messages:
src/calcRunner.cpp:43: Error: invalid instruction suffix for `fld'
src/calcRunner.cpp:43: Error: invalid instruction suffix for `fistp'
发生了什么事?
答案 0 :(得分:2)
这有效:
int trunk(float x)
{
int i;
__asm__ __volatile__(
" flds %1\n"
" fistpl %0\n"
: "=m"(i) : "m"(x));
return i;
}
但是,如果您实际使用的是x87模式,它只会(可能)比编译器生成的代码更快,并且速度更快,因为它不会加载和存储决定舍入的FP控制字。我将带回几个基准......
简单基准:
#include <stdio.h>
#include <stdlib.h>
int trunk(float x)
{
int i;
__asm__ __volatile__(
" flds %1\n"
" fistpl %0\n"
: "=m"(i) : "m"(x));
return i;
}
int trunk2(float x)
{
return (int)x;
}
inline long long rdtsc()
{
unsigned long a, d;
__asm volatile ("rdtsc" : "=a" (a), "=d" (d) : : "ebx", "ecx");
return a | ((long long)d << 32);
}
int main()
{
float f[1000];
for(int i = 0; i < 1000; i++)
{
f[i] = rand() / (i+1);
}
long long t = rdtsc();
int sum = 0;
for(int i = 0; i < 1000; i++)
{
sum = trunk(f[i]);
}
t = rdtsc() - t;
printf("Sum=%d time=%ld\n", sum, t);
t = rdtsc();
sum = 0;
for(int i = 0; i < 1000; i++)
{
sum = trunk2(f[i]);
}
t = rdtsc() - t;
printf("Sum=%d time=%ld\n", sum, t);
return 0;
}
使用gcc -O2 -m64 -std = c99编译,产生以下结果:
Sum=1143565 time=30196
Sum=1143565 time=15946
在32位编译中(gcc -O2 -m32 -std = c99):
Sum=1143565 time=29847
Sum=1143565 time=107618
换句话说,它慢得多。但是,如果我们启用sse2(并删除:gcc -m32 -msse2 -mfpmath=sse -O2
,它会变得更好:
Sum=1143565 time=30277
Sum=1143565 time=11789
请注意,第一个数字是“您的解决方案”,其中第二个结果是编译器的解决方案。
显然,请在您的系统上进行测量,以确保结果确实匹配。
编辑:在发现我实际上应该在循环中添加数字之后,而不是仅仅通过它们将它们放入sum
,我得到以下结果:
clang -m32 -msse2 -mfpmath=sse -O2 floatbm.c -std=c99
Sum=625049287 time=30290
Sum=625049287 time=3663
为什么它在“让编译器完成工作”方面做得更好的解释是Clang 3.5正在为第二个循环生成一个具有正确SSE simd的展开循环 - 它不能为第一个循环执行此操作,所以每次迭代都是1浮点值。
为了表明gcc仍然给出相同的结果,我重新运行gcc:
Sum=625049287 time=31612
Sum=625049287 time=15007
与以前不同的是,我使用sum += trunk(f[i]);
代替sum = ...
。
答案 1 :(得分:1)
Floats是内存操作数,不是寄存器。所以你需要这个:
inline int FISTToInt (float f) {
int i;
asm("flds %1;"
"fistl %0;"
:"=m" ( i )
:"m" ( f )
:
);
return i;
}
注意s
用于16位整数,但32位单(浮点)用于浮点,l
是32位int用于整数,但是64位双用于浮点。
答案 2 :(得分:-1)
如果你能比编译器更快地完成它,尽可能地抛出那个,并得到一个像样的。
请在这里告诉我们,所以没有人会想到认真使用它。