Mimarilerde Veri Hizalaması: İyi, Kötü ve Çirkin


Bir bilgisayarın bellek haritası ilk bakışta oldukça düzgün ve çok fazla bayt adreslenebilir görünse de, donanım düzeyinde aynı bellek çok daha engebelidir. Bir geliştiricinin bu bağlamda karşılaşabileceği önemli bir terim, veri hizalama, donanımın sistemin rasgele erişim belleğine (RAM) nasıl eriştiğini ifade eder. Bu ve diğerleri, yazılım geliştiriciler için çeşitli çıkarımlarla birlikte sistemin RAM ve bellek veri yolu uygulamasının özellikleridir.

32 bitlik bir bellek veriyolu için, bazı veriler için en uygun erişim türü, bellek içindeki dört baytlık bir sınıra tam olarak hizalanmış dört bayt olacaktır. Hizalanmamış erişim denendiğinde – örneğin bir kelimenin yarısına hizalanmış söz konusu dört baytlık değeri okumak gibi – uygulama tanımlıdır. Bazı donanım platformlarının hizasız erişim için donanım desteği vardır, diğerleri ise işletim sisteminin (OS) yakalayabileceği ve yazılımdaki hizalanmamış bir rutine geri dönebileceği bir istisna atar. Hizasız erişime çalışırsanız, diğer platformlar genellikle bir veri yolu hatası (POSIX’te SIGBUS) verir.

Yine de hizalanmamış bellek erişimine izin verilse bile, gerçek performans etkisi nedir?

Donanım Görünümü

Temel DRAM topolojisi (Kredi: Anandtech)
Temel DRAM topolojisi (Kredi: Anandtech)

Sistem belleği ne kadar belirsiz görünse de, eşzamanlı dinamik rasgele erişimli bellek (SDRAM) fiziksel sınırlamalara çok bağlıdır. 2010 Anandtech’te SDRAM’ın nasıl çalıştığına dair harika bir başlangıç Rajinder Gill’in makalesi. Bundan temel çıkarım, SDRAM modüllerinin nasıl ele alındığıdır.

Her okuma ve yazma isteği, bir DIMM (BAn) üzerindeki hedef bankayı ve ardından hedef satırı (RAS) ve sütunu (CAS) belirten komutları seçmelidir. Her satır, bir DIMM üzerindeki her bir SDRAM yongası, DDR 3 ve DDR 4 DIMM’lerde bulunan 64 bitlik veri yoluna sekiz bit katkıda bulunan binlerce hücreden oluşur.

Bu fiziksel konfigürasyonun sonucu, RAM’e ve ara önbellek(ler)e tüm erişimin bu fiziksel olarak tanımlanmış sınırlar boyunca hizalanmasıdır. Bir bellek denetleyicisine bazı değişkenler için verileri alma görevi verildiğinde, bu verilerin tek bir okuma işleminde RAM’den alınabilmesi ve böylece CPU’nun bir kaydına okunabilmesi inanılmaz derecede yararlıdır.

Bu veriler hizalanmadığında ne olur? Önce verinin ilk bölümünü okumak, ardından son bölümü elde etmek için ikinci bir okuma yapmak ve ardından iki parçayı birleştirmek mümkündür. Doğal olarak bu, ya doğrudan bellek denetleyicisi tarafından desteklenmesi ya da işletim sistemi tarafından işlenmesi gereken bir işlemdir.

Bellek denetleyicisi bir otobüs hatası geçersiz bir adrese erişmesi istendiğinde, bir sayfalama hatasıyla karşılaşıyor veya bu desteklenmediğinde hizalanmamış erişim yapması isteniyor. x86 ve türevleri gibi platformlar, hizalanmamış bellek erişimini destekler.

İşler Patladığında

Belirtildiği gibi, x86 ve ayrıca x86_64 temelde gayet iyi, ancak sistem RAM’ine, seçtiğiniz veya rastgele kullandığınız herhangi bir hizalama ile erişirsiniz. İşlerin daha da karıştığı yer, ARM gibi diğer platformlardır. ARMv7 belgeleri hizalanmamış veri erişimi bağlamında platformun özelliklerini listelemek. Esasen, birkaç durumda donanımdan bir hizalama hatası alırsınız.

İçinde bu IBM makalesi 2005’ten itibaren, o dönemin Motorola m68k, MIPS ve PowerPC CPU’larının hizasız erişimi nasıl ele aldığı ele alındı. Burada dikkat edilmesi gereken ilginç nokta, 68020’ye kadar hizasız erişimin her zaman bir bus hatası vermesidir. MIPS CPU’ları hız adına hizasız erişimle uğraşmadı ve PowerPC, 32 bit hizalanmamış erişime izin verilen, ancak 64 bit (kayan nokta) hizasız erişime yol açan bir veri yolu hatasıyla hibrit bir yaklaşım benimsedi.

SIGBUS hizalama hatasının kopyalanması söz konusu olduğunda, bu, örneğin bir işaretçinin referansının kaldırılması yoluyla kolayca yapılabilir:


uint8_t* data = binary_blob;
uint32_t val = *((uint32_t*) data);

Burada binary_blob yalnızca 32 bit tam sayıların değil, değişken boyutlu değerlerin bir koleksiyonu olduğu varsayılır.

Bu kod herhangi bir x86 platformunda, Raspberry Pi gibi ARM tabanlı bir platformda düzgün çalışsa da, bu şekilde referans kaldırmanın size bir SIGBUS hatası ve çok ölü bir süreç getirmesi garanti edilir. Sorun şu ki, erişim isteğinde bulunduğunuzda uint8_t 32 bitlik bir tamsayı olarak işaretçi, bunun doğru bir şekilde hizalanmış olma şansı uint32_t esasen sıfırdır.

Peki bu durumda ne yapmalı?

Uyumlu Kalmak

Hizalanmış bellek erişimini kullanma argümanları çoktur. En önemlilerinden bazıları atomiklik ve taşınabilirliktir. Atomisite, tek bir okuma veya yazma işleminde gerçekleştirilebilen bir tamsayı okuma veya yazma anlamına gelir. Hizalanmamış erişim durumunda, bu atomiklik artık geçerli değildir çünkü sınırların ötesinde okumak zorundadır. Bazı kodlar bu tür atomik okuma ve yazma işlemlerine dayanabilir, bu da hizasız erişim dikkate alınmadığında ilginç ve düzensiz hatalara ve çökmelere yol açabilir.

Bununla birlikte, odadaki en belirgin fil, taşınabilirliktir. Önceki bölümde gördüğümüz gibi, bir platformda harika çalışan, ancak başka bir platformda acınası bir şekilde ölecek kod yazmak çok kolaydır. Bununla birlikte, tamamen taşınabilir olacak bir kod yazmanın bir yolu vardır; bu, aslında C belirtiminde, hizalama sorunlarıyla karşılaşmadan verileri kopyalamanın tek gerçek yolu olarak tanımlanmıştır: memcpy.

Önceki kod parçasını kullanarak yeniden yazacak olsaydık memcpyaşağıdaki kodu elde ederiz:


#include <cstring>
uint8_t* data = binary_blob; 
uint32_t val;
memcpy(&val, data, 4); 

Bu kod ile tamamen taşınabilir memcpy uyum sorunlarını ele alan uygulama. Örneğin bir Raspberry Pi sisteminde bunun gibi bir kod çalıştırırsak, hiçbir SIGBUS hatası üretilmez ve süreç başka bir CPU döngüsü görmek için devam eder.

Veri yapıları, struct C’de, ilgili veri değerlerinin gruplarıdır. Bunlar arka arkaya RAM’e yerleştirileceğinden, dolgu uygulanmadığı sürece bu açıkça hizalama sorunları yaratacaktır. Derleyiciler, gerektiğinde, bir yapının her bir veri üyesinin bellekte hizalanmasını sağlayan bu tür dolguları varsayılan olarak ekler. Açıkçası bu, bir miktar hafızayı ‘harcatır’ ve yapının boyutunu arttırır, ancak bir veri üyesinin her erişiminin tamamen hizalı olarak gerçekleşmesini sağlar.

MCU’larda ve çevresel donanım aygıtlarında bellek eşlemeli G/Ç gibi yapıların kullanıldığı yaygın durumlar için, bunlar genellikle ilk veri üyesi olduğunda her zaman hizalanan 32 bit veya 64 bit kayıtlar kullandığından bu genellikle bir sorun değildir. dır-dir. Yapı dolgusunu manuel olarak ayarlamak, performans ve boyut nedenleriyle derleyici araç zincirleriyle genellikle mümkündür, ancak yalnızca çok dikkatli yapılmalıdır.

Performans Etkisi

Ancak, veri hizalamasını göz ardı etmenin ve yalnızca donanımın veya işletim sisteminin komplikasyonları örtbas etmesine izin vermenin performansa etkisi nedir? Sistem RAM’inin fiziksel uygulamasının araştırılmasında gördüğümüz gibi, ek okuma veya yazma döngüleri pahasına hizasız erişim mümkündür. Açıkçası, bu, bu tür döngülerin sayısını en azından ikiye katlayacaktır. Bu, bankalar arasında gerçekleşirse, performans etkisi büyük olabilir.

Teke karşı çifte karşı dört bayt erişim (Kredi: Jonathan Rentzsch, IBM)
Teke karşı çifte karşı dört bayt erişim (Kredi: Jonathan Rentzsch, IBM)

Daha önce atıfta bulunulan 2005 IBM makalesi Johnathan Rentzsch tarafından, tek, çift, dört ve sekiz baytlık erişim kalıpları kullanılarak bir dizi kıyaslama sonucu sağlanmıştır. Oldukça yavaş bir 800 MHz PowerBook G5 üzerinde çalışmasına rağmen, hizasız erişimin etkisi oldukça dikkat çekiciydi ve iki baytlık hizalanmamış erişim, hizalı erişimden %27 daha yavaştı. Dört baytlık hizalanmamış erişim için bu, hizalanmış iki baytlık erişimden daha yavaştı ve daha büyük veri boyutlarına geçişi alakasız hale getirdi.

Sekiz baytlık hizalı erişime geçerken, bu, dört baytlık hizalı erişimden %10 daha hızlıydı. Bununla birlikte, hizalanmamış sekiz bayt erişim, PowerPC G4’ün sekiz bayt hizalanmamış erişim için donanım desteğine sahip olmaması ve bunun yerine gerekli birleştirme işlemlerini gerçekleştirmesi nedeniyle, hizalamaya göre 4,6 kat daha yavaş, tüm arabellek için şaşırtıcı bir 1,8 saniye sürdü.

Performans etkisi, yalnızca standart CPU ALU işlemleriyle değil, aynı zamanda tarafından ayrıntılı olarak açıklandığı üzere SIMD (vektör) uzantılarıyla da ilgilidir. Mesa et al. (2007). Ek olarak, x264 codec bileşeninin geliştirilmesi sırasında, önbellek hizalaması (hizalanmış 16 bayt aktarım) x264’te en sık kullanılan işlevlerden birinde %69 daha hızlıydı. Buradaki anlam, veri hizalamanın sistem RAM’inin çok ötesine geçtiğidir, ancak aynı zamanda bir bilgisayar sisteminin önbellekleri ve diğer öğeleri için de geçerlidir.

Bunun çoğu, (ortak) işlemlerin iki katına çıkarılmasına ve sonuçta genel performansa isabet etmesine bağlıdır.

Sarmak

Bazı yönlerden x86 mimarisi, hizalanmamış bellek erişimi gibi gerçekliğin çirkin kısımlarından sizi nasıl koruduğu konusunda oldukça rahattır, ancak gerçekliğin, hiç beklemediğiniz bir anda size gizlice yaklaşmanın bir yolu vardır. Birkaç ay önce böyle bir olay benim için bazı profil oluşturma ve optimizasyonlar yaparken gerçekleşti. uzaktan prosedür çağrı kitaplığı.

Valgrind’deki Cachegrind profil oluşturma aracından sonra bana göstermişti dahililerde aşırı miktarda hizalanmamış kopyalama devam ederken, zorluk yalnızca kitaplığın sıfır kopya sürümünü uygulamak değil, aynı zamanda ikili verilerin yerinde ayrıştırılmasını da sağlamaktı. Bu, (paketlenmiş) ikili verilerin referansının kaldırılması üzerine hizalanmamış bellek erişimi ile yukarıda belirtilen bazı sonuçlara yol açtı.

Sorun, yukarıda belirtilenler kullanılarak kolayca çözülmüş olsa da memcpytabanlı çözüm, aynı kodun x86_64 sistemlerinde sorunsuz çalıştığı ARM tabanlı sistemlerdeki SIGBUS hatalarına büyüleyici bir bakış sağladı. Performans etkisine gelince? RPC kitaplığındaki değişikliklerden önceki ve sonraki kıyaslamalar, kısmen uyumlu erişime geçişten kaynaklanabilecek performansta kayda değer bir artış gösterdi.

Etrafta, hizasız erişimden kaynaklanan performansın bugün endişelenecek kadar önemli olmadığı konusunda ısrar eden insanlar olsa da, taşınabilirlik ve atomsallık üzerindeki gerçek etki, herkesin duraklamasına neden olmalıdır. Bunun ötesinde, bellek erişim modellerinin nasıl olduğu ve nelerin iyileştirilebileceği veya optimize edilebileceği konusunda bir fikir edinmek için kodu bir profil oluşturucu aracılığıyla çalıştırmaya kesinlikle değer.


Kaynak : https://hackaday.com/2022/05/10/data-alignment-across-architectures-the-good-the-bad-and-the-ugly/

Yorum yapın