SORU
4 EYLÜL 2014, PERŞEMBE


Bir değeri C Bir dizinin mevcut olup olmadığını hızlı bir şekilde bulabilirsiniz?

Ben gömülü bir uygulama ile kritik bir IL olan ihtiyaçlar arasında yineleme bir dizi boyutu 256 (tercihen 1024 ama 256 minimum) ve kontrol edin eğer bir değeri ile eşleşen dizileri içindekiler. bool doğru olacaktır bu durumda. MCU LPC4357, ARM Cortex M4 çekirdekli, derleyici GCC bir NXP. Ben zaten optimizasyon seviye 2 (3 yavaştır) kombine ve RAM işlevi yerine flash yerleştirmek. Ben de işaretçi aritmetiği ve aşağı saymak yerine (i!=0 i<256 Eğer kontrol daha hızlı ise çok hızlı yapar for bir döngü kullanın. Sonuçta, büyük ölçüde uygulanabilir olması için az olmalıdır 12.5 bizi bir süre ile bitirmek. Bu şimdi kullandığım (pseudo) kod:

uint32_t i;
uint32_t *array_ptr = &theArray[0];
uint32_t compareVal = 0x1234ABCD;
bool validFlag = false;

for (i=256; i!=0; i--)
{
    if (compareVal == *array_ptr  )
    {
         validFlag = true;
         break;
     }
}

Bunu yapmak için mutlak en hızlı yolu ne olurdu? Satır içi derleme kullanarak izin verilir. Diğer 'daha şık' numaralar da izin verdi.

CEVAP
4 EYLÜL 2014, PERŞEMBE


Performans son derece önemli olduğu durumlarda, C derleyicisi, büyük olasılıkla el ayarlı assembly dili ile karşılaştırıldığında hızlı kod üretecek. Kaç yürütmek için en az direnç yolu bu gibi küçük rutinleri için, sadece sembolik makina kodunu yaz eğilimindedir ve iyi bir fikrim var. C kodu ile keman ve derleyici iyi çıktı oluşturmak için almak mümkün olabilir, ama çok fazla çıktı bu şekilde ayarlama kaybı söz konusu olabilir. Derleyiciler (özellikle Microsoft) buraya çok uzun bir yoldan son birkaç yıl, ama onlar yine de bizler kadar akıllı değiller derleyici arasında kulaklarını çünkü çalışıyorsun eğer özel bir durum değil genel bir durum. Derleyici bu kadar hızlı olabilir bazı talimatlar (örneğin TEMAS) kullanılamaz ve yeterli döngü göz önüne sermek için akıllı olmak olası değil. Benim yorumda bahsettiğim 3 fikirler içeriyor bunu yapmak için bir yol burada: Döngü çözümü, önbellek hazırlık ve birden fazla yük kullanma (temas) kullanma. Öğretim döngüsü sayısı dizi elemanı başına yaklaşık 3 saat için geliyor, ama bu hesabın bellek gecikmeleri içine almaz.

Çalışma teorisi:KOL CPU tasarım bir saat döngüsünde en talimatları yürütür, ancak talimatlar bir boru hattı yürütülür. C derleyicileri arasında diğer talimatları araya girme potansiyel gecikmeleri ortadan kaldırmak için çalışacağız. Orijinal C kodu gibi sıkı bir döngü ile sunulan, derleyici sabit bir zaman değeri bellekten okuma hemen oranla olması gerektiğinden gecikmeler saklanmak zorunda kalacak. Önemli ölçüde bellek gecikmeleri azaltmak için 4 kayıtçı 2 takım arasında dönüşümlü aşağıda benim kod kendisi ve ardışık veri alınıyor. Büyük veri setleri ve kod ile çalışmak en veya mevcut kayıtları tüm kullanımı yapmaz, genel olarak, maksimum performans alamayacaksın.

; r0 = count, r1 = source ptr, r2 = comparison value

   stmfd sp!,{r4-r11}   ; save non-volatile registers
   mov r3,r0,LSR #3     ; loop count = total count / 8
   pld [r1,#128]
   ldmia r1!,{r4-r7}    ; pre load first set
loop_top:
   pld [r1,#128]
   ldmia r1!,{r8-r11}   ; pre load second set
   cmp r4,r2            ; search for match
   cmpne r5,r2          ; use conditional execution to avoid extra branch instructions
   cmpne r6,r2
   cmpne r7,r2
   beq found_it
   ldmia r1!,{r4-r7}    ; use 2 sets of registers to hide load delays
   cmp r8,r2
   cmpne r9,r2
   cmpne r10,r2
   cmpne r11,r2
   beq found_it
   subs r3,r3,#1        ; decrement loop count
   bne loop_top
   mov r0,#0            ; return value = false (not found)
   ldmia sp!,{r4-r11}   ; restore non-volatile registers
   bx lr                ; return
found_it:
   mov r0,#1            ; return true
   ldmia sp!,{r4-r11}
   bx lr

Güncelleme: Benim deneyim anekdot/değersiz olduğunu düşünen yorum şüpheciler bir sürü var ve kanıt yok. GCC 4.8 (Android Mall of Sofia ve 9C) optimizasyon (en iyi duruma döndü . aşağıdaki çıktıyı üretmek için kullanıyorum ^strong>döngü çözümü de dahil olmak üzere). Orijinal C kodu soruda yukarıda sunulan derlenmiş. GCC üretilen:

.L9: cmp r3, r0
     beq .L8
.L3: ldr r2, [r3, #4]!
     cmp r2, r1
     bne .L9
     mov r0, #1
.L2: add sp, sp, #1024
     bx  lr
.L8: mov r0, #0
     b .L2

GCC çıktı tek döngü göz önüne sermek değil, ama aynı zamanda SİSTEMİNİZE bir durak sonra bir saat atıklar. Dizi elemanı başına en az 8 saat gerektirir. Döngü çıkmak için zaman bilmek adresini kullanarak iyi bir iş yok, ama Derleyiciler yapma yeteneği olan her yerde bu kod yer alıyor. Hedef platform üzerinde kod (ben değil) bakmadım, ama herkes KOL kod performans konusunda deneyimli kodumu daha hızlı olduğunu görebilirsiniz.

Güncelleme 2: Microsoft'un Visual Studio 2013 SP2 daha iyi kod ile yapmak için bir şans verdim. Oldu kullanabilir NEON talimatları vektörize benim dizisi başlatma, ama doğrusal değer arama yazan OP çıktı benzer GCC oluşturulan (ismi etiketleri için daha okunabilir):

loop_top:
   ldr  r3,[r1],#4  
   cmp  r3,r2  
   beq  true_exit
   subs r0,r0,#1 
   bne  loop_top
false_exit: xxx
   bx   lr
true_exit: xxx
   bx   lr

Dediğim gibi, bilmiyorum kendi OP tam donanım, ama acıyacak test performansı üzerinde bir nVıdıa Tegra 3 ve Tegra 4'ün 3 farklı versiyonu ve sonrası sonuçları çok yakında burada.

Güncelleme 3: Bir çift çekirdekli işlemcisine 3 Kodu ve benim Microsoft derlenmiş KOLU kodu inceledim ve 4 (Yüzey RT, Yüzey RT 2) çift çekirdekli işlemcisine sahip. Her şey önbelleğinde bir eşleşme bulmak için başarısız olan bir döngü 1000000 yineleme koştum ve kolay ölçmek için.

             My Code       MS Code
Surface RT    297ns         562ns
Surface RT 2  172ns         296ns  

Her iki durumda da benim kod neredeyse iki kat daha hızlı çalışır. En modern KOL CPU muhtemelen benzer sonuçlar verecektir.

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

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • GALERNAYA20

    GALERNAYA20

    19 NİSAN 2011
  • Julia Graf

    Julia Graf

    6 Mayıs 2006
  • Roger Huffman

    Roger Huffma

    4 ŞUBAT 2007