Golang Memory Alignment

Ömer Burak Demirpolat
Delivery Hero Tech Hub
3 min readNov 18, 2021

Selam, bu yazıda memory alignment Go için nasıl gerçekleşiyor ve basit şekilde oluşturduğumuz struct’ları nasıl optimize ederiz anlatmaya çalışacağım.

Bir Sorun

Bir sorun yaratalım ve geliştirdiğimiz uygulamalarda aslında bu memory alignment konusu ile ne kadar sık karşılaştığımızı anlayalım.

Çok basit bir program, User adında bir struct’a sahibiz ve bu struct içerisinde A adında int32 bir field bulunuyor. Program çıktı olarak “size: 4” veriyor.

int32 veri tipinin boyutu 32 bit yani 4 byte, zaten programımızın çıktısı da bunu kanıtladı.

Bu arada Go’da empty struct’ın boyutu 0 byte şeklindedir yani struct’ın boyutunu oluşturan şey içerisinde bulunanlardır diyebiliriz.

Bu konuda detaylı bilgiyi şu yazıda bulabilirsiniz.

https://dave.cheney.net/2014/03/25/the-empty-struct

Şimdi bu User struct’ımıza iki field daha ekleyelim ve struct size’ını tekrar kontrol edelim.

Ufak bir hesaplama yapacak olursak:

  • A = int32, 4 byte
  • B = int64, 8 byte

Toplamda struct size’ımız 12 byte olmalı fakat programımız bize çıktı olarak “size: 16” verecektir. Neden?

Bunun sebebi machine word size şeklinde adlandırılan terim, peki nedir bu?

Machine word size işlemcinin tek bir cycle’da işleyebildiği bit sayısıdır. Bu bit sayısı ise günümüz mimarilerinde 32 bit ve 64 bit şeklindedir.

64 bit bir işlemci için User adlı struct ile örnekleyelim. Go için array ve struct fieldlarının bellekde ardışık şekilde barındığını biliyoruz ve bu doğrultuda struct’ımızın bellekteki durumu şu şekilde olmalı.

64 bit bir işlemci için User adındaki struct’dan yola çıkacak olursak işlemcimiz “A” field’ına erişmek için 1 adet cycle gerçekleştirecek, yukarıda bahsedildiği üzere bu cycle’da 64 bit işleyebilir ve A field’ı 32 bit boyutunda yani tek seferde bellekten okuma yapabilir.

Peki ya “B” field’ı için aynı durum geçerli mi?

İşlemci “B” field’ı için öncelikle ilk önce soldaki adrese gidecek ilk 4 byte’ı eleyip son 4 byte’ı okuyacak ve ikinci bir cycle gerçekleştirip sağdaki adrese gidecek oradan da ilk 4 byte’ı okuduktan sonra toplam 2 döngüde “B” field’ının değerine ulaşabilmiş olacak. Bu durumu 2x işlem gerektiren durumu önlemek adına compiler alignment uyguluyor. Go compiler’ının yaptığı alignment sonrasında User adlı struct’ımızın bellekteki durumu aşağıdaki gibi oluyor.

Compiler “A” field’ı için 4 byte padding uyguladı ve “B” field’ı tek bir adreslenebilir alana yerleştirildi, bu durumda “B” field’ına tek bir CPU cycle’ı ile ulaşılabilecek.

Şimdi basit bir örnek bu alignment olayı ile nasıl karşılaşıyoruz ve sadece struct field’larının sıralamasını değiştirerek nasıl bellek kullanımını azaltırız görelim.

Yine ufak bir hesap yapalım:

  • A: 4 byte
  • B: 8 byte
  • C: 4 byte
  • D: 8 byte

Aslında toplam field size’ımız 24 ama yukarıdaki verdiğim örnekteki gibi padding eklendiği için programımız “size: 32” çıktısını verecektir.

Yukaridaki struct’ın bellekteki durumu şöyle olacaktır:

Struct’ımızın field diziliminde şöyle bir değişiklik yapalım.

Yaptığım şey sadece int64 olan field’ları üst kısıma taşımak. Struct aynı verileri barındırıyor ama artık daha az bellek kullanıyor, programımız şu an çıktı olarak “size: 24” veriyor.

Peki bu durumda struct’ımızın bellekteki yeni durumuna bakalım.

Basit bir dizilim değişikliği ile 32 bit yerine 24 bit alan kullanmış olduk. Bu dizilimde padding’e gerek kalmadı.

Benim yorumlamam bu kadardır, yanlış düşündüğüm, yanlış aktardığım bir şeyler var ise beni bulun.

Twitter: demirpolatb

Kaynaklar:

--

--