2012-04-30 14 views
6

चल बिंदु चर का एक बड़ा (~ 100 000) सरणी है, और वहाँ एक सीमा (भी बिंदु चल) है।कुशल चल बिन्दु तुलना (Cortex-ए 8)

समस्या यह है कि मुझे प्रत्येक एक चर को सरणी से सरणी से तुलना करना है, लेकिन नीयन झंडे स्थानांतरण वास्तव में एक लंबा समय लेता है (~ 20 चक्र एक प्रोफाइलर के अनुसार)।

क्या इन मूल्यों की तुलना करने का कोई प्रभावी तरीका है?

नोट: के रूप में पूर्णांकन त्रुटि कोई फर्क नहीं पड़ता, मैं निम्नलिखित की कोशिश की:

float arr[10000]; 
float threshold; 
.... 

int a = arr[20]; // e.g. 
int t = threshold; 
if (t > a) {....} 

लेकिन इस मामले में मैं निम्नलिखित प्रोसेसर आदेश अनुक्रम हो रही: रूपांतरण होता है

vldr.32  s0, [r0] 
vcvt.s32.f32 s0, s0 
vmov   r0, s0 <--- takes 20 cycles as `vmrs APSR_nzcv, fpscr` in case of 
cmp   r0, r1   floating point comparison 

नीयन में, कोई फर्क नहीं पड़ता कि मैं पूर्णांक की तुलना करता हूं, वर्णित तरीके या फ्लोट द्वारा।

+0

codereview.stackexchange.com पर लोगों को भी कुछ कहना पड़ सकता है। – PlasmaHH

+3

आपका कोड आपकी समस्या कथन के साथ असंगत है - डेटा फ़्लोट है लेकिन आप थ्रेसहोल्ड को int के रूप में दिखाते हैं - आपने प्रत्येक फ्लोट डेटा वैल्यू को int में क्यों डाला - क्यों? यदि आप डेटा फ्लोट करते हैं तो थ्रेसहोल्ड फ्लोट होना चाहिए और आपको फ्लोट तुलना करना चाहिए (यानी कोई अंतर-फ्लोट रूपांतरण नहीं)। साथ ही, आप उन मानों के साथ क्या करने की योजना बना रहे हैं जो थ्रेसहोल्ड से अधिक (या उससे कम) हैं (यह निर्धारित करेगा कि नीयन उचित है या नहीं)? –

+2

कई लोग एआरएम से धीमे होने के लिए एनईओएन को कुचलने के बिना जानते हैं कि क्या बचाना है और सिम को सही ढंग से प्रोग्राम कैसे करें। आप जो चाहते हैं उसके आधार पर, यह या तो शुरू करने के लिए सिम संभव नहीं है, या आप नहीं जानते कि कैसे एनईओएन के साथ-साथ संभालना है। –

उत्तर

5

तैरता 32-बिट आईईईई-754 कर रहे हैं और ints 32-बिट भी कर रहे हैं और अगर कोई + अनंत, -infinity और NaN मान होते हैं, हम एक छोटे से चाल के साथ ints के रूप में तैरता की तुलना कर सकते हैं:

#include <stdio.h> 
#include <limits.h> 
#include <assert.h> 

#define C_ASSERT(expr) extern char CAssertExtern[(expr)?1:-1] 
C_ASSERT(sizeof(int) == sizeof(float)); 
C_ASSERT(sizeof(int) * CHAR_BIT == 32); 

int isGreater(float* f1, float* f2) 
{ 
    int i1, i2, t1, t2; 

    i1 = *(int*)f1; 
    i2 = *(int*)f2; 

    t1 = i1 >> 31; 
    i1 = (i1^t1) + (t1 & 0x80000001); 

    t2 = i2 >> 31; 
    i2 = (i2^t2) + (t2 & 0x80000001); 

    return i1 > i2; 
} 

int main(void) 
{ 
    float arr[9] = { -3, -2, -1.5, -1, 0, 1, 1.5, 2, 3 }; 
    float thr; 
    int i; 

    // Make sure floats are 32-bit IEE754 and 
    // reinterpreted as integers as we want/expect 
    { 
    static const float testf = 8873283.0f; 
    unsigned testi = *(unsigned*)&testf; 
    assert(testi == 0x4B076543); 
    } 

    thr = -1.5; 
    for (i = 0; i < 9; i++) 
    { 
    printf("%f %s %f\n", arr[i], "<=\0> " + 3*isGreater(&arr[i], &thr), thr); 
    } 

    thr = 1.5; 
    for (i = 0; i < 9; i++) 
    { 
    printf("%f %s %f\n", arr[i], "<=\0> " + 3*isGreater(&arr[i], &thr), thr); 
    } 

    return 0; 
} 

आउटपुट:

-3.000000 <= -1.500000 
-2.000000 <= -1.500000 
-1.500000 <= -1.500000 
-1.000000 > -1.500000 
0.000000 > -1.500000 
1.000000 > -1.500000 
1.500000 > -1.500000 
2.000000 > -1.500000 
3.000000 > -1.500000 
-3.000000 <= 1.500000 
-2.000000 <= 1.500000 
-1.500000 <= 1.500000 
-1.000000 <= 1.500000 
0.000000 <= 1.500000 
1.000000 <= 1.500000 
1.500000 <= 1.500000 
2.000000 > 1.500000 
3.000000 > 1.500000 
बेशक

, यह है कि तुलना ऑपरेटर में प्रयोग किया जाता है, तो अपनी सीमा में परिवर्तन नहीं होता isGreater() में है कि अंतिम पूर्णांक मान precalculate कर देनी चाहिए।

यदि आप उपरोक्त कोड में सी/सी ++ में अपरिभाषित व्यवहार से डरते हैं, तो आप असेंबली में कोड को फिर से लिख सकते हैं।

+0

बहुत अच्छा विचार लगता है। मुझे अभी भी vmov.32 के साथ समस्या का सामना करना पड़ रहा है लेकिन मुख्य रूप से यह एक अच्छा विचार है। धन्यवाद। – Alex

+0

समाधान काम करता है। – Alex

+0

@ वासाइल: आप किस बग के बारे में बात कर रहे हैं? जटिल क्या है? यदि यह है, तो आप इसे सरल कैसे बनाते हैं? –

0

यदि गोल करने वाली त्रुटियां कोई फर्क नहीं पड़ती हैं, तो आपको std::lrint का उपयोग करना चाहिए।

Faster Floating Point to Integer Conversions इसे फ़्लोट से int रूपांतरण के लिए उपयोग करने की अनुशंसा करता है।

+0

पहले से ही देखा है। यह अभी भी बहुत महंगा है। – Alex

2

यदि आपका डेटा फ़्लोट है तो आपको फ्लोट्स के साथ अपनी तुलना करना चाहिए, उदा।

float arr[10000]; 
float threshold; 
.... 

float a = arr[20]; // e.g. 
if (threshold > a) {....} 

अन्यथा आप महंगा नाव-पूर्णांक रूपांतरण होगा।

+0

यदि मैं 2 फ्लोट्स के बीच तुलना करता हूं तो यह महंगा ध्वज-रजिस्टर हस्तांतरण का कारण बनता है। यही कारण है कि मैंने 2 पूर्णांक के बीच तुलना करने की कोशिश की। – Alex

+0

थ्रेसहोल्ड टेस्ट सत्य/गलत होने पर आप बाद में कौन से ऑपरेशन कर रहे हैं? –

+0

vcmpe.f32 s17, s16 vmrs APSR_nzcv, fpscr यदि मुझे आपका प्रश्न मिला। – Alex

2

आपका उदाहरण दिखाता है कि बुरा संकलक कोड जनरेट किए गए हो सकते हैं:

यह नियोन के साथ एक मूल्य को लोड करता है बस int करने के लिए यह कन्वर्ट करने के लिए है, तो करता है एक NEON-> एआरएम हस्तांतरण कि 11 में जिसके परिणामस्वरूप एक पाइप लाइन फ्लश का कारण बनता है ~ 14 चक्र बर्बाद हो गए।

सबसे अच्छा समाधान पूरी तरह से हाथ विधानसभा में समारोह लिख रहा होगा।(पूर्णांक तुलना के रूप में बिल्कुल के रूप में तेजी से)

दहलीज सकारात्मक: प्रति

void example(float * pSrc, float threshold, unsigned int count) 
{ 
    typedef union { 
    int ival, 
    unsigned int uval, 
    float fval 
    } unitype; 

    unitype v, t; 
    if (count==0) return; 
    t.fval = threshold; 
    do { 
    v.fval = *pSrc++; 
    if (v.ival < t.ival) { 
     // your code here 
    } 
    else { 
     // your code here (optional) 
    } 
    } while (--count); 
} 

थ्रेसहोल्ड नकारात्मक (1 चक्र अधिक

हालांकि, वहाँ एक सरल चाल है कि typecasting और काट-छांट के बिना तेजी से नाव तुलना की अनुमति देता है int तुलना की तुलना में मूल्य):

void example(float * pSrc, float threshold, unsigned int count) 
{ 
    typedef union { 
    int ival, 
    unsigned int uval, 
    float fval 
    } unitype; 

    unitype v, t, temp; 
    if (count==0) return; 
    t.fval = threshold; 
    t.uval &= 0x7fffffff; 
    do { 
    v.fval = *pSrc++; 
    temp.uval = v.uval^0x80000000; 
    if (temp.ival >= t.ival) { 
     // your code here 
    } 
    else { 
     // your code here (optional) 
    } 
    } while (--count); 
} 

मुझे लगता है कि यह ऊपर स्वीकृत एक से काफी तेज है। दोबारा, मैं बहुत देर हो चुकी हूँ।

संबंधित मुद्दे