为什么我会收到这些汇编错误?

时间:2014-03-13 22:33:21

标签: c++ gcc inline-assembly

我有一个很大的功能,需要在某一点上从浮点数转换为整数。如果没有这种转换,该功能在我的机器上需要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'

发生了什么事?

3 个答案:

答案 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位双用于浮点。

Live demo

This seems like a decent resource

答案 2 :(得分:-1)

如果你能比编译器更快地完成它,尽可能地抛出那个,并得到一个像样的。

请在这里告诉我们,所以没有人会想到认真使用它。