2012-04-30 28 views
6

Có một mảng lớn (~ 100 000) là biến động điểm và có ngưỡng (cũng là nổi điểm).So sánh điểm trôi nổi hiệu quả (Cortex-A8)

Vấn đề là tôi phải so sánh từng biến từ mảng với một ngưỡng, nhưng cờ NEON chuyển mất một thời gian thực sự dài (~ 20 chu kỳ phù hợp với một hồ sơ).

Có cách nào hiệu quả để so sánh các giá trị này không?

LƯU Ý: lỗi Như tròn không quan trọng, tôi đã cố gắng như sau:

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

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

Nhưng trong trường hợp này tôi nhận được chuỗi lệnh xử lý như sau:

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 

Như chuyển đổi xảy ra tại NEON, không có vấn đề gì nếu tôi so sánh các số nguyên, theo cách được mô tả hoặc nổi.

+0

Những người ở tại codereview.stackexchange.com cũng có thể phải nói điều gì đó với điều này. – PlasmaHH

+3

Mã của bạn không phù hợp với tuyên bố vấn đề của bạn - dữ liệu là nổi nhưng bạn hiển thị ngưỡng dưới dạng int - bạn cũng truyền mỗi giá trị dữ liệu float tới int - tại sao? Nếu dữ liệu của bạn trôi nổi thì ngưỡng phải trôi nổi và bạn nên thực hiện so sánh nổi (nghĩa là không có chuyển đổi int-float). Ngoài ra, bạn dự định làm gì với các giá trị lớn hơn (hoặc nhỏ hơn) ngưỡng (điều này sẽ xác định xem NEON có phù hợp hay không)? –

+2

Nhiều người bỏ NEON vì chậm hơn ARM mà không biết phải tránh những gì và cách lập trình SIMD đúng cách. Tùy thuộc vào những gì bạn chính xác muốn, nó hoặc là không SIMD khả thi để bắt đầu với, hoặc bạn không biết làm thế nào để xử lý nếu-else với NEON. –

Trả lời

5

Nếu nổi là 32-bit IEEE-754 và ints là 32-bit quá và nếu không có + vô cực, vô cực và NaN giá trị, chúng ta có thể so sánh nổi như ints với một mẹo nhỏ:

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

Output:

-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 

Tất nhiên, nó làm cho tinh thần để precalculate rằng giá trị số nguyên cuối cùng trong isGreater() đó được sử dụng trong các toán tử so sánh nếu ngưỡng của bạn không thay đổi.

Nếu bạn sợ hành vi không xác định trong C/C++ trong mã trên, bạn có thể viết lại mã trong assembly.

+0

Dường như là ý tưởng tuyệt vời. Tôi vẫn phải đối mặt với vấn đề với vmov.32 nhưng chủ yếu là một ý tưởng tốt. Cảm ơn. – Alex

+0

Giải pháp hoạt động. – Alex

+0

@vasile: Bạn đang nói về lỗi nào? Cái gì phức tạp? Nếu có, làm thế nào để bạn làm cho nó đơn giản hơn? –

2

Nếu dữ liệu của bạn nổi thì bạn nên so sánh với phao nổi, ví dụ:

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

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

nếu không bạn sẽ có chuyển đổi float-int đắt tiền.

+0

Nếu tôi so sánh giữa 2 phao nổi, nó sẽ gây ra việc chuyển đổi cờ-đăng ký tốn kém. Đó là lý do tại sao tôi đã cố gắng so sánh giữa 2 số nguyên. – Alex

+0

Bạn đang thực hiện những hoạt động nào sau khi kiểm tra ngưỡng là đúng/sai? –

+0

vcmpe.f32 s17, s16 vmrs APSR_nzcv, fpscr Nếu tôi có câu hỏi của bạn. – Alex

2

dụ của bạn cho thấy làm thế nào xấu mã trình biên dịch tạo ra có thể là:

Nó tải một giá trị với NEON chỉ để chuyển nó sang int, sau đó thực hiện một NEON-> chuyển ARM gây một tuôn ra đường ống dẫn đến 11 ~ 14 chu kỳ bị lãng phí.

Giải pháp tốt nhất là viết chức năng hoàn toàn trong lắp ráp bằng tay.

Tuy nhiên, có một thủ thuật đơn giản cho phép so sánh phao nhanh chóng mà không typecasting VÀ cắt ngắn:

Ngưỡng dương (chính xác nhanh như int so sánh):

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

Ngưỡng âm (1 chu kỳ hơn mỗi giá trị so với so sánh 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); 
} 

Tôi nghĩ nó nhanh hơn rất nhiều so với mức được chấp nhận ở trên. Một lần nữa, tôi hơi muộn.

Các vấn đề liên quan