Bellek hizalama ne kadar önemlidir? Hala önemli mi?


15

Bir süredir bellek hizalaması, nasıl çalıştığı ve nasıl kullanılacağı hakkında çok şey araştırdım ve okudum. Şimdilik bulduğum en alakalı makale bu .

Ancak bununla ilgili hala bazı sorularım var:

  1. Gömülü sistemden, bilgisayarımızda bellek yönetimini çok daha az eleştiren yapan büyük bellek yığınlarımız var, tamamen optimizasyondayım, ama şimdi, aynı programı ile karşılaştırırsak gerçekten fark yaratabilecek bir şey mi yoksa bellek yeniden düzenlenmeden ve hizalanmadan?
  2. Bellek hizalamanın başka avantajları var mı? CPU'nun hizalanmış bellekle daha iyi / daha hızlı çalıştığı bir yerde okudum, çünkü bu işlemek için daha az talimat alır (eğer birinizle ilgili bir makale / kıyaslama için bir bağınız varsa?), Bu durumda, fark gerçekten önemli mi? Bu ikisinden daha fazla avantaj var mı?
  3. Makale bağlantısında, 5. bölümde yazar şöyle diyor:

    Dikkat: C ++ 'da, yapılara benzeyen sınıflar bu kuralı kırabilir! (Temel sınıfların ve sanal üye işlevlerinin nasıl uygulandığına bağlıdır ve derleyiciye göre değişir.)

  4. Makale çoğunlukla yapılar hakkında konuşuluyor, ancak yerel değişkenler beyanı da bu ihtiyaçtan etkileniyor mu?

    Bazı farklılıklar olduğu için bellek hizalamanın tam olarak C ++ ile nasıl çalıştığı hakkında bir fikriniz var mı?

Bu eski soru "hizalama" kelimesini içerir, ancak yukarıdaki sorulara cevap vermez.


C ++ derleyicileri bunu yapmak için daha eğilimlidir (gerekli veya faydalı olan yerlerde dolgu ekleyin). Bahsettiğiniz bağlantıdan, kullanabileceğiniz şeyler için bölüm 12 "Araçlar" altına bakın.
rwong

Yanıtlar:


11

Evet, verilerinizin hem hizalanması hem de düzenlenmesi, yalnızca yüzde birkaç değil, yüzde birkaç ila yüzlerce arasında performansta büyük bir fark yaratabilir.

Bu döngüyü alın, yeterli döngü çalıştırıyorsanız iki talimat önemlidir.

.globl ASMDELAY
ASMDELAY:
    subs r0,r0,#1
    bne ASMDELAY
    bx lr

Önbellekli ve önbelleğe sahip olmayan ve dal tahmininde önbellek ile ve önbelleksiz hizalama ile bu iki komutun performansını önemli miktarda değiştirebilirsiniz (zamanlayıcı keneleri):

min      max      difference
00016DDE 003E025D 003C947F

Kendinizi kolayca yapabileceğiniz bir performans testi. test edilen kodun etrafına düğüm ekleyin veya çıkarın ve doğru bir zamanlama işi yapın, test edilen talimatları önbellek çizgilerinin kenarlarına dokunacak kadar geniş bir adres aralığında hareket ettirin.

Veri erişimiyle aynı tür bir şey. Bazı mimariler, size bir veri hatası vererek, hizalanmamış erişimlerden (örneğin 0x1001 adresinde 32 bit okuma gerçekleştirme) şikayet eder. Bunlardan bazıları hatayı devre dışı bırakabilir ve performans isabetini alabilirsiniz. Atanmamış erişime izin veren diğerleri sadece performans isabetini alırsınız.

Bazen "talimatlar" ama çoğu zaman saat / otobüs çevrimleridir.

Çeşitli hedefler için gcc'deki memcpy uygulamalarına bakın. Diyelim ki 0x43 bayt olan bir yapıyı kopyalıyorsunuz, 0x42 bırakarak bir bayt kopyalayıp daha sonra büyük verimli parçalar halinde 0x40 baytı kopyalayan bir uygulama bulabilirsiniz, sonra son 0x2 iki ayrı bayt veya 16 bit aktarım olarak yapabilir. Kaynak ve hedef adresler 0x1003 ve 0x2003 ile aynı hizalamadaysa hizalama ve hedef devreye girer, sonra bir bayt, daha sonra büyük parçalar halinde 0x40, sonra 0x2, ancak biri 0x1002 ve diğer 0x1003 ise, o zaman alır çok çirkin ve çok yavaş.

Çoğu zaman otobüs döngüleri. Veya daha kötü transfer sayısı. ARM gibi 64 bit genişliğinde bir veri yoluna sahip bir işlemci alın ve 0x1004 adresine dört kelimeyle aktarma (okuma veya yazma, LDM veya STM), bu kelime ile hizalanmış bir adres ve mükemmel bir yasal, ancak veri yolu 64 ise bit genişliğinde tek komutun 0x1004'te 32 bit, 0x1008'de 64 bit ve 0x100A'da 32 bit olmak üzere üç aktarıma dönüşmesi muhtemeldir. Ancak aynı talimatı aldıysanız, ancak 0x1008 adresinde, 0x1008 adresinde tek bir dört sözcük aktarımı yapabilir. Her aktarımın ilişkili bir kurulum süresi vardır. Bu nedenle, 0x1004 - 0x1008 adres farkı tek başına birkaç kat daha hızlı olabilir, hatta bir önbellek kullanırken / esp ve hepsi de önbellek isabetidir.

Bundan bahsetmişken, 0x1000 vs 0x0FFC adresinde iki kelimelik bir okuma yapmış olsanız bile, önbellek özlemleri olan 0x0FFC, 0x1000'in bir önbellek satırı olduğu iki önbellek satırı okumasına neden olacak, rastgele bir önbellek satırı cezası zaten olacak erişim (kullanmaktan daha fazla veri okuma) ancak bu iki katına çıkar. Yapılarınızın nasıl hizalandığı veya genel olarak verileriniz ve bu verilere vb. Erişim sıklığınız önbellek bozulmasına neden olabilir.

Verileri işlerken tahliye oluşturabileceğiniz, gerçek şanssızlaşabileceğiniz ve önbelleğinizin yalnızca bir kısmını kullanarak sonuçlandırabileceğiniz gibi sonlandırabilir ve bir sonraki veri bloğu bir önceki blob ile çarpıştığında . Verilerinizi karıştırarak veya kaynak kodundaki işlevleri yeniden düzenleyerek, çarpışmalar oluşturabilir veya kaldırabilirsiniz, çünkü tüm önbellekler eşit şekilde oluşturulmaz, derleyici burada size yardımcı olmayacaktır. Performans vuruşunu veya iyileştirmeyi tespit etmek bile sizin üzerinizde.

Performansı artırmak için eklediğimiz her şey, daha geniş veri yolları, boru hatları, önbellekler, şube tahmini, çoklu yürütme birimleri / yolları vb. Çoğu zaman yardımcı olacaktır, ancak hepsinin kasıtlı veya yanlışlıkla kullanılabilecek zayıf noktaları vardır. Derleyici veya kütüphanelerin bu konuda yapabileceği çok az şey var, eğer performansla ilgileniyorsanız ayarlamanız gerekir ve en büyük ayar faktörlerinden biri kodun ve verilerin hizalanmasıdır, sadece 32, 64, 128, 256'ya hizalanmamıştır. bit sınırlarının yanı sıra, şeylerin birbirine göreli olduğu yerlerde, yoğun bir şekilde kullanılan döngülerin veya yeniden kullanılan verilerin aynı önbellek yolunda inmemesini istiyorsunuz, her biri kendi istediklerini istiyor. Derleyiciler, örneğin bir süper skaler mimari için talimatların sıralanmasına yardımcı olabilir, birbirlerine göre önemli olmayan talimatları yeniden düzenleyebilir,

En büyük gözetim, işlemcinin darboğaz olduğu varsayımıdır. On yıldan fazla bir süredir doğru olmamış, işlemciyi beslemek problemdir ve hizalama performansı isabetleri, önbellek atma, vb. Kaynak kodu düzeyinde bile küçük bir çalışma ile, bir yapıdaki verilerin yeniden düzenlenmesi, değişken / yapı bildirimlerinin sıralanması, kaynak kodu içindeki işlevlerin sıralanması ve verilerin hizalanması için biraz fazla kod kullanılması, performansı birkaç kez artırabilir veya Daha.


Yalnızca son paragrafınız için +1. Bellek bant genişliği, bugün hızlı kod yazmaya çalışan herkes için en önemli konudur, talimat sayısı değil. Ve bu, birçok durumda hizalamayı değiştirerek yapılabilecek önbellek hatalarını azaltmak için şeyleri optimize etmenin çok önemli olduğu anlamına gelir.
Jules

Kodunuz ve verileriniz önbelleğe alınırsa ve bu veriler üzerinde yeterli sayıda döngü / döngü gerçekleştirirseniz, talimat sayısı ve talimatların, dalların güvendiklerine göre boruya indiği bir getirme çizgisi içinde nerede olduğu önemli olur. Ancak dram ve / veya flash tabanlı sistemlerde öncelikle işlemciyi besleme konusunda endişelenmeniz gerekir evet.
old_timer

15

Evet, bellek hizalaması hala önemlidir.

Bazı işlemciler hizalanmamış adreslerde okuma yapamaz. Bu tür bir donanım üzerinde çalışıyorsanız ve tamsayılarınızı hizalanmamış olarak saklıyorsanız, çeşitli baytları doğru yerlere yerleştirmek için bunları iki talimat ve ardından bazı talimatları izlemeniz gerekir. . Dolayısıyla, hizalanmış veriler performans açısından kritiktir.

İyi haber şu ki, aslında umurunda değil. Hemen hemen her dil için hemen hemen her derleyici, hedef sistemin hizalama gereksinimlerine uyan makine kodu üretecektir. Yalnızca verilerinizin bellek içi sunumunu doğrudan kontrol ediyorsanız düşünmeye başlamanız gerekir; bu, bir zamanlar olduğu kadar yakın bir yerde gerekli değildir. Bilmeniz ilginç bir şeydir ve oluşturduğunuz çeşitli yapılardan bellek kullanımını anlamak isteyip istemediğinizi ve işleri daha verimli hale getirmek için nasıl yeniden düzenleneceğini (dolgudan kaçınarak) bilmek kesinlikle önemlidir. Ancak bu tür bir kontrole ihtiyaç duymadığınız sürece (ve sadece sahip olmadığınız çoğu sistem için), bilmeden veya umursamayan tüm kariyeri mutlu bir şekilde geçirebilirsiniz.


1
Özellikle, ARM hizalanmamış erişimi desteklemez. Ve bu, mobil cihazların kullandığı neredeyse her şey CPU.
Jan Hudec

Ayrıca Linux'un bazı çalışma zamanı maliyetleriyle hizalanmamış erişimi taklit ettiğini, ancak Windows (CE ve Telefon) hizalanmayan erişime izin vermediğini ve denemediğini unutmayın.
Jan Hudec

2
Bu çoğunlukla doğru olsa da, bazı platformların (x86 dahil) hangi yönergelerin kullanılacağına bağlı olarak farklı hizalama gereksinimleri olduğunu unutmayın , bu da derleyicinin kendi kendine çalışması kolay değildir, bu nedenle bazen emin olmak için ped yapmanız gerekir. bazı işlemler için belirli işlemler (örneğin, birçoğu 16 baytlık hizalama gerektiren SSE talimatları) kullanılabilir. Ayrıca, sık sık birlikte kullanılan iki öğenin aynı önbellek satırında (ayrıca 16 bayt) oluşması için ek dolgu eklenmesi bazı durumlarda performans üzerinde büyük bir etkiye sahip olabilir ve otomatik değildir.
Jules

3

Evet, hala önemli ve bazı performans kritik algoritmalarında, derleyiciye güvenemezsiniz.

Sadece birkaç örnek listeleyeceğim:

  1. Gönderen bu cevap :

Normalde, mikro kod bellekten uygun 4 bayt miktarını getirecektir, ancak hizalanmamışsa, bellekten iki adet 4 bayt konum almak ve iki konumun uygun baytlarından istenen 4 bayt miktarını yeniden yapılandırmak gerekecektir.

  1. SSE talimat seti özel hizalama gerektirir. Karşılanmıyorsa, verileri hizalanmamış belleğe yüklemek ve saklamak için özel işlevler kullanmanız gerekir. Bu iki ekstra talimat anlamına gelir.

Performans açısından kritik algoritmalar üzerinde çalışmıyorsanız, sadece bellek hizalamalarını unutun. Normal programlama için gerçekten gerekli değildir.


1

Önemli olan durumlardan kaçınma eğilimindeyiz. Önemliyse önemlidir. Örneğin, günümüzde kaçınılmış gibi görünen ikili veriler işlenirken, hizalanmamış veriler gerçekleşiyordu (insanlar XML veya JSON'u çok kullanıyor).

Bir şekilde hizalanmamış bir tamsayı dizisi oluşturmayı başarırsanız, tipik bir intel işlemcide kod diziniz bu diziyi hizalanmış verilerden biraz daha yavaş çalıştıracaktır. Bir ARM işlemcisinde, derleyiciye verilerin hizalanmamış olduğunu bildirirseniz biraz daha yavaş çalışır. Derleyiciye haber vermeden hizasız veri kullanırsanız, işlemci modeline ve işletim sistemine bağlı olarak, korkunç, korkunç bir lot çok daha yavaş çalışabilir veya yanlış sonuçlar verebilir.

C ++ başvurusunu açıklama: C'de, bir yapıdaki tüm alanların artan bellek düzeninde depolanması gerekir. Bu nedenle, char / double / char alanlarınız varsa ve her şeyin hizalanmasını istiyorsanız, bir bayt char, yedi bayt kullanılmamış, sekiz bayt çift, bir bayt char, yedi bayt kullanılmamış olacaktır. C ++ yapılarında uyumluluk için aynıdır. Ancak yapılar için derleyici alanları yeniden sıralayabilir, bu nedenle bir bayt karakter, başka bir bayt karakter, altı bayt kullanılmamış, 8 bayt çift olabilir. 24 bayt yerine 16 kullanma. C yapılarında, geliştiriciler genellikle bu durumdan kaçınır ve ilk etapta alanları farklı bir düzende kullanırlardı.


1
Hizalanmamış veriler bellekte olur. Doğru şekilde paketlenmiş veri yapılarına sahip olmayan programlar, değerlerin eşitsiz bir şekilde sıralanması için bile büyük performans cezalarına maruz kalabilir. Örneğin, iş parçacıklı kodda, tek bir önbellek satırındaki iki değer, iki iş parçacığı aynı anda eriştiğinde (iş parçacığı güvenliği sorunlarını yoksayarak) büyük boru hattı duraklarına neden olur.
greyfade

Bir C ++ derleyicisi yalnızca belirli koşullar altında alanları yeniden sıralayabilir; bu kuralların farkında değilseniz büyük olasılıkla karşılanmaz. Bunun da ötesinde, aslında bu özgürlüğü kullanan herhangi bir C ++ derleyicisinin farkında değilim.
Sjoerd

1
Daha önce bir C derleyicisi yeniden sipariş alanı görmedim. Örneğin, chars / ints arasında birçok insert dolgu ve hizalama gördüm ..
PaulHK


1

Bellek hizalama ne kadar önemlidir? Hala önemli mi?

Evet. Hayır.

Gömülü sistemden, bilgisayarımızda bellek yönetimini çok daha az eleştiren yapan büyük bellek yığınlarımız var, tamamen optimizasyondayım, ama şimdi, aynı programı ile karşılaştırırsak gerçekten fark yaratabilecek bir şey mi yoksa bellek yeniden düzenlenmeden ve hizalanmadan?

Uygulamanız daha küçük bir bellek alanına sahip olacak ve düzgün hizalanmışsa daha hızlı çalışacaktır. Tipik masaüstü uygulamasında, nadir / atipik durumlar dışında önemli olmayacaktır (uygulamanız her zaman aynı performans darboğazıyla biten ve optimizasyon gerektiren). Yani, uygulama uygun şekilde hizalandıysa daha küçük ve daha hızlı olacaktır, ancak çoğu pratik durumda kullanıcıyı bir şekilde etkilememelidir.

Bellek hizalamanın başka avantajları var mı? CPU'nun hizalanmış bellekle daha iyi / daha hızlı çalıştığı bir yerde okudum, çünkü bu işlemek için daha az talimat alır (eğer birinizle ilgili bir makale / kıyaslama için bir bağınız varsa?), Bu durumda, fark gerçekten önemli mi? Bu ikisinden daha fazla avantaj var mı?

Olabilir. Kod yazarken (muhtemelen) akılda tutulması gereken bir şeydir, ancak çoğu durumda sadece önemli olmamalıdır (yani, üye değişkenlerimi bellek ayak izi ve erişim frekansına göre düzenlerim - bu da önbelleğe almayı kolaylaştırmalıdır - ama bunu yapıyorum önbellek amacıyla değil kullanım / kod okuma ve yeniden düzenleme kolaylığı).

Bazı farklılıklar olduğu için bellek hizalamanın tam olarak C ++ ile nasıl çalıştığı hakkında bir fikriniz var mı?

Alignof şeyler ortaya çıktığında bunu okudum (C ++ 11?) O zamandan beri rahatsız etmedim (çoğunlukla masaüstü uygulamaları ve arka uç sunucu geliştirme yapıyorum).

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.