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

  • ecf150king

    ecf150king

    20 Ocak 2006
  • Glyn Dewis

    Glyn Dewis

    25 AĞUSTOS 2007
  • UberFacts

    UberFacts

    26 EKİM 2013