x86

2008-09-17 14 views
20

पर फ्लोट से int को परिवर्तित करने का सबसे तेज़ तरीका क्या है आप फ़्लोटिंग-पॉइंट नंबर को x86 CPU पर int में कनवर्ट करने का सबसे तेज़ तरीका क्या है। -> 32/64-बिट पूर्णांक x86

  • 32/64/80-बिट फ्लोट: अधिमानतः सी या विधानसभा में निम्न में से किसी भी संयोजन के लिए (कि सी में में लाइन हो सकता है) मैं कुछ तकनीक की तलाश में हूं जो संकलक को ऐसा करने की तुलना में तेज है।

+0

एक पेंटियम 5 से चिप के लिए स्विच करें जो गणित सही है ... (मैन जो मुझे पुराना महसूस करता है ...) – JBB

+0

मैं जमीन पर चारों ओर घूम रहा हूं। डांग - यह बहुत बुरे लोगों ने आपको इसके लिए नीचे संशोधित किया है! – Kevin

+0

यह इसके लायक था। :) – JBB

उत्तर

16

यह अगर आप चाहते हैं पर निर्भर करता है एक छोटा रूपांतरण या एक गोलाकार एक और सटीक पर। डिफ़ॉल्ट रूप से, जब आप फ्लोट से int तक जाते हैं तो सी एक छोटा रूपांतरण करेगा। एफपीयू निर्देश हैं जो ऐसा करते हैं लेकिन यह एएनएसआई सी रूपांतरण नहीं है और इसका उपयोग करने के लिए महत्वपूर्ण चेतावनी हैं (जैसे एफपीयू राउंडिंग स्टेटस को जानना)। चूंकि आपके समस्या का जवाब काफी जटिल है और कुछ चर आप व्यक्त नहीं किया है पर निर्भर करता है, मैं इस मुद्दे पर इस लेख की सिफारिश:

http://www.stereopsis.com/FPU.html

12

एसएसई का उपयोग करके पैक किया गया रूपांतरण अब तक का सबसे तेज़ तरीका है, क्योंकि आप एक ही निर्देश में एकाधिक मानों को परिवर्तित कर सकते हैं। ffmpeg में इसके लिए बहुत सारी असेंबली है (ज्यादातर ऑडियो के डीकोडेड आउटपुट को पूर्णांक नमूने में परिवर्तित करने के लिए); कुछ उदाहरणों के लिए इसे जांचें।

+0

यह एक अच्छा सुझाव है, हालांकि मैं इसे दो चीजें मानता हूं: - आपके पास एसएसई (> पीआईआई) या एसएसई 2 (> पीआईआईआई) के साथ एक x86 प्रोसेसर है - वास्तव में आप एक छंटनी चाहते हैं, नहीं एक गोल, रूपांतरण –

+0

यह भी ध्यान दें कि यह निश्चित रूप से 80-बिट फ़्लोटिंग पॉइंट मान – PhiS

6

एक फ्लोटिंग पॉइंट को असेंबली में एक इंट में बदलने के लिए एक निर्देश है: FISTP निर्देश का उपयोग करें। यह फ़्लोटिंग-पॉइंट स्टैक से मूल्य को पॉप करता है, इसे एक पूर्णांक में परिवर्तित करता है, और फिर निर्दिष्ट पते पर स्टोर करता है। मुझे नहीं लगता कि एक तेज़ तरीका होगा (जब तक आप एमएमएक्स या एसएसई जैसे विस्तारित निर्देश सेट का उपयोग नहीं करते हैं, जिसे मैं परिचित नहीं हूं)।

एक और निर्देश, FIST, एफपी स्टैक पर मूल्य छोड़ देता है लेकिन मुझे यकीन नहीं है कि यह क्वाड-शब्द आकार के गंतव्यों के साथ काम करता है।

3

यदि आप वास्तव में इसकी गति की परवाह करते हैं तो सुनिश्चित करें कि आपका कंपाइलर FIST निर्देश उत्पन्न कर रहा है। MSVC में आप/QIfist साथ ऐसा कर सकते, see this MSDN overview

तुम भी आप के लिए काम करने के लिए SSE intrinsics उपयोग करने पर विचार कर सकते हैं, इंटेल का यह लेख देखें: http://softwarecommunity.intel.com/articles/eng/2076.htm

-7

आमतौर पर, आप संकलक भरोसा कुशल और सही होने के लिए कर सकते हैं। आमतौर पर कंपाइलर में मौजूद कुछ चीज़ों के लिए अपने स्वयं के कार्यों को रोल करके प्राप्त करने के लिए कुछ भी नहीं होता है।

+2

के लिए एक विकल्प नहीं होगा, आप बस गलत हैं। इस मामले में अपने आप को रोल करना अंतर्निहित कार्यों पर एक बहुत ही शक्तिशाली 10x गति सुधार है क्योंकि जब आप इसे स्वयं करते हैं तो आप एफपीयू झंडे की स्थिति पर भरोसा कर सकते हैं, जो _ftol में निर्मित नहीं होता है, या आप इसका उपयोग कर समानांतर कर सकते हैं SSE। –

+2

या आप '-msse3' (gcc) को ध्वजांकित कर सकते हैं और 'निश्चित' FTSTTP इसे सही, निर्बाध रूप से कर सकते हैं। – akauppi

+0

संकलक की आपूर्ति दिनचर्या में अच्छी तरह से मल्टीमीडिया अनुप्रयोगों के लिए अनुकूल है, जहां प्रदर्शन महत्वपूर्ण –

6

लुआ कोड बेस में ऐसा करने के लिए निम्न स्निपेट है (www.lua.org से src/luaconf.h में जांचें)। यदि आपको लगता है कि (एसओ पाता है) एक तेज़ तरीका है, तो मुझे यकीन है कि वे रोमांचित होंगे।

ओह, lua_Number का मतलब है डबल। :)

/* 
@@ lua_number2int is a macro to convert lua_Number to int. 
@@ lua_number2integer is a macro to convert lua_Number to lua_Integer. 
** CHANGE them if you know a faster way to convert a lua_Number to 
** int (with any rounding method and without throwing errors) in your 
** system. In Pentium machines, a naive typecast from double to int 
** in C is extremely slow, so any alternative is worth trying. 
*/ 

/* On a Pentium, resort to a trick */ 
#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \ 
    (defined(__i386) || defined (_M_IX86) || defined(__i386__)) 

/* On a Microsoft compiler, use assembler */ 
#if defined(_MSC_VER) 

#define lua_number2int(i,d) __asm fld d __asm fistp i 
#define lua_number2integer(i,n)  lua_number2int(i, n) 

/* the next trick should work on any Pentium, but sometimes clashes 
    with a DirectX idiosyncrasy */ 
#else 

union luai_Cast { double l_d; long l_l; }; 
#define lua_number2int(i,d) \ 
    { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; } 
#define lua_number2integer(i,n)  lua_number2int(i, n) 

#endif 

/* this option always works, but may be slow */ 
#else 
#define lua_number2int(i,d) ((i)=(int)(d)) 
#define lua_number2integer(i,d) ((i)=(lua_Integer)(d)) 

#endif 
9

एक सादे x86/x87 कोड के लिए आमतौर पर इस्तेमाल किया चाल पूर्णांक प्रतिनिधित्व करने के लिए नाव की अपूर्णांश हिस्सा मजबूर करने के लिए है। 32 बिट संस्करण निम्नानुसार है।

64-बिट संस्करण समान है। उपरोक्त पोस्ट किया गया लुआ संस्करण तेजी से है, लेकिन 32-बिट परिणाम के लिए डबल की छंटनी पर निर्भर करता है, इसलिए इसे x87 इकाई को डबल परिशुद्धता पर सेट करने की आवश्यकता होती है, और इसे डबल से 64-बिट int रूपांतरण के लिए अनुकूलित नहीं किया जा सकता है।

इस कोड के बारे में अच्छी बात यह है कि यह आईईईई 754 के अनुरूप सभी प्लेटफार्मों के लिए पूरी तरह से पोर्टेबल है, केवल एक ही धारणा है कि फ्लोटिंग पॉइंट राउंडिंग मोड निकटतम पर सेट है। नोट: पोर्टेबल यह समझ में आता है कि यह संकलित करता है और काम करता है। X86 के अलावा प्लेटफ़ॉर्म आमतौर पर इस तकनीक से अधिक लाभ नहीं लेते हैं, अगर बिलकुल भी।

static const float Snapper=3<<22; 

union UFloatInt { 
int i; 
float f; 
}; 

/** by Vlad Kaipetsky 
portable assuming FP24 set to nearest rounding mode 
efficient on x86 platform 
*/ 
inline int toInt(float fval) 
{ 
    Assert(fabs(fval)<=0x003fffff); // only 23 bit values handled 
    UFloatInt &fi = *(UFloatInt *)&fval; 
    fi.f += Snapper; 
    return ((fi.i)&0x007fffff) - 0x00400000; 
} 
+2

अहस्ताक्षरित पूर्णांक यह हो सकता है के लिए है नहीं कर रहे हैं सरल: इनलाइन toInt (नाव fval) uint32_t { स्थिर नाव स्थिरांक स्नैपर = 1 << 23; फ़वाल + = स्नैपर; वापसी (* (uint32_t *) fval) और 0x007FFFFF; } – chmike

+0

'स्थैतिक फ्लोट कॉन्स स्नैपर; 'यह आवश्यक से धीमा बनाता है। बस लिखें 'fval + = 1 << 23; ' –

+3

x86 पर यह धीमा नहीं है, क्योंकि उत्पन्न कोड समान है। X87 पर तत्काल तर्क लेने वाले कोई एफपीयू निर्देश नहीं हैं। – Suma

6

आप सीपीयू अपने कोड चलाने की गारंटी कर सकते हैं SSE3 संगत (यहां तक ​​कि पेंटियम 5, JBB है), तो आप संकलक अपने FISTTP अनुदेश उपयोग करने दे सकता है (यानी जीसीसी के लिए -msse3)।

http://software.intel.com/en-us/articles/how-to-implement-the-fisttp-streaming-simd-extensions-3-instruction/

ध्यान दें कि FISTTP FISTP (कि सुस्ती के कारण, इसकी समस्या नहीं है) से अलग है: ऐसा लगता है कि यह हमेशा किया गया है चाहिए काम करते हैं लगता है। यह एसएसई 3 के हिस्से के रूप में आता है लेकिन वास्तव में (केवल) X87-side परिशोधन है।

अन्य तो एक्स 86 सीपीयू शायद रूपांतरण ठीक से करेगा, वैसे भी। :)

Processors with SSE3 support

3

के बाद से एमएस X64 में इनलाइन विधानसभा से बाहर हमें scews और intrinsics उपयोग करने के लिए हमें मजबूर करता है, मैं ऊपर देखा जो उपयोग करने के लिए। MSDN doc एक उदाहरण के साथ _mm_cvtsd_si64x देता है।

उदाहरण काम करता है, लेकिन 2 युगल के एक असाइन किए गए भार का उपयोग करके बेहद अक्षम है, जहां हमें केवल एक लोड की आवश्यकता है, इसलिए अतिरिक्त संरेखण आवश्यकता से छुटकारा पाएं। तब अनावश्यक भार और पुनः लोड का एक बहुत उत्पादन कर रहे हैं, लेकिन वे इस प्रकार समाप्त किया जा सकता:

#include <intrin.h> 
#pragma intrinsic(_mm_cvtsd_si64x) 
long long _inline double2int(const double &d) 
{ 
    return _mm_cvtsd_si64x(*(__m128d*)&d); 
} 

परिणाम:

 i=double2int(d); 
000000013F651085 cvtsd2si rax,mmword ptr [rsp+38h] 
000000013F65108C mov   qword ptr [rsp+28h],rax 

गोलाई मोड, इनलाइन विधानसभा के बिना स्थापित किया जा सकता उदा

_control87(_RC_NEAR,_MCW_RC); 

जहां निकटतम गोलाकार डिफ़ॉल्ट है (वैसे भी)।

सवाल यह है कि क्या प्रत्येक कॉल पर राउंडिंग मोड सेट करना है या इसे मानना ​​है कि इसे पुनर्स्थापित किया जाएगा (तीसरे पक्ष के libs) को अनुभव से जवाब देना होगा, मुझे लगता है। आपको _control87() और संबंधित स्थिरांक के लिए float.h शामिल करना होगा।

और, नहीं, यह 32 बिट में काम करेंगे नहीं है, इसलिए FISTP अनुदेश का उपयोग कर रखें:

_asm fld d 
_asm fistp i 
+0

यह दिलचस्प है, और यह सही प्रतीत होता है, लेकिन मेरे परीक्षणों में x64 कंपाइलर वास्तव में आपके कोड और एमएसडीएन उदाहरण के लिए * सटीक एक ही कोड * (एक डिस्सेबलर का उपयोग करके सत्यापित) उत्पन्न करता है। –

2

मैं एक ही मान काट-छांट की आवश्यकता होती है, जैसे कि एक 'सी' में i = (int)f लिखता है।

आप SSE3 है, तो आप उपयोग कर सकते हैं:

int convert(float x) 
{ 
    int n; 
    __asm { 
     fld x 
     fisttp n // the extra 't' means truncate 
    } 
    return n; 
} 

वैकल्पिक रूप से, SSE2 के साथ (या 64 जहां इनलाइन विधानसभा उपलब्ध नहीं हो सकता में), आप लगभग के रूप में तेजी से उपयोग कर सकते हैं:

#include <xmmintrin.h> 
int convert(float x) 
{ 
    return _mm_cvtt_ss2si(_mm_load_ss(&x)); // extra 't' means truncate 
} 

पुराने कंप्यूटरों पर राउंडिंग मोड को मैन्युअल रूप से सेट करने और सामान्य fistp निर्देश का उपयोग करके रूपांतरण करने का विकल्प होता है।यह संभवतः केवल फ्लोट के सरणी के लिए काम करेगा, अन्यथा किसी भी संरचना का उपयोग न करने के लिए देखभाल की जानी चाहिए जो संकलक परिवर्तन गोल मोड (जैसे कास्टिंग) को बनाएगी। यह इस तरह से किया जाता है:

void Set_Trunc() 
{ 
    // cw is a 16-bit register [_ _ _ ic rc1 rc0 pc1 pc0 iem _ pm um om zm dm im] 
    __asm { 
     push ax // use stack to store the control word 
     fnstcw word ptr [esp] 
     fwait // needed to make sure the control word is there 
     mov ax, word ptr [esp] // or pop ax ... 
     or ax, 0xc00 // set both rc bits (alternately "or ah, 0xc") 
     mov word ptr [esp], ax // ... and push ax 
     fldcw word ptr [esp] 
     pop ax 
    } 
} 

void convertArray(int *dest, const float *src, int n) 
{ 
    Set_Trunc(); 
    __asm { 
     mov eax, src 
     mov edx, dest 
     mov ecx, n // load loop variables 

     cmp ecx, 0 
     je bottom // handle zero-length arrays 

    top: 
     fld dword ptr [eax] 
     fistp dword ptr [edx] 
     loop top // decrement ecx, jump to top 
    bottom: 
    } 
} 

ध्यान दें कि इनलाइन विधानसभा केवल माइक्रोसॉफ्ट के दृश्य स्टूडियो compilers (और शायद बोर्लेन्ड) के साथ काम करता है, यह आदेश जीसीसी के साथ संकलित करने के जीएनयू विधानसभा के लिए लिखा जा करना होगा। इंट्रिनिक्स के साथ एसएसई 2 समाधान काफी पोर्टेबल होना चाहिए, हालांकि।

अन्य राउंडिंग मोड विभिन्न एसएसई 2 इंट्रिनिक्स द्वारा या मैन्युअल रूप से एफपीयू नियंत्रण शब्द को एक अलग राउंडिंग मोड में सेट करके संभव है।

+0

फिर इनलाइन असेंबली: हाँ एम्बरकाडेरो (पूर्व में बोर्लैंड) इसका समर्थन करता है (दोनों सी ++ और डेल्फी कंपाइलर्स करते हैं) – PhiS