2012-04-30 14 views
6

Istnieje duża (~ 100 000) tablicę pływający zmiennoprzecinkowych, i nie ma próg (również pływający punkt).Skuteczna pływający porównanie punkt (Cortex-A8)

Problem polega na tym, że muszę porównać każdą zmienną z tablicy z progiem, ale transfer flag NEON trwa naprawdę długo (~ 20 cykli zgodnie z profilerem).

Czy istnieje jakiś skuteczny sposób porównania tych wartości?

UWAGA: błędu jako zaokrąglenie nie ma znaczenia, próbowałem następujące:

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

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

Ale w tym przypadku otrzymuję następującą sekwencję poleceń Procesor:

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 

Ponieważ konwersja dzieje w NEON, nie ma znaczenia, czy porównaję liczby całkowite, opisywaną drogą, czy pływaki.

+0

Osoby powyżej codereview.stackexchange.com również mogą coś o tym powiedzieć. – PlasmaHH

+3

Twój kod jest niespójny z twoim stwierdzeniem problemu - dane są zmienne, ale pokazujesz próg jako int - również rzucasz każdą wartość danych float na int - dlaczego? Jeśli dane są zmiennoprzecinkowe, próg powinien być zmienny i powinieneś wykonać porównanie zmiennoprzecinkowe (tzn. Bez konwersji typu int-float). Ponadto, co zamierzasz zrobić z wartościami, które są większe niż (lub mniejszy niż) próg (to zdecyduje, czy NEON jest odpowiedni, czy nie)? –

+2

Wiele osób porzuca NEON, ponieważ jest wolniejszy od ARM, nie wiedząc, czego unikać i jak poprawnie programować SIMD. W zależności od tego, czego akurat chcesz, albo nie jest możliwe rozpoczęcie SIMD, albo nie wiesz, jak sobie z nim poradzić, jeśli nie - z NEON. –

Odpowiedz

5

Jeśli pływaki są 32-bitowe IEEE-754 i ints są 32-bitowe zbyt i jeśli nie ma + nieskończoność, nieskończoność i NaN wartości, możemy porównać pływaków jako wskazówki przy odrobinie trick:

#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; 
} 

wyjściowa:

-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 

oczywiście, ma to sens, aby precalculate że ostateczną wartość całkowitą w isGreater(), który jest używany w operatora porównania, jeśli próg nie zmienia.

Jeśli boisz się niezdefiniowanego zachowania w C/C++ w powyższym kodzie, możesz przepisać kod w zespole.

+0

Wydaje się być świetnym pomysłem. Wciąż stoję w obliczu problemu z vmov.32, ale głównie jest to dobry pomysł. Dzięki. – Alex

+0

Rozwiązanie działa. – Alex

+0

@vasile: O jakim bugu mówisz? Co jest złożone? Jeśli tak, jak to ułatwić? –

2

Jeśli twoje dane są zmienne, powinieneś wykonać porównania z wartościami zmiennoprzecinkowymi, np.

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

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

w przeciwnym razie będziesz miał kosztowne konwersje float-int.

+0

Jeśli dokonam porównania między dwoma zmiennymi, spowoduje to kosztowny transfer z użyciem bander. Dlatego próbowałem dokonać porównania między dwiema liczbami całkowitymi. – Alex

+0

Jakie operacje wykonujecie później, gdy test progowy ma wartość prawda/fałsz? –

+0

vcmpe.f32 s17, s16 vms APSR_nzcv, fpscr Jeśli dostałem twoje pytanie. – Alex

2

Twój przykład pokazuje, jak złe kody kompilatora generowane mogą być:

Ładuje wartość z neon po prostu przekonwertować go na int, a następnie robi NEON-> Transfer ARM, który powoduje przypływ rurociągu powstałego w 11 ~ 14 cykli zmarnowanych.

Najlepszym rozwiązaniem byłoby napisanie funkcji całkowicie w montażu ręcznym.

Istnieje jednak prosty trik, który pozwala porównywać szybko pływaka bez rzutowania I obcinania:

Próg pozytywny (dokładnie tak szybko jak porównaniu int):

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); 
} 

Próg ujemny (1 cykl więcej za wartość niż porównanie 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); 
} 

Myślę, że to dużo szybciej niż przyjęty powyżej. Ponownie, jestem trochę za późno.

Powiązane problemy