2014-03-13 51 views
2

我有一個大功能,需要從一個點浮動轉換爲整數。沒有這個轉換,我的機器上的功能需要11-12 ns /循環。通過轉換,它需要約400納秒/循環。爲什麼我會收到這些彙編錯誤?

經過一番閱讀,我找到了一種方法來加快使用一些內聯彙編的轉換。我的函數的第一次迭代如下:

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

它可以跨x86體系結構移植,在這種情況下,我需要的速度遠遠超過我需要的可移植性,尤其是考慮到它將運行的網格由x86機器組成。這個循環運行數百萬億次,我只能使用我的大學網格規則獲得600個核心,因此循環需要非常快速,因此需要組裝。如果您運行在一組有限的體系結構上,則可移植性無關緊要。 –

+0

我想我知道問題是什麼(我也不相信它會加速很多!)。在使用fpu時,你需要使用內存加載/存儲,而不是寄存器。我會測試並寫出答案。 –

+0

如果你打算爲速度編寫程序集,你應該考慮SIMD指令:http://en.wikipedia.org/wiki/Streaming_SIMD_Extensions –

回答

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 -02 -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 = ...

+0

對於時間安排,我已經走了:http://stereopsis.com/sree/fpu2006.html。他證明「我的解決方案」應該更快。 –

+0

你已經測試過了嗎?自2006年以來,例如編譯器就有一些奇怪的發展,今天的操作系統支持SSE2和SSE3指令,這些指令允許編譯器「做得更好」。如果你的編譯器是一個64位的x86編譯器,那麼你的「優化」的結果將是我發佈的第一個結果。換句話說,比編譯器生成的代碼慢大約2倍。如果你在沒有sse的情況下編譯x86 32位,則適用中間結果,如果你使用sse2編譯32位,則應該得到相同的2倍差。 –

+0

即使使用gcc -O3(版本4.5.1),編譯器也會生成需要很長時間比較的代碼。我自由地承認,自那時起事情可能會變得更好。我在循環中的其他地方使用了sse2,它表現良好,這是將事情降低到11-12ns。當我使用sse時,在最終商店之前,然後添加,轉換爲整數並簡單地添加整數,循環仍然需要200ns。我正在尋找其他方法。當然,我會以此爲基準,因爲我擁有其他一切。但是我首先無法弄清楚什麼是錯的。 –

-1

如果你能做得比你的編譯器更快,那麼儘可能地拋出一個,並得到一個體面的。

請在這裏告訴我們,所以沒有人會甚至想到認真使用它。

+0

那麼使用xmmintrin.h中可用的東西,就加速內部循環而言,我可以將gcc -O3(版本4.5.1)中的heck粉碎掉30倍左右。但是,我沒有看到有人很快就要離開gcc。 –

1

浮點數是內存操作數,而不是寄存器。因此,你需要這樣的:

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

相關問題