Özet: Tek-iş parçacıklı bir programdaki (komut seviyesi) paralelliği bulma ve kullanma, üzerinde çalıştığı CPU çekirdeği tarafından tamamen donanımda yapılır. Ve sadece birkaç yüz talimatın olduğu bir pencerede, büyük çapta yeniden sıralama değil.
Dışında Tek iş parçacıklı programlar, çok çekirdekli işlemcilerden hiçbir yararı olsun başka şeyler yerine tek iş parçacıklı görev uzak zaman ayırdığınız diğer çekirdek çalışabilir.
İşletim sistemi tüm iş parçacıklarının talimatlarını birbirlerini beklemeyecek şekilde düzenler.
İşletim sistemi, ipliklerin talimat akışlarına bakmaz. Sadece çekirdeği çekirdeğe zamanlar.
Aslında, her bir çekirdek, daha sonra ne yapacağını bulması gerektiğinde işletim sisteminin zamanlayıcı işlevini çalıştırır. Çizelgeleme dağıtılmış bir algoritmadır. Çok çekirdekli makineleri daha iyi anlamak için her çekirdeğin çekirdeği ayrı çalıştığını düşünün. Tıpkı çok iş parçacıklı bir program gibi, çekirdek de paylaşılır veri yapılarını (çalışmaya hazır olan iş parçacıklarının listesi gibi) güncellemek için bir çekirdekteki kodunun diğer çekirdeklerdeki kodlarıyla güvenli bir şekilde etkileşime girebilmesi için yazılmıştır.
Yine de, işletim sistemi çok iş parçacıklı işlemlerin , çok iş parçacıklı bir programın elle yazılmasıyla açıkça ortaya konması gereken iş parçacığı düzeyinde paralellikten yararlanmasına yardımcı olmakla ilgilenmektedir . (Veya OpenMP veya benzeri bir şey ile otomatik paralel bir derleyici tarafından ).
Ardından CPU'nun ön ucu ayrıca her bir çekirdeğe bir iş parçacığı dağıtarak bu talimatları düzenler ve her iş parçasından bağımsız komutları açık döngüler arasında dağıtır.
Bir CPU çekirdeği, durdurulmadığında yalnızca bir talimat akışını çalıştırıyordur (bir sonraki kesintiye kadar uyuyor, örneğin zamanlayıcı kesintisi). Genellikle bu bir iş parçacığıdır, ancak aynı zamanda bir iş parçacığı kesme işleyicisi veya başka bir çekirdek kodu, eğer çekirdeğin taşıma ve kesme ya da sistem çağrısından sonra bir önceki iş parçasına dönmekten başka bir şey yapmaya karar vermesi durumunda olabilir.
HyperThreading veya diğer SMT tasarımlarında, fiziksel bir CPU çekirdeği çoklu "mantıksal" çekirdekler gibi davranır. Dört çekirdekli, yüksek okuyuculu dört çekirdekli (4c8t) bir CPU ve düz 8 çekirdekli bir makine (8c8t) arasındaki bir işletim sistemi perspektifinden tek fark, HT tanıyan bir işletim sisteminin fiziksel çekirdeği ayırmak için konuları programlamaya çalışacak olmalarıdır. t birbirleriyle yarışmak. Hiper-dişlemeyi bilmeyen bir işletim sistemi sadece 8 çekirdeği görecektir (BIOS'ta HT'yi devre dışı bırakmazsanız, sadece 4'ü algılar).
" Ön uç" terimi, makine kodunu alan, talimatların kodunu çözen ve bunları çekirdeğin sıra dışı parçasına veren bir CPU çekirdeğinin bir parçasını belirtir . Her çekirdeğin kendi ön ucu var ve bir bütün olarak çekirdeğin bir parçası. O getirir Talimatlar olan CPU anda çalışan ne.
Çekirdeğin sıra dışı bölümünde, giriş işleyicileri hazır olduğunda ve boş bir yürütme bağlantı noktası bulunduğunda komutlar (veya uops) yürütme bağlantı noktalarına gönderilir. Bu, program sırasına göre gerçekleşmek zorunda değildir, bu nedenle bir OOO işlemcisinin tek bir iş parçacığı içindeki komut düzeyinde paralellikten nasıl yararlanabileceğidir .
"Çekirdek" i fikrinizde "yürütme birimi" ile değiştirirseniz, düzeltmeye yakınsınız. Evet, CPU, yürütme birimlerine paralel olarak bağımsız talimatlar / uops dağıtır. (Ancak bir terminoloji karışımı var, çünkü gerçekten CPU'nun talimat zamanlayıcısı olan Reservation Station çalıştırılmaya hazır olanları seçtiğinde "front-end" dediğinizden beri).
Sıra dışı yürütme, ILP'yi yalnızca yerel düzeyde, iki bağımsız döngü arasında (kısa olmadıkça) birkaç yüze kadar talimat bulabilir.
Örneğin, bunun asm eşdeğeri
int i=0,j=0;
do {
i++;
j++;
} while(42);
Intel Haswell'de yalnızca bir sayacı artıran aynı döngü kadar hızlı çalışacaktır. i++
Sadece önceki değerine bağlıdır i
iken, j++
sadece önceki değerine bağlıdır j
, böylece iki bağımlılık zincirleri programı sırayla yürütülmektedir her şeyin yanılsama bozmadan paralel olarak çalışabilir.
X86'da, döngü şöyle görünür:
top_of_loop:
inc eax
inc edx
jmp .loop
Haswell'de 4 tamsayı yürütme bağlantı noktası vardır ve hepsinde ek birim vardır, bu nedenle inc
hepsi bağımsızsa, saat başına 4 adede kadar komut verimini sürdürebilir . (Gecikme = 1 olduğunda, yalnızca 4 inc
talimatı uçuşta tutarak verimi en üst düzeye çıkarmak için yalnızca 4 yazıcınıza ihtiyacınız olur. Kontrastı, vektör-FP MUL veya FMA ile kontrastlandırın: gecikme = 5 çıkış = 0.5, 10 FMA'yı uçuşta tutmak için 10 vektör akümülatöre ihtiyaç duyar Verimi en üst düzeye çıkarmak için Ve her bir vektör 256b olabilir, 8 tek duyarlıklı yüzer tutan).
Alınan dal aynı zamanda bir tıkanıklıktır: ilmek her zaman yineleme başına en az bir tam saat alır, çünkü alınan dal verimi her saat başı 1 ile sınırlıdır. Performansı düşürmeden döngü içinde bir talimat daha ekleyebilirim, ayrıca okuma / yazma eax
veya edx
bu durumda bağımlılık zincirini uzatacaksa. Döngü içine (veya bir karmaşık multi-uop komutundan) 2 komut daha koymak, ön uçta bir tıkanıklık yaratacaktır; ( 4 uop'un katı olmayan döngüler için neler olduğuna ilişkin bazı ayrıntılar için bu SO Q&A'ya bakın : loop-buffer ve uop önbelleği işleri ilginç hale getirir.)
Daha karmaşık durumlarda, paralelliği bulmak daha geniş bir talimat penceresine bakmayı gerektirir . (örneğin, belki hepsinin birbirine bağlı, sonra bazı bağımsız olanlar için 10 komut dizisi vardır).
Yeniden Sipariş Arabellek kapasitesi, sıra dışı pencere boyutunu sınırlayan faktörlerden biridir. Intel Haswell'de 192 ay. (Ayrıca , kayıt defteri yeniden adlandırma kapasitesi (kayıt dosyası boyutu) ile birlikte deneysel olarak da ölçebilirsiniz .) ARM gibi düşük güçlü CPU çekirdeği, sıra dışı çalıştırma yaparlarsa çok daha küçük ROB boyutlarına sahiptir.
Ayrıca, CPU'ların sıra dışı olmasının yanı sıra boru hattında olması gerektiğini de unutmayın. Bu nedenle, çalıştırılmakta olanlardan çok daha önce, tercihen herhangi bir alım döngüsünü kaybettikten sonra tamponları tekrar doldurmak için yeterli verim ile talimatları almak ve kodunu çözmek zorundadır. Şubeler zor, çünkü bir şubenin hangi yöne gittiğini bilmiyorsak nereden getireceğimizi bile bilmiyoruz. Bu nedenle şube kestirimi çok önemlidir. (Ve neden modern CPU'lar spekülatif yürütme kullanıyorlar: bir dalın hangi yöne gideceğini ve bu komut akışını almaya / deşifre etmeye / yürütmeye başlayacağını tahmin ediyorlar. Bir yanlışlık tespit edildiğinde, bilinen en son iyi duruma geri dönüp oradan yürütüyorlar.)
İşlemci dahili değerleri hakkında daha fazla bilgi edinmek istiyorsanız, Stackoverflow x86 etiketi wiki'de , Agner Fog'un microarch kılavuzuna ve David Kanter'in Intel ve AMD CPU diyagramları içeren ayrıntılı yazılarına dahil olmak üzere bazı bağlantılar vardır . Intel Haswell mikro mimari yazısından , bu bir Haswell çekirdeğinin (çipin tamamı değil) tüm boru hattının son diyagramıdır.
Bu, tek bir CPU çekirdeğinin blok şemasıdır . Dört çekirdekli bir işlemci, bunlardan 4'ünü bir çipte, her biri kendi L1 / L2 önbelleklerine sahip (bir L3 önbellek, bellek denetleyicileri ve sistem cihazlarına PCIe bağlantılarını paylaşıyor) sahip.
Bunun çok karmaşık olduğunu biliyorum. Kanter'in makalesi, örneğin ön kısım hakkında yürütme birimlerinden veya önbelleklerden ayrı olarak konuşulacak kısımlarını da göstermektedir.