Önbellek çizgileri nasıl çalışır?


169

İşlemcinin önbelleğe veri aktardığını (örneğin Atom işlemcimde) okunan gerçek verilerin boyutu ne olursa olsun bir seferde yaklaşık 64 bayt getirdiğini anlıyorum.

Sorum şu:

Bellekten bir bayt okumanız gerektiğini düşünün, hangi 64 bayt önbelleğe getirilecek?

Görebildiğim iki olasılık, 64 baytın ilgili baytın altındaki en yakın 64 bayt sınırında başlaması veya 64 baytın önceden belirlenmiş bir şekilde baytın etrafına yayılmasıdır (örneğin, yarısı altı, yarısı üstü veya her şeyden önce).

Hangisi?


22
Bunu okuyun: Her programcının bellek hakkında bilmesi gerekenler . Sonra tekrar okuyun. Daha iyi (pdf) kaynak burada .
andersoj

Yanıtlar:


129

Yüklediğiniz bayt veya kelimeyi içeren önbellek satırı önbellekte yoksa, CPU'nuz önbellek sınırında başlayan 64 bayt ister (ihtiyacınız olanın altında en büyük adres 64'tür). .

Modern PC bellek modülleri bir seferde 64 bit (8 bayt) sekiz aktarımla aktarılır , böylece bir komut bellekten tam bir önbellek satırının okunmasını veya yazılmasını tetikler. (DDR1 / 2/3/4 SDRAM seri çekim boyutu 64B'ye kadar yapılandırılabilir; CPU'lar seri çekim boyutunu önbellek satır boyutlarına uyacak şekilde seçecektir, ancak 64B yaygındır)

Genel bir kural olarak, işlemci bir bellek erişimini tahmin edemezse (ve önceden getirdiyse), alma işlemi ~ 90 nanosaniye veya ~ 250 saat döngüsü alabilir (CPU'dan adresi bilen veriye kadar).

Buna karşılık, L1 önbelleğindeki bir isabetin 3 veya 4 döngü yük kullanım gecikmesi vardır ve mağaza yeniden yükleme modern x86 CPU'larda 4 veya 5 döngü mağaza yönlendirme gecikmesine sahiptir. Diğer mimarilerde işler benzer.

İlave okumalar: Ulrich Drepper Her Programcının Bellek Hakkında Bilmesi Gerekenler . Yazılım ön getirme önerisi biraz modası geçmiş: modern HW ön getiricileri daha akıllıdır ve hiper iş parçacığı P4 günlerinden çok daha iyidir (bu nedenle bir ön getirme iş parçacığı genellikle bir israftır). Ayrıca tag wiki, bu mimari için birçok performans bağlantısına sahiptir.


1
Bu cevap kesinlikle bir anlam ifade etmiyor. 64 bit bellek bant genişliği (bu konuda da yanlıştır) 64 bayt (!) İle yapmak için ne yapmaz? Ayrıca Ram'a vurursanız 10 ila 30 ns da tamamen yanlıştır. L3 veya L2 önbellek için doğru olabilir, ancak 90ns gibi olduğu RAM için geçerli olmayabilir. Demek istediğin patlama zamanı - patlama modunda sonraki dört kelimeye erişme zamanı (aslında doğru cevap)
Martin Kersten

5
@MartinKersten: DDR1 / 2/3/4 SDRAM'ın bir kanalı 64 bit veri yolu genişliği kullanıyor. Tüm bir önbellek hattının seri aktarımı, her biri 8B'lik sekiz transfer alır ve gerçekte olan budur. İşlemin, ilk önce istenen baytı içeren 8B hizalanmış yığının aktarılması, yani burada patlamanın başlatılması (ve eğer patlama aktarım boyutunun ilk 8B'si değilse sarılması) ile işlemin optimize edilmesi yine de doğru olabilir. Çok seviyeli önbellekli modern CPU'lar muhtemelen bunu artık yapmaz, çünkü bu, patlamanın ilk bloklarını L1 önbelleğine erken aktarmak anlamına gelir.
Peter Cordes

2
Haswell, L2 ve L1D önbellek arasında 64B'lik bir yola (yani tam bir önbellek hattı genişliği) sahiptir, bu nedenle istenen baytı içeren 8B'nin aktarılması bu veriyolunun verimsiz kullanımını sağlayacaktır. @Martin, ana belleğe gitmesi gereken bir yükün erişim süresi konusunda da doğrudur.
Peter Cordes

3
Verilerin bellek hiyerarşisinde bir kereye kadar gidip gitmediği veya L3'ün L2'ye göndermeye başlamadan önce bellekten tam bir satır bekleyip beklemediği hakkında iyi bir soru. Farklı önbellek seviyeleri arasında transfer arabellekleri vardır ve her bir kaçırılan hata bir tane talep eder. Yani ( toplam tahmin ) muhtemelen L3, bellek denetleyicisinden gelen baytları, onları istenen L2 önbelleği için uygun yük arabelleğine yerleştirirken aynı zamanda kendi alma arabelleğine koyar. Hat bellekten tamamen aktarıldığında, L3 L2'ye hattın hazır olduğunu bildirir ve kendi dizisine kopyalar.
Peter Cordes

2
@ Martin: Devam edip bu cevabı düzenlemeye karar verdim. Sanırım şimdi daha doğru ve hala basit. Gelecek okuyucular: ayrıca bkz. Mike76'nın sorusu ve cevabım: stackoverflow.com/questions/39182060/…
Peter Cordes

22

Önbellek satırları 64 bayt genişliğinde ise, 64 ile bölünebilen adreslerde başlayan bellek bloklarına karşılık gelir. Herhangi bir adresin en az 6 biti önbellek satırına kaydırılır.

Bu nedenle, herhangi bir bayt için, getirilmesi gereken önbellek satırı, adresin 64 ile bölünebilen en yakın adrese yuvarlamaya karşılık gelen en az önemli altı biti temizleyerek bulunabilir.

Bu donanım tarafından yapılmasına rağmen, bazı referans C makro tanımlarını kullanarak hesaplamaları gösterebiliriz:

#define CACHE_BLOCK_BITS 6
#define CACHE_BLOCK_SIZE (1U << CACHE_BLOCK_BITS)  /* 64 */
#define CACHE_BLOCK_MASK (CACHE_BLOCK_SIZE - 1)    /* 63, 0x3F */

/* Which byte offset in its cache block does this address reference? */
#define CACHE_BLOCK_OFFSET(ADDR) ((ADDR) & CACHE_BLOCK_MASK)

/* Address of 64 byte block brought into the cache when ADDR accessed */
#define CACHE_BLOCK_ALIGNED_ADDR(ADDR) ((ADDR) & ~CACHE_BLOCK_MASK)

1
Bunu anlamak için zor zamanım var. 2 yıl sonra biliyorum, ama bunun için bana örnek kod verebilir misin? bir veya iki satır.
Nick

1
@Nick Bu yöntemin çalışmasının nedeni ikili sayı sisteminde yatar. 2'nin herhangi bir gücünün sadece bir bit seti vardır ve kalan tüm bitler temizlenir, bu nedenle 64 0b1000000için son 6 basamağın sıfır olduğunu fark edersiniz , bu nedenle bu 6 setten herhangi birine sahip bir sayı olsa bile (sayıyı temsil eder) % 64) temizlerseniz, size en yakın 64 baytlık hizalanmış bellek adresi verilir.
legends2k

21

Her şeyden önce ana bellek erişimi çok pahalıdır. Şu anda 2GHz CPU (bir kez en yavaş) saniyede 2G kenelere (döngü) sahiptir. Bir CPU (günümüzde sanal çekirdek), işaret başına bir kez bir değer alabilir. Sanal çekirdek birden fazla işlem biriminden (ALU - aritmetik mantık birimi, FPU vb.) Oluştuğundan, mümkünse bazı talimatları paralel olarak işleyebilir.

Ana belleğe erişim yaklaşık 70ns ila 100ns arasındadır (DDR4 biraz daha hızlıdır). Bu süre temelde L1, L2 ve L3 önbelleğini arar ve daha sonra belleğe vurur (bellek bankalarına gönderen bellek denetleyicisine komut gönder), yanıtı bekle ve bitti.

100ns yaklaşık 200 keneyi ifade eder. Temel olarak, bir program her bir belleğin eriştiği önbellekleri her zaman özleyecekse, CPU zamanının yaklaşık% 99,5'ini (yalnızca belleği okursa) belleği beklemeden harcayacaktır.

İşleri hızlandırmak için L1, L2, L3 önbellekleri var. Doğrudan çipin üzerine yerleştirilen ve verilen bitleri saklamak için farklı bir transistör devresi kullanan belleği kullanırlar. Bir CPU genellikle daha gelişmiş bir teknoloji kullanılarak üretildiği ve L1, L2, L3 belleğindeki bir üretim arızasının CPU'yu değersiz hale getirme şansına sahip olduğu için bu daha fazla yer, daha fazla enerji ve ana bellekten daha maliyetlidir. büyük L1, L2, L3 önbellekleri, ROI'yi doğrudan azaltan verimi azaltan hata oranını artırır. Dolayısıyla, mevcut önbellek boyutu söz konusu olduğunda büyük bir değişim var.

(şu anda gerçek üretim kusurunun önbellek alanları CPU hatasını bir bütün olarak ortaya koyma olasılığını azaltmak için belirli bölümleri devre dışı bırakabilmek için daha fazla L1, L2, L3 önbellek oluşturur).

Bir zamanlama fikri vermek için (kaynak: önbelleklere ve belleğe erişim maliyetleri )

  • L1 önbellek: 1ns - 2ns (2-4 döngü)
  • L2 önbellek: 3ns - 5ns (6-10 döngü)
  • L3 önbellek: 12ns - 20ns (24-40 döngü)
  • RAM: 60ns (120 döngü)

Farklı CPU türlerini karıştırdığımız için bunlar sadece tahminlerdir, ancak bir bellek değeri getirildiğinde gerçekten neler olduğunu iyi bir fikir verir ve belirli önbellek katmanında bir isabet veya özlem olabilir.

Bu nedenle önbellek temel olarak bellek erişimini büyük ölçüde hızlandırır (60ns ve 1ns).

Bir değeri getirmek, tekrar okuma şansı için önbellekte saklamak, genellikle erişilen değişkenler için iyidir, ancak bellek kopyalama işlemleri için, bir değer okuduğu, değeri bir yere yazdığı ve değeri asla okumadığı için hala yavaş olacaktır. tekrar ... hiçbir önbellek isabet, ölü yavaş (bunun yanında paralel olarak olabilir çünkü biz sıra dışı yürütme var).

Bu bellek kopyası o kadar önemlidir ki, onu hızlandırmak için farklı yollar vardır. İlk günlerde bellek genellikle CPU dışında bellek kopyalayabiliyordu. Bellek denetleyicisi tarafından doğrudan işlendi, bu nedenle bellek kopyalama işlemi önbellekleri kirletmedi.

Ancak, düz bir bellek kopyasının yanı sıra, belleğin diğer seri erişimi de oldukça yaygındı. Bir örnek, bir dizi bilgiyi analiz etmektir. Bir tamsayı dizisine sahip olmak ve toplamı, ortalama, ortalama veya daha basit bir değeri belirli bir değeri (filtre / arama) hesaplamak, her zaman herhangi bir genel amaçlı CPU'da çalışan çok önemli bir algoritma sınıfıdır.

Bu nedenle, bellek erişim paternini analiz ederek verilerin sıralı olarak çok sık okunduğu anlaşıldı. Bir program indeks i'deki değeri okursa, programın i + 1 değerini de okuyabilme olasılığı yüksektir. Bu olasılık, aynı programın i + 2 değerini de okuma olasılığından biraz daha yüksektir.

Bu nedenle, bir bellek adresi verildiğinde, önceden okumak ve ek değerler getirmek iyi bir fikirdi (ve hala da öyle). Güçlendirme modunun olmasının nedeni budur.

Güçlendirme modunda bellek erişimi, bir adresin gönderildiği ve birden çok değerin sıralı olarak gönderildiği anlamına gelir. Her ek değer gönderimi yalnızca yaklaşık 10ns (hatta daha düşük) sürer.

Başka bir sorun da adresti. Adres göndermek zaman alır. Belleğin büyük bir bölümünü ele almak için büyük adresler gönderilmelidir. İlk günlerde, adres veri yolunun adresi tek bir döngüde (kene) gönderecek kadar büyük olmadığı ve adresi daha fazla gecikme ekleyerek göndermek için birden fazla döngü gerektiği anlamına geliyordu.

Örneğin 64 baytlık bir önbellek satırı, belleğin 64 bayt büyüklüğünde farklı (çakışmayan) bellek bloklarına bölündüğü anlamına gelir. 64bayt, her bloğun başlangıç ​​adresinin her zaman sıfır olmak üzere en düşük altı adres bitine sahip olduğu anlamına gelir. Bu nedenle, bu altı sıfır biti her seferinde göndermek gerekli değildir, böylece herhangi bir sayıda adres veriyolu genişliği için adres alanını 64 kat arttırır (hoş geldiniz efekti).

Önbellek satırının çözdüğü bir diğer sorun (ileri okuma ve adres veriyoluna altı bit kaydetme / boşaltma yanında) önbelleğin düzenlenme biçimidir. Örneğin, bir önbellek 8 baytlık (64 bit) bloklara (hücrelere) bölünürse, bu önbellek hücresinin değeri ile birlikte bellek hücresinin adresini saklaması gerekir. Adres 64 bit ise, bu önbellek boyutunun yarısının% 100 ek yük ile sonuçlanan adres tarafından tüketildiği anlamına gelir.

Bir önbellek satırı 64bayt olduğundan ve bir CPU 64bit - 6bit = 58bit (sıfır bitlerini çok doğru depolamaya gerek yok) kullanabileceğinden, 64bit veya 512 bit'i 58bit (% 11 ek yük) ile önbelleğe alabileceğimiz anlamına gelir. Gerçekte saklanan adresler bundan daha küçüktür, ancak durum bilgileri vardır (önbellek satırı geçerli ve doğru, kirli ve ram'de geri yazılması gerekir gibi).

Başka bir yönü de set-ilişkisel önbellek var. Her önbellek hücresi belirli bir adresi değil, yalnızca bir alt kümesini saklayabilir. Bu, gerekli saklanan adres bitlerini daha da küçük yapar, önbelleğe paralel erişime izin verir (her alt kümeye bir kez erişilebilir, ancak diğer alt kümelerden bağımsız olarak).

Özellikle, farklı sanal çekirdekler, çekirdek başına bağımsız çoklu işlem birimleri ve son olarak bir anakartta (48 işlemci ve daha fazlası içeren kartlar içeren) birden fazla işlemci arasında önbellek / bellek erişimini senkronize etmek söz konusu olduğunda daha çok şey var.

Temelde neden önbellek hatlarımız olduğuna dair şu anki fikir budur. Ön okumadan elde edilen yarar çok yüksektir ve önbellek satırından tek bir bayt okumanın en kötü durumu ve geri kalanını bir daha asla okumama olasılığı çok incedir.

Önbellek satırının boyutu (64), daha büyük önbellek satırları arasında seçilen akıllıca bir ödünleşmedir ve son baytının yakın gelecekte de okunması, tüm önbellek satırının alınması için gereken süre bellekten (ve geri yazmak için) ve ayrıca önbellek organizasyonundaki ek yük ve önbellek ve bellek erişiminin paralelleştirilmesi.


1
Bir küme ilişkilendirilebilir önbellek, bir küme seçmek için bazı adres bitleri kullanır, böylece etiketler örneğinizden bile daha kısa olabilir. Tabii ki, önbelleğin hangi etiketin kümede hangi veri dizisiyle gittiğini de takip etmesi gerekir, ancak genellikle bir küme içindeki yollardan daha fazla küme vardır. (örn. 32kB 8 yönlü ilişkilendirilebilir L1D önbellek, 64B satırlı, Intel x86 CPU'larda: ofset 6 bit, dizin 6 bit. Etiketler yalnızca 48-12 bit genişliğinde olmalıdır, çünkü x86-64 (şimdilik) yalnızca 48- Bildiğim gibi, düşük 12 bitin sayfa ofseti olması tesadüf değil, bu yüzden L1 diğer ad olmadan VIPT olabilir.)
Peter Cordes

şaşırtıcı cevap bud ... herhangi bir yerde bir "beğen" düğmesi var mı?
Edgard Lima

@EdgardLima, upvote düğmesi değil mi?
Pacerier

6

İşlemciler çok düzeyli önbelleklere (L1, L2, L3) sahip olabilir ve bunlar boyut ve hız bakımından farklılık gösterir.

Yine de, her önbelleğe tam olarak ne girdiğini anlamak için, söz konusu işlemci tarafından kullanılan dal tahmincisini ve programınızın talimatlarının / verilerinin ona karşı nasıl davrandığını incelemeniz gerekir.

Şube tahmincisi , CPU önbelleği ve değiştirme politikaları hakkında bilgi edinin .

Bu kolay bir iş değil. Günün sonunda istediğiniz tek şey bir performans testi ise Cachegrind gibi bir araç kullanabilirsiniz . Bununla birlikte, bu bir simülasyon olduğu için sonucu bir dereceye kadar değişebilir.


4

Her donanım farklı olduğu için kesin olarak söyleyemem, ancak genellikle CPU için çok hızlı ve basit bir işlem olduğu için "64 bayt aşağıda en yakın 64 bayt sınırında başlar" dır.


2
Ben yapabilirsiniz kesin söylemek. Herhangi bir makul önbellek tasarımı, 2 gücü olan ve doğal olarak hizalanmış boyutlara sahip çizgilere sahip olacaktır. (örneğin, 64B hizalı). Sadece hızlı ve basit değil, kelimenin tam anlamıyla ücretsiz: örneğin, adresin düşük 6 bitini görmezden geliyorsunuz. Önbellekler genellikle farklı adres aralıklarıyla farklı şeyler yapar. (örneğin, önbellek isabetle özlülüğü algılamak için etiket ve dizine önem verir, ardından yalnızca veri eklemek / çıkarmak için bir önbellek satırındaki ofseti kullanır)
Peter Cordes
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.