Kayıtlar bu kadar hızlıysa, neden daha fazlasına sahip değiliz?


89

32bit'te 8 "genel amaçlı" yazmacımız vardı. 64bit ile miktar ikiye katlanıyor, ancak 64bit değişikliğinden bağımsız görünüyor.
Şimdi, eğer kayıtlar çok hızlıysa (hafıza erişimi yoksa), neden doğal olarak daha fazlası yok? CPU kurucularının CPU'da mümkün olduğunca çok yazmaç çalışması gerekmez mi? Neden sadece sahip olduğumuz miktara sahip olduğumuzun mantıksal kısıtlaması nedir?


CPU'lar ve GPU'lar, gecikmeyi öncelikli olarak sırasıyla önbellekler ve büyük çok iş parçacıklı okuma yoluyla gizler. Bu nedenle, CPU'ların az sayıda kaydı vardır (veya buna ihtiyaç duyarlar), oysa GPU'larda on binlerce kayıt vardır. Tüm bu değiş tokuşları ve faktörleri tartışan GPU kayıt dosyasındaki anket kağıdıma bakın .
user984260

Yanıtlar:


120

Çok sayıda kayda sahip olmamanızın birçok nedeni vardır:

  • Çoğu boru hattı aşamasına oldukça bağlıdırlar. Yeni başlayanlar için yaşam sürelerini takip etmeniz ve sonuçları önceki aşamalara geri iletmeniz gerekir. Karmaşıklık çok hızlı bir şekilde çözülemez hale gelir ve dahil olan tellerin sayısı (kelimenin tam anlamıyla) aynı oranda artar. Alan açısından pahalıdır, bu da sonuçta belirli bir noktadan sonra güç, fiyat ve performans açısından pahalı olduğu anlamına gelir.
  • Komut kodlama alanını kaplar. 16 yazmaç, kaynak ve hedef için 4 bit ve 3 işlenen talimatınız varsa 4 bit daha alır (örn. ARM). Bu, sadece kaydı belirtmek için kullanılan çok fazla komut seti kodlama alanı. Bu sonuçta kod çözmeyi, kod boyutunu ve yine karmaşıklığı etkiler.
  • Aynı sonucu elde etmenin daha iyi yolları var ...

Bu günlerde gerçekten çok sayıda sicilimiz var - bunlar açıkça programlanmadı. "Register renaming" var. Yalnızca küçük bir kümeye (8-32 yazmaç) erişirken, aslında çok daha büyük bir kümeyle (örneğin 64-256) desteklenirler. CPU daha sonra her kaydın görünürlüğünü izler ve bunları yeniden adlandırılmış kümeye tahsis eder. Örneğin, arka arkaya birçok kez yükleyebilir, değiştirebilir, daha sonra bir kayıtta depolayabilir ve bu işlemlerin her birinin, önbellek eksikliklerine vb. Bağlı olarak bağımsız olarak gerçekleştirilmesini sağlayabilirsiniz. ARM'de:

ldr r0, [r4]
add r0, r0, #1
str r0, [r4]
ldr r0, [r5]
add r0, r0, #1
str r0, [r5]

Cortex A9 çekirdekleri kayıt adını değiştirir, bu nedenle "r0" a ilk yükleme aslında yeniden adlandırılmış bir sanal sicile gider - hadi "v0" diyelim. Yükleme, artış ve saklama "v0" üzerinde gerçekleşir. Bu arada, tekrar r0'a yükleme / değiştirme / kaydetme gerçekleştiririz, ancak bu, r0 kullanan tamamen bağımsız bir dizi olduğu için "v1" olarak yeniden adlandırılır. Diyelim ki "r4" deki işaretçiden gelen yük, bir önbellek kaybı nedeniyle durdu. Sorun değil - "r0" ın hazır olmasını beklememize gerek yok. Yeniden adlandırıldığı için, bir sonraki sekansı "v1" ile çalıştırabiliriz (ayrıca r0 ile eşleştirilmiştir) - ve belki de bu bir önbellek isabetidir ve biz çok büyük bir performans kazandık.

ldr v0, [v2]
add v0, v0, #1
str v0, [v2]
ldr v1, [v3]
add v1, v1, #1
str v1, [v3]

Bence x86, bu günlerde çok sayıda yeniden adlandırılmış yazmaç (ballpark 256). Bu, her talimat için sadece kaynak ve hedefin ne olduğunu söylemek için 8 bit çarpı 2 olması anlamına gelir. Çekirdekte ihtiyaç duyulan tel sayısını ve boyutunu büyük ölçüde artıracaktır. Bu nedenle, çoğu tasarımcının kararlaştırdığı 16-32 kayıt arasında tatlı bir nokta var ve sıra dışı CPU tasarımları için, kayıt yeniden adlandırma, bunu azaltmanın bir yolu.

Düzenleme : Arıza dışı yürütmenin ve bunun üzerinde kayıt yeniden adlandırmanın önemi. OOO'ya sahip olduğunuzda, yazmaçların sayısı çok da önemli değildir çünkü bunlar sadece "geçici etiketlerdir" ve çok daha büyük sanal kayıt kümesine yeniden adlandırılırlar. Numaranın çok küçük olmasını istemezsiniz çünkü küçük kod dizileri yazmak zorlaşır. Bu, x86-32 için bir sorundur, çünkü sınırlı 8 yazmaç, yığın üzerinden çok sayıda geçicinin geçtiği anlamına gelir ve çekirdek okuma / yazma işlemlerini belleğe iletmek için ekstra mantığa ihtiyaç duyar. OOO'niz yoksa, genellikle küçük bir çekirdekten bahsediyorsunuzdur, bu durumda büyük bir kayıt seti düşük bir maliyet / performans avantajıdır.

Bu nedenle, çoğu CPU sınıfı için yaklaşık 32 mimari kayıtta maksimuma çıkan kayıt bankası boyutu için doğal bir tatlı nokta var. x86-32'de 8 yazmaç var ve kesinlikle çok küçük. ARM 16 yazmaçla gitti ve bu iyi bir uzlaşma. 32 yazmaç, bir şey varsa, biraz fazla - son 10'a ya da daha fazlasına ihtiyacınız kalmaz.

Bunların hiçbiri, SSE ve diğer vektör kayan nokta yardımcı işlemcileri için aldığınız ekstra yazmaçlara değinmez. Bunlar ekstra bir set olarak mantıklıdır çünkü tamsayı çekirdekten bağımsız çalışırlar ve CPU'nun karmaşıklığını üssel olarak büyütmezler.


12
Mükemmel cevap - Karışıma başka bir neden eklemek istiyorum - ne kadar çok yazmaç varsa, bağlam değiştirirken onları yığına atmak / yığından çıkarmak o kadar çok zaman alır. Kesinlikle önemli bir mesele değil, bir düşüncedir.
Will A

7
@WillA iyi bir nokta. Ancak, çok sayıda kaydı olan mimarilerin bu maliyeti azaltmanın yolları vardır. ABI genellikle çoğu yazmaç için arama kaydetme özelliğine sahip olacaktır, bu nedenle yalnızca bir çekirdek kümesi kaydetmeniz gerekir. Bağlam değiştirme genellikle yeterince pahalıdır ve fazladan kaydetme / geri yükleme, diğer tüm bürokrasiye kıyasla çok pahalıya mal olmaz. SPARC aslında kayıt bankasını bir bellek alanında bir "pencere" haline getirerek bunun üstesinden gelir, bu yüzden bununla bir şekilde ölçeklenir (bunu bir tür el salladı).
John Ripley

4
Kesinlikle beklemediğim bu kadar kapsamlı bir cevap aklımı uçurdu. Ayrıca neden bu kadar çok adlandırılmış kayda ihtiyacımız olmadığına dair bu açıklama için teşekkürler, bu çok ilginç! Cevabınızı okumaktan gerçekten zevk aldım, çünkü "kaputun altında" neler olup bittiğiyle tamamen ilgileniyorum. :) Bir cevabı kabul etmeden önce biraz daha bekleyeceğim, çünkü asla bilemezsin, ama + 1'im emin.
Xeo

1
Kayıtları kaydetme sorumluluğunun nerede olduğuna bakılmaksızın, geçen süre idari masraftır. Tamam, bu nedenle bağlam değiştirme en sık görülen durum olmayabilir, ancak kesintiler öyledir. Elle kodlanmış yordamlar yazmaçlardan tasarruf sağlayabilir, ancak sürücüler C ile yazılırsa, kesme-bildirilen işlevin her bir kaydı kaydedeceği ihtimali vardır, isr'yi çağırın ve sonra tüm kaydedilmiş kayıtları geri yükleyin. IA-32, RISC mimarilerinin 32'den fazla regs'e kıyasla 15-20 regs ile kesme avantajına sahipti.
Olof Forshell

1
Mükemmel yanıt, ancak "yeniden adlandırılmış" kayıtların "gerçek" adreslenebilir olanlarla doğrudan karşılaştırılmasına katılmıyorum. X86-32'de, 256 dahili yazmaçla bile herhangi bir tek yürütme noktasında yazmaçlarda saklanan 8'den fazla geçici değer kullanamazsınız. Temel olarak, kayıt yeniden adlandırma sadece OOE'nin ilginç bir yan ürünüdür, daha fazlası değildir.
noop

12

Biz Do Daha dükkânlarımız var

Hemen hemen her komutun mimari olarak görünür 1, 2 veya 3 yazmaç seçmesi gerektiğinden, bunların sayısının genişletilmesi kod boyutunu her komutta birkaç bit artıracak ve böylece kod yoğunluğunu azaltacaktır. Ayrıca , iş parçacığı durumu olarak kaydedilmesi ve bir işlevin etkinleştirme kaydına kısmen kaydedilmesi gereken bağlam miktarını da artırır . Bu işlemler sıklıkla gerçekleşir. Boru hattı kilitleri, her kayıt için bir puan tablosunu kontrol etmelidir ve bu, ikinci dereceden zaman ve uzay karmaşıklığına sahiptir. Ve belki de en büyük neden, önceden tanımlanmış komut setiyle uyumluluktur.

Ancak görünen o ki, yeniden adlandırma kaydı sayesinde , gerçekten çok sayıda kayıtlarımız var ve onları kaydetmemize bile gerek yok. CPU aslında birçok yazmaç kümesine sahiptir ve kodunuz çalıştırıldığında bunlar arasında otomatik olarak geçiş yapar. Bunu tamamen size daha fazla kayıt almak için yapar.

Misal:

load  r1, a  # x = a
store r1, x
load  r1, b  # y = b
store r1, y

Yalnızca r0-r7'ye sahip bir mimaride, aşağıdaki kod CPU tarafından otomatik olarak aşağıdaki gibi yeniden yazılabilir:

load  r1, a
store r1, x
load  r10, b
store r10, y

Bu durumda r10, geçici olarak r1 ile ikame edilen gizli bir kayıttır. CPU, ilk depodan sonra r1 değerinin bir daha asla kullanılmayacağını söyleyebilir. Bu, ikinci yüklemenin veya ikinci deponun geciktirilmesine gerek kalmadan ilk yüklemenin geciktirilmesine olanak tanır (yonga üzerindeki bir önbellek isabeti bile genellikle birkaç döngü alır).


2

Her zaman yazmaç eklerler, ancak genellikle özel amaçlı talimatlara (örneğin SIMD, SSE2, vb.) Bağlıdırlar veya taşınabilirliği azaltan belirli bir CPU mimarisine derlenmeyi gerektirirler. Mevcut talimatlar genellikle belirli kayıtlar üzerinde çalışır ve varsa diğer kayıtlardan yararlanamaz. Eski komut seti ve tümü.


1

Buraya biraz ilginç bilgi eklemek için, 8 aynı büyüklükteki kayıtlara sahip olmanın, işlem kodlarının onaltılık gösterimle tutarlılığı korumasına izin verdiğini fark edeceksiniz. Örneğin, komut push axx86 üzerinde opcode 0x50'dir ve son yazmaç di için 0x57'ye kadar gider. Ardından komut pop ax0x58'de başlar pop dive ilk base-16'yı tamamlamak için 0x5F'ye kadar gider . Onaltılık tutarlılık, boyut başına 8 kayıt ile korunur.


2
X86 / 64'te REX komut önekleri, yazmaç indekslerini daha fazla bit ile genişletir.
Alexey Frunze
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.