SORU
20 NİSAN 2012, Cuma


Neden GCC neredeyse aynı C kodu için tamamen farklı böyle bir Kurul oluşturur mu?

ftol optimize edilmiş bir fonksiyon yazarken GCC 4.6.1 çok tuhaf davranışları buldum. Bana kod ilk (netlik farkları işaretledim için) göstereyim:

fast_trunc_one, C:

int fast_trunc_one(int i) {
    int mantissa, exponent, sign, r;

    mantissa = (i & 0x07fffff) | 0x800000;
    exponent = 150 - ((i >> 23) & 0xff);
    sign = i & 0x80000000;

    if (exponent < 0) {
        r = mantissa << -exponent;                       /* diff */
    } else {
        r = mantissa >> exponent;                        /* diff */
    }

    return (r ^ -sign)   sign;                           /* diff */
}

fast_trunc_two, C:

int fast_trunc_two(int i) {
    int mantissa, exponent, sign, r;

    mantissa = (i & 0x07fffff) | 0x800000;
    exponent = 150 - ((i >> 23) & 0xff);
    sign = i & 0x80000000;

    if (exponent < 0) {
        r = (mantissa << -exponent) ^ -sign;             /* diff */
    } else {
        r = (mantissa >> exponent) ^ -sign;              /* diff */
    }

    return r   sign;                                     /* diff */
}

Aynı değil mi? İyi GCC aynı fikirde değil. gcc -O3 -S -Wall -o test.s test.c ile derleme sonra bu derleme çıktı

fast_trunc_one, oluşturulan:

_fast_trunc_one:
LFB0:
    .cfi_startproc
    movl    4(%esp), êx
    movl    $150, ìx
    movl    êx, íx
    andl    $8388607, íx
    sarl    $23, êx
    orl $8388608, íx
    andl    $255, êx
    subl    êx, ìx
    movl    íx, êx
    sarl    %cl, êx
    testl   ìx, ìx
    js  L5
    rep
    ret
    .p2align 4,,7
L5:
    negl    ìx
    movl    íx, êx
    sall    %cl, êx
    ret
    .cfi_endproc

fast_trunc_two, oluşturulan:

_fast_trunc_two:
LFB1:
    .cfi_startproc
    pushl   ëx
    .cfi_def_cfa_offset 8
    .cfi_offset 3, -8
    movl    8(%esp), êx
    movl    $150, ìx
    movl    êx, ëx
    movl    êx, íx
    sarl    $23, ëx
    andl    $8388607, íx
    andl    $255, ëx
    orl $8388608, íx
    andl    $-2147483648, êx
    subl    ëx, ìx
    js  L9
    sarl    %cl, íx
    movl    êx, ìx
    negl    ìx
    xorl    ìx, íx
    addl    íx, êx
    popl    ëx
    .cfi_remember_state
    .cfi_def_cfa_offset 4
    .cfi_restore 3
    ret
    .p2align 4,,7
L9:
    .cfi_restore_state
    negl    ìx
    sall    %cl, íx
    movl    êx, ìx
    negl    ìx
    xorl    ìx, íx
    addl    íx, êx
    popl    ëx
    .cfi_restore 3
    .cfi_def_cfa_offset 4
    ret
    .cfi_endproc

Biraşırıfark. Aslında bu profil üzerinde de, fast_trunc_one 0 civarında daha hızlı fast_trunc_two daha gösterir. Bu neden oluyor? şimdi sorum şu:

CEVAP
20 NİSAN 2012, Cuma


OP ile eşitlemek için güncellendi düzenlemek

Kodu ile müdahalesi, GCC ilk dava optimize nasıl görmek başardım.

Bu kadar farklı olduğunu anlamak için önce fast_trunc_one() optimize nasıl anlamak gerekir.

İster inan ister inanma fast_trunc_one() Bu optimize ediliyor:

int fast_trunc_one(int i) {
    int mantissa, exponent;

    mantissa = (i & 0x07fffff) | 0x800000;
    exponent = 150 - ((i >> 23) & 0xff);

    if (exponent < 0) {
        return (mantissa << -exponent);             /* diff */
    } else {
        return (mantissa >> exponent);              /* diff */
    }
}

Bu orijinal olarak aynı Meclisi fast_trunc_one() - kayıt adları ve her şeyi üretir.

fast_trunc_one() derleme xors yok dikkat edin. Bu benim için onu verdim.


Nasıl yani?


Adım 1:sign = -sign

Önce sign değişken bir göz atalım. sign = i & 0x80000000; beri sign alabileceği sadece iki değer vardır:

  • sign = 0
  • sign = 0x80000000

Şimdi her iki durumda da, sign == -sign farkındayız. Bu orijinal kodu değiştirdiğimde bu nedenle:

int fast_trunc_one(int i) {
    int mantissa, exponent, sign, r;

    mantissa = (i & 0x07fffff) | 0x800000;
    exponent = 150 - ((i >> 23) & 0xff);
    sign = i & 0x80000000;

    if (exponent < 0) {
        r = mantissa << -exponent;
    } else {
        r = mantissa >> exponent;
    }

    return (r ^ sign)   sign;
}

fast_trunc_one() orijinal tam olarak aynı montaj üretir. Derleme bağışlarım, ama aynı isim ve kayıt.


Adım 2:Matematiksel azaltma: x (y ^ x) = y

sign yalnızca iki değer 0 0x80000000 birini alabilir.

  • x = 0 x (y ^ x) = y o zaman önemsiz bir tutar.
  • Ve 0x80000000 ekleyerek yazılmış bir oyun programı. aynı durum. İşaret biti değişiyor. Bu nedenle x (y ^ x) = y x = 0x80000000 zaman tutar.

Bu nedenle, x (y ^ x) y azaltır. Ve bu kodu.:

int fast_trunc_one(int i) {
    int mantissa, exponent, sign, r;

    mantissa = (i & 0x07fffff) | 0x800000;
    exponent = 150 - ((i >> 23) & 0xff);
    sign = i & 0x80000000;

    if (exponent < 0) {
        r = (mantissa << -exponent);
    } else {
        r = (mantissa >> exponent);
    }

    return r;
}

Yine aynı Meclis - kayıt adları ve tüm derler.


Bu yukarıdaki sürümü nihayet bu azaltır:

int fast_trunc_one(int i) {
    int mantissa, exponent;

    mantissa = (i & 0x07fffff) | 0x800000;
    exponent = 150 - ((i >> 23) & 0xff);

    if (exponent < 0) {
        return (mantissa << -exponent);             /* diff */
    } else {
        return (mantissa >> exponent);              /* diff */
    }
}

tam olarak GCC derleme oluşturur zaten.


Neden derleyici aynı şeyi fast_trunc_two() optimize değil?

fast_trunc_one() anahtar rol x (y ^ x) = y optimizasyonu. fast_trunc_two() 50 *ifade Şubesi arasında bölünmüş ediliyor.

GCC bu optimizasyonu yapmak için değil, şaşırtmak için yeterli olabilir sanırım. (Şube ^ -sign vinç için gerekir ve bu birleştirme sonunda r sign içine.)

Örneğin, bu fast_trunc_one() aynı montaj üretir:

int fast_trunc_two(int i) {
    int mantissa, exponent, sign, r;

    mantissa = (i & 0x07fffff) | 0x800000;
    exponent = 150 - ((i >> 23) & 0xff);
    sign = i & 0x80000000;

    if (exponent < 0) {
        r = ((mantissa << -exponent) ^ -sign)   sign;             /* diff */
    } else {
        r = ((mantissa >> exponent) ^ -sign)   sign;              /* diff */
    }

    return r;                                     /* diff */
}

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Anthony Le

    Anthony Le

    10 EKİM 2006
  • eHow

    eHow

    27 NİSAN 2006
  • Turkish Airlines

    Turkish Airl

    11 AĞUSTOS 2006