2015-02-24 28 views
7

gcc'da -O3 en iyileştirme ile derlenmiş basit C kodunu inceleyerek vektörleştirmeyi öğrenmeye çalışıyorum. Daha spesifik olarak, derleyiciler ne kadar iyi ifade ederler? Daha karmaşık hesaplamayla gcc-O3 performansını doğrulamak için kişisel bir yolculuktur. Geleneksel bilgeliğin, derleyicilerin insanlardan daha iyi olduğu, ama asla böyle bir bilgelik almadığım anlamına geldiğini anlıyorum. En iyileştirilmiş C kodunda derleme kodu yedeklemesi

İlk basit testimde, gcc seçeneklerinden bazılarını oldukça garip ve oldukça dürüst bir şekilde, optimizasyon açısından oldukça ihmalkar buluyorum. Derleyici amaca yönelik bir şey olduğunu ve CPU'yla ilgili bir şey bildiğimi (bu durumda Intel i5-2557M) bilmiyorum. Ama bilgili kişilerden bazı onaylara ihtiyacım var.

My basit bir test kodu (kademeli) aşağıdaki gibidir:

.L6:      ; loop starts here 
    movdqa xmm0, xmm1  ; copy packed integers in xmm1 to xmm0 
.L3: 
    movdqa xmm1, xmm0  ; wait, what!? WHY!? this is redundant. 
    cvtdq2ps xmm0, xmm0 ; convert integers to float 
    add rax, 16    ; increment memory pointer for next iteration 
    mulps xmm0, xmm0  ; pack square all integers in xmm0 
    paddd xmm1, xmm2  ; pack increment all integers by 4 
    movaps XMMWORD PTR [rax-16], xmm0 ; store result 
    cmp rax, rdx   ; test loop termination 
    jne .L6     

tüm adımları anlamak aşağıdaki gibidir: dongu olduğu tekabül

int i; 
float a[100]; 

for (i=0;i<100;i++) a[i]= (float) i*i; 

Sonuçta ortaya çıkan grup kodu (kademeli) ve hesaplamalı olarak, hepsi mantıklı. Anlamıyorum ne olacağım değil xmm0XMM1 ile yüklendi sonra adım xmm0 ile XMM1 yüklemek için iteratif döngü içinde dahil etmek seçerek gcc olduğunu.

.L6 
     movdqa xmm0, xmm1  ; loop starts here 
.L3 
     movdqa xmm1, xmm0  ; grrr! 
Yalnız bu, optimize edicinin aklı olup olmadığı sorusunu sorguluyor. Açıkçası, ekstra MOVDQA verileri rahatsız etmiyor, ancak yüz değeri olarak, gcc'un parçası üzerinde çok ihmalkar görünüyor. Daha önce montaj kodu (gösterilmemiştir) içerisinde

, xmm0 ve XMM2 döngüsünün başlangıcında, yani belli vektörleştirme için anlamlı bir değeri başlatılır, kodu ilk MOVDQA geçmek zorundadır. aşağıda gösterildiği gibi Ama neden gcc basitçe yeniden düzenlemek gelmez.

.L3 
     movdqa xmm1, xmm0  ; initialize xmm1 PRIOR to loop 
.L6 
     movdqa xmm0, xmm1  ; loop starts here 

Hatta daha da iyisi, sadece başlatmak XMM1 yerine xmm0ve MOVDQA xmm0 adım tamamen XMM1, dökümü!

Ben CPU böyle gereksiz adımı falan atlamak kadar akıllı olduğuna inanmaya hazırım

, ama nasıl bunu bile doğru bu basit kodunu alabilirsiniz eğer tam karmaşık kod optimize etmek gcc güvenebiliriz? Ya da birisi bana gcc-O3'un iyi şeyler olduğuna inanan bir ses açıklaması sağlayabilir mi?

+0

@Down seçmenleri: lütfen nedenini yorumlayın. – Stefan

+0

En iyileştirmeler açık duruma getirildi mi? Bazı optimizasyon seviyelerinde, gereksiz hareket işlemi ortadan kalkar. –

+1

Kodunuzun derleyicilerden daha hızlı olduğundan emin misiniz? Onları zamanlamaya çalıştın mı? – Degustaf

cevap

4

ben% 100 emin değilim, ama döngü float dönüştürerek xmm0 yok gibi görünüyor, bu nedenle xmm1 yılında tamsayı değeri vardır ve o zaman (bu durumda xmm0) başka kayıt için kopyalamak için.

Derleyiciler bazen gereksiz yönergeler yayınladığı bilinirken, bu durumda durumun nasıl olduğunu gerçekten göremiyorum.

Eğer tamsayı kalmasını xmm0 (veya xmm1) isterseniz, o zaman i ilk değeri için float bir döküm yok. Belki ne yapmak istediğini ise: yaklaşık 3 ila (3.7.0

g++ -S -O3 floop.cpp 

.L2: 
    cvtdq2ps %xmm1, %xmm0 
    mulps %xmm0, %xmm0 
    addq $16, %rax 
    paddd %xmm2, %xmm1 
    movaps %xmm0, -16(%rax) 
    cmpq %rbp, %rax 
    jne .L2 

Nor tınlamak kapsamaz:

for (i=0;i<100;i++) 
    a[i]= (float)(i*i); 

Ama öte yandan

, gcc 4.9.2 bunu görünmüyor önce hafta) Ben derledik

clang++ -S -O3 floop.cpp 


    movdqa .LCPI0_0(%rip), %xmm0 # xmm0 = [0,1,2,3] 
    xorl %eax, %eax 
    .align 16, 0x90 
.LBB0_1:        # %vector.body 
             # =>This Inner Loop Header: Depth=1 
    movd %eax, %xmm1 
    pshufd $0, %xmm1, %xmm1  # xmm1 = xmm1[0,0,0,0] 
    paddd %xmm0, %xmm1 
    cvtdq2ps %xmm1, %xmm1 
    mulps %xmm1, %xmm1 
    movaps %xmm1, (%rsp,%rax,4) 
    addq $4, %rax 
    cmpq $100, %rax 
    jne .LBB0_1 

Kodu:

extern int printf(const char *, ...); 

int main() 
{ 
    int i; 
    float a[100]; 

    for (i=0;i<100;i++) 
     a[i]= (float) i*i; 

    for (i=0; i < 100; i++) 
     printf("%f\n", a[i]); 
} 

(Derleyicinin şifreden kurtulmasını önlemek için printf ekledim)

+0

Ama aslında böyle oluyor. Eğer meclise bakarsanız, görebildiğiniz gibi, xmm0 şamandıraya dönüştürülür, kare haline getirilir ve kaydedilir. Soru, derleyicinin neden döngü atlamadan sonra xmm1 üzerine yazmasıdır. – Marandil

+0

Ah, iyi nokta. Yani, "derleyicileri yazmak çok zor" bir başka durum.Bir meydan okumadan hoşlanıyorsanız, bunun gcc'de nerede olduğunu bulmak için bir yol bulabileceğinizi ve bir düzeltme önerebileceğinizi söyleyebilirim. –

+1

Ya da sadece daha yeni bir gcc'ye yükseltin, belki? –