2012-09-23 21 views
8

Yaparsam:2^n üs hesaplamaları bit vardiyalardan gerçekten daha az verimlidir?

int x = 4; 
pow(2, x); 

Bu gerçekten o kadar az verimli adil yapmaktan daha:

1 << 4 

? derleyici bağlıdır, ancak (derleyici tamamen braindead olmadığında) genel olarak evet, vardiya bir işlemci talimatıdır

+3

Bunu denediniz mi? –

+2

"O kadar" ne kadar? Daha az verimli olmasını beklemelisiniz, aksi takdirde soruyu sormazsınız. Öyleyse burada sahip olduğumuz şey, akıl almayı umduğumuz araştırmalarda hiçbir girişimde bulunmadan, anlamsız bir sorudur.-1 –

+0

Bunu beklediğimden değildi, birisi bir pow (2, x); Kodumda vardı ve "her zaman 2'nin güçleri yerine biraz değişiyor" dedim ve daha önce hiç duymamıştım, bu yüzden buradaki soruyu burada sordum. – patrick

cevap

20

Evet. Bunu göstermenin kolay bir yolu, aynı şeyi yapan aşağıdaki iki işlevi derlemek ve sonra sökmeye bakmaktır.

#include <stdint.h> 
#include <math.h> 

uint32_t foo1(uint32_t shftAmt) { 
    return pow(2, shftAmt); 
} 

uint32_t foo2(uint32_t shftAmt) { 
    return (1 << shftAmt); 
} 

cc -arch armv7 -O3 -S -o - shift.c (Okumayı kolaylaştırmak ARM asm bulmak için ne ama x86 isterseniz sadece kemer bayrağını kaldırmak)

_foo1: 
@ BB#0: 
    push {r7, lr} 
    vmov s0, r0 
    mov r7, sp 
    vcvt.f64.u32 d16, s0 
    vmov r0, r1, d16 
    blx _exp2 
    vmov d16, r0, r1 
    vcvt.u32.f64 s0, d16 
    vmov r0, s0 
    pop {r7, pc} 

_foo2: 
@ BB#0: 
    movs r1, #1 
    lsl.w r0, r1, r0 
    bx lr 

Sen foo2 sadece birkaç talimatları alır 2 talimatları vs foo1 alır görebilirsiniz . Verileri FP HW yazmaçlarına (vmov) taşımak, tamsayıyı bir float (vcvt.f64.u32) işlevine çevirmek ve exp işlevini çağırmak ve sonra yanıtı bir uint'e (vcvt.u32.f64) dönüştürmek ve FP HW'den geriye GP kaydeder.

+1

+1. – fuzz

+1

Çoğu zaman burada gösterilen kodların hiçbirinde _exp2 işlevinde çekilecektir. –

0

, diğer cari devleti yığın çerçeve bir ayar biriktirmeye içeren bir işlev çağrısı olduğu Bu birçok talimat gerektirir.

1

Genellikle evet, bit vardiyası işlemci için çok basit bir işlemdir. Diğer taraftan, birçok derleyici kodu optimize eder, böylece güce güç sağlamak aslında biraz değişiyor.

+0

"Çift" için mi? Şüpheliyim. –

+1

Tabi ki, ama burada konuşuyorduk. –

+1

OP'nin örneğidir, yani pow() 'yi çağırıyorsanız. –

3

Evet. Ne kadar söyleyemem olsa da. Bunu belirlemenin en kolay yolu, onu karşılaştırmaktır.

pow işlevi, ikilileri kullanır ... En azından, C standardına uyuyorsa. o 2 bir üs gördüğünde o fonksiyonu bitshift kullanılan bile, yine de test ve basit bitshift tamamlanacağı hangi zaman bu sonuca ulaşmak dallanma olacaktı. Ve henüz bir fonksiyon çağrısının daha fazlasını düşünmedik bile.

Eşdeğerlik için 1 << 4 yerine 1 << x kullanmak istediğinizi varsayalım.

Belki de bir derleyici bunların her ikisini de optimize edebilir, ancak bir aramayı pow numaralı telefona göre optimize etme olasılığı o kadar düşüktür. 2'nin gücünü hesaplamanın en hızlı yoluna ihtiyacınız varsa, bunu değiştirerek yapın.

Güncelleme ... Değinilmesi kolay olduğundan bahsettiğimden, bunu yapmaya karar verdim. Windows ve Visual C++ kullanışlıdır, bu yüzden bunu kullandım. Sonuçlar değişecektir. Programım:

#include <Windows.h> 

#include <cstdio> 
#include <cmath> 
#include <ctime> 

LARGE_INTEGER liFreq, liStart, liStop; 


inline void StartTimer() 
{ 
    QueryPerformanceCounter(&liStart); 
} 


inline double ReportTimer() 
{ 
    QueryPerformanceCounter(&liStop); 
    double milli = 1000.0 * double(liStop.QuadPart - liStart.QuadPart)/double(liFreq.QuadPart); 
    printf("%.3f ms\n", milli); 
    return milli; 
} 


int main() 
{  
    QueryPerformanceFrequency(&liFreq); 

    const size_t nTests = 10000000; 
    int x = 4; 
    int sumPow = 0; 
    int sumShift = 0; 

    double powTime, shiftTime; 

    // Make an array of random exponents to use in tests. 
    const size_t nExp = 10000; 
    int e[nExp]; 
    srand((unsigned int)time(NULL)); 
    for(int i = 0; i < nExp; i++) e[i] = rand() % 31; 

    // Test power. 
    StartTimer(); 
    for(size_t i = 0; i < nTests; i++) 
    { 
     int y = (int)pow(2, (double)e[i%nExp]); 
     sumPow += y; 
    } 
    powTime = ReportTimer(); 

    // Test shifting. 
    StartTimer(); 
    for(size_t i = 0; i < nTests; i++) 
    { 
     int y = 1 << e[i%nExp]; 
     sumShift += y; 
    } 
    shiftTime = ReportTimer(); 

    // The compiler shouldn't optimize out our loops if we need to display a result. 
    printf("Sum power: %d\n", sumPow); 
    printf("Sum shift: %d\n", sumShift); 

    printf("Time ratio of pow versus shift: %.2f\n", powTime/shiftTime); 

    system("pause"); 
    return 0; 
} 

Benim çıkışı:

379.466 ms 
15.862 ms 
Sum power: 157650768 
Sum shift: 157650768 
Time ratio of pow versus shift: 23.92 
+1

Kayan nokta sayısını, tabanın '2' olsa bile, onu genişletmek için değiştiremezsiniz. –

+0

@CarlNorum Biliyorum, ancak tamsayı aralığında olduğunu ve tamsayıları kullanabileceğini test edebilirsiniz. Bu benim amacımdı ... Ama böyle bir test onu daha da yavaşlatacaktı. – paddy

+0

Karşılaştırma kodunu ve Visual C++ kullanarak bir Windows platformundan sonuçlar ekledim (çünkü bu benim kullandığım şeyden). – paddy