Thread overview
Sınıf&Yapılarda Hafıza Yönetimi'ne Giriş
Jan 14, 2022
Salih Dincer
Jan 14, 2022
Ali Çehreli
Jan 15, 2022
Ali Çehreli
Jan 15, 2022
Salih Dincer
Jan 15, 2022
Ferhat Kurtulmuş
Jan 15, 2022
Salih Dincer
Jan 15, 2022
Ali Çehreli
January 14, 2022

BURDA başlayan tartışmayan burdan devam...
On Tuesday, 11 January 2022 at 08:33:20 UTC, Ferhat Kurtulmuş wrote:

>

Salih, tam olarak ne elde etmek istediğini anlamadım. Ama aşağıdaki örnek üzerinden bir şeyler anlatmak istedim. Hatalı olduğum bir yer varsa muhtemelern Ali abimiz düzeltecektir.

Sınıflar d'de referans tipleridir ve aslında arkasında (T*) gibi pointerlar çalışmaktadır. Bu yüzden örnekte A'nı elemanlarını azaltalım veya yeni elemanlar ekleyelim, her zaman aynı A.sizeof değerinini (size_t tipinden byte değeri) alırız. Struct'larda böyle değil. Ha, structlarda bir de alignment devreye girmekte, Ali abinin kitabında alignment konusuna yer verilmiş olmalı. Konumuza dönelim. A sınıfı için A.sizeof bir pointer'ın kapladığı alana karşılık geliyor. D'de sınıf instance'ının byte olarak hafızada kapladığı alanın değerini ise __traits(classInstanceSize, A) ile öğreniyoruz. İşte buradaki değer, yeni sınıf üyeleri ekledikçe veya sınıf üyelerini çıkardıkça değişir.

Gelelim class üye metodlarına. Bunları ekleyip çıkarmak classInstanceSize'ı etkilemez. Çünkü onlar sınıf tarafından aslında kapsanan fonksiyonlar değiller. Sınıf üye metodları, virtual table'da[1] o sınıfın instance'ı ile ilişkilendirilen fonksiyon pointerlarından ibaretler ve class instance boyutuna katkıları yoktur. Ancaaaaak, aşağıdaki örnekte A sınıfının bir de var4 adında bir fonksiyon pointer üyesi bulunmakta. Bunun gibi üye fonksiyon pointerları class instance boyutunu etkilerler. Çünkü o bir pointer ve bir pointerın kaplayacağı hafıza kadar yer kaplar (bende 8 byte). Sınıf üyelerini benim örneğimde yoruma alarak veya kendin yeni üyeler ekleyerek nasıl bir çıktı alıyorsun deneyip görebilirsin.

Selamlar.

1: https://dlang.org/library/object/type_info__class.vtbl.html

import std;

class B {
}

class A {
    int var1;
    ubyte var2;
    B var3;

    void function() var4;

    void f1(){}
}

void main()
{
    pragma(msg, A.sizeof);

    writeln(__traits(classInstanceSize, A));
}

Ferhat Hocam, geç cevap yazdığım için kusura bakmayın; ailecek korona olduk da (!) şu an toparladık şükür...:)

Konu içinde tekrar bir konu oluşturduğum için telafi amaçlı buradan devam edelim ve hızlıca asıl konumuza dönelim...

Aşağıdaki kodu siz de denemek isterseniz her sınıf sonuna düştüğüm notlardaki değerleri bulabilirsiniz. Hatta kod, yapılarda da çalışıyor. Tek yapmanız gereken, main()'deki ilk satırın remark'ını açmak. Sınıflara geçmeden önce yapılara şöyle pratik şekilde dokunalım:

T.sizeof() ile edindiğimiz bilgiler sıradan çünkü her int, bize 4 bayta maloluyor. Öyle ya int, 32 bit bir değer ve short'da, bu yarı yarıya düşecekken long'da, ikiye katlanacaktı. a4.Node için de anlaşılmayacak bir şey yok çünkü kendini de işaret eden bir işaretçi kendi gibi 8 bayt yer kaplıyor, değil mi?

Class'lara geldiğimizde ise extern(C) ayrımı var. Ali hocam düzeltirse sevinirim; anladığım kısaca şu:

D'de sınıflar için ekstra bir şeyler var ve eğer kodunuz müsaitse (sanırım sınıf, D olanaklarını içermemeli) daha az yer kaplaması için tercih edilebiliyor.

Aslında sınıflar için de anlaşılmayacak bir şey yokmuş. Açık söylemek gerekirse şimdi farkettim. Çünkü yapıya göre 16 bayt ekstrası var. O da Object'den türediği için. Neticede kod yazarken görmediğimiz üyeleri var. Yani extern(c) değilse formül şu 16 byte + (üyeAdeti * türBoyutu)

a4.Node için son bir not: Yapılar için anlaşılmayacak bir şey yok dedim ama neden boyutu 24/32LU kalıyor? Sanırım henüz new ile kurulmamış bir sınıf işaretçisi olduğu için, olabilir mi?

/* extern / class / struct */

//extern (C++)
class Point {
  int x, y;
} /* 16LU / 24LU / 8 byte */


//extern (C++)
class Line {
  int x, y, z;
} /* 20LU / 28LU / 12 byte */


//extern (C++)
class Rectangle {
  Point XY, EB;
} /* 24LU / 32LU/ 16 byte */


//extern (C++)
class Node {
  Point item;
  Node * next;
} /* 24LU / 32LU / 16 byte */

import std.stdio;

alias a1 = Point;
alias a2 = Line;
alias a3 = Rectangle;
alias a4 = Node;
void main()
{
//                            a1 test;/*
  __traits(classInstanceSize, a1).writeln(" LU");
              auto test = new a1;//*/
         write(typeof(test).stringof);
  writeln(": ",typeof(test).sizeof, " bytes");
}

On Tuesday, 11 January 2022 at 08:33:20 UTC, Ferhat Kurtulmuş wrote:

>

Sınıflar d'de referans tipleridir ve aslında arkasında (T*) gibi pointerlar çalışmaktadır. Bu yüzden örnekte A'nı elemanlarını azaltalım veya yeni elemanlar ekleyelim, her zaman aynı A.sizeof değerinini (size_t tipinden byte değeri) alırız. Struct'larda böyle değil. Ha, structlarda bir de alignment devreye girmekte, Ali abinin kitabında alignment konusuna yer verilmiş olmalı.

Hocam böyle sonuç almanızın sebebi henüz sınıfı new ile kurmadan test etmeniz olabilir mi? Ayrıca Ali hocanın yapılardaki yaslama mevzusunu araştıracağım. Acaba bellek yönetimi konusunda çok faydası var mı?

Ali hocam, yoksa bu analizler ile çok kastırmayıp std.bitmanip ile inanılmaz küçük boyutlarda D yapılarını oluşturup keyfimize bakabilir miyiz?

Herkese teşekkürler...

January 14, 2022
On 1/14/22 01:21, Salih Dincer wrote:

> kendini de işaret eden bir işaretçi kendi gibi 8 bayt
> yer kaplıyor, değil mi?

Doğru.

> D'de sınıflar için ekstra bir şeyler var

İki adet gösterge:

- vtblptr (virtual function table pointer)
- Monitor

> ve eğer kodunuz müsaitse
> (sanırım sınıf, D olanaklarını içermemeli) daha az yer kaplaması için
> tercih edilebiliyor.

extern(C++) olan sınıflarda Java'dan (ve belki de C#'tan da?) alınmış olan 'monitor' göstergesi bulunmaz. Bu gösterge, her sınıf nesnesinin 'synchronized' anahtar sözcüğüyle bir kilit olarak kullanılabilmesini sağlıyor.

Monitor D'nin hemen hemen hiç kullanılmayan bir hatası.

İlgili olarak, sınıflarda bir de otomatik olarak Object'ten türemenin getirdiği rahatsızlıklar var. (Örneğin, toString const değildir.) Hem monitor'ün hem de bu rahatsızlıkların önüne geçmek için D'ye ProtoObject diye bir üst sınıf geliyor. (Konu şu anda İngilizce forumda devam ediyor.) İstediğimizde sınıfları ProtoObject'ten türetebileceğiz.

> extern(c) değilse formül şu 16 byte + (üyeAdeti * türBoyutu)

(extern(C++) demek istedin.) O hesap tam doğru değil çünkü hizalama nedeniyle aralara doldurma baytları (padding) gelebilir. (Ben alignment'a hizalama demişim ama yaslamayı da beğendim.)

> **a4.Node için son bir not:** Yapılar için anlaşılmayacak bir şey yok
> dedim ama neden boyutu 24/32LU kalıyor?

Hem Point hem de Node extern(C++) olduklarında boyutu 16 oluyor.

> Sanırım henüz new ile kurulmamış
> bir sınıf işaretçisi olduğu için, olabilir mi?

sizeof bütünüyle derleme zamanında işler. İlginç bir ayrıntı olarak, ifadeyi hiç işletmez bile. Yalnızca, "işletilseydi ne büyüklükte bir sonuç oluştururdu" sorusunun yanıtını verir.

> Hocam böyle sonuç almanızın sebebi henüz sınıfı new ile kurmadan test
> etmeniz olabilir mi?

Hayır.

> Ayrıca Ali hocanın yapılardaki yaslama mevzusunu
> araştıracağım. Acaba bellek yönetimi konusunda çok faydası var mı?

Bu konu, ancak kendi ayırdığımız belleğe nesne yerleştirirken önemlidir. Yani, hemen hemen hiçbir zaman. :)

> Ali hocam, yoksa bu analizler ile çok kastırmayıp std.bitmanip ile
> inanılmaz küçük boyutlarda D yapılarını oluşturup keyfimize bakabilir
> miyiz?

Gerektiğinde tabii ama hemen hemen hiçbir zaman gerekmez... diyordum ama ben bitfields'i, belleğe sığabilmek için kullanmak zorunda kalmıştım. Şöyle bir yapı fazla büyük oluyordu:

struct MessageIndex {
  ushort fileId;
  uint blockId;
  ulong id;
  // vs.
}

Bazı üyeleri bitfields ile birleştirerek belleğe sığabilmiştim.

İşin güzel tarafı, baştan rahat rahat hepsini size_t yapmıştım. Sonra belleğe sığamayınca MessageIndex'e üye gibi işlevler ekledim ve programın geri kalanının haberi bile olmadı:

struct MessageIndex {
  bitfields!(/* her ne ise *) fileAndBlockIds;
  // vs.

  // Programın geri kalanı farkında olmadan bunu
  // kullanmaya başladı:
  ushort fileId() const {
    return fileAndBlockIds.fileId;
  }
}

Ali

January 15, 2022
On 1/14/22 11:55, Ali Çehreli wrote:

> hizalama nedeniyle aralara doldurma baytları (padding) gelebilir. (Ben alignment'a hizalama demişim ama yaslamayı da
> beğendim.)

Aşağıdaki bölümde nesneYerleşiminiYazdır() diye bir işlev var:

  http://ddili.org/ders/d/bellek_yonetimi.html

Aynı işlevi bir kere daha yazdım:

import std.traits;
import std.stdio;
import std.format;
import std.range;
import std.algorithm;

struct A {
  char c;
  double d;
  short s;
  int i;
  byte b;
}

struct B {
  short s;
  A a;
  int i;
}

void main() {
  printLayout!B;
}

void printLayout(T)() {
  enum size = T.sizeof;
  enum name = T.stringof;
  writefln!"\n===== %s byte%s of %s ====="(size, pluralSuffix(size), name);

  size_t lastUsed;
  size_t totalPadding;

  printLayoutImpl!T(name, 0, lastUsed, totalPadding);
  printPadding(lastUsed, size);

  totalPadding += (size - lastUsed);
  const percentage = (double(size) - totalPadding) / size * 100;
  writefln!"Utilization: %.1f%% (%s byte%s padding)"(percentage, totalPadding, pluralSuffix(totalPadding));
}

void printLayoutImpl(T)(string prefix, size_t base, ref size_t lastUsed, ref size_t totalPadding) {
  alias types = Fields!T;
  alias names = FieldNameTuple!T;
  static assert(types.length == names.length);

  static foreach (fieldId; 0 .. names.length) {{
    alias type = types[fieldId];
    const size = type.sizeof;
    const name = names[fieldId];
    const offset = base + mixin(format!"T.%s.offsetof"(name));

    static if (is (type == struct)) {
      printLayoutImpl!type(format!"%s.%s"(prefix, name), offset, lastUsed, totalPadding);

    } else {
      const padding = offset - lastUsed;
      printPadding(lastUsed, offset);

      foreach (byteId; 0 .. size) {
        writef!"%4s (O)"(offset + byteId);

        if (byteId == 0) {
          writef!" %s.%s (%s, %s-byte alignment)"(prefix, name, type.stringof, type.alignof);
          if (fieldId == 0) {
            writef!" (%s, %s-byte alignment)"(T.stringof, T.alignof);
          }
        }
        writeln();
      }

      lastUsed += size + padding;
      totalPadding += padding;
    }
  }}
}

string pluralSuffix(T)(T value) {
  return (value == 1) ? "" : "s";
}

void printPadding(size_t from, size_t to) {
  iota(from, to).each!(i =>writefln!"%4s  |"(i));
}

Örnekteki B yapısı için aşağıdaki gibi bir çıktı veriyor. Yani, toplam 48 bayt olan B'nin yalnızca 22 baytı veri için kullanılıyor. Geri kalan 26 bayt doldurma baytı!

Kullanılan baytları (0) olarak, doldurma baytlarını da dar çizgiyle gösterdim.

Üyelerin isimlerini yalnızca başladıkları bayta yazdım. Örneğin, short türündeki B.s sıfırıncı bayttan başlıyor ve bir sonraki baytı da kapsıyor.

Hizalama değerlerini de veriyorum. B.a.c'de olduğu gibi hem bir yapı hem de bir üye başladığında iki hizalama değeri var.

===== 48 bytes of B =====
   0 (O) B.s (short, 2-byte alignment) (B, 8-byte alignment)
   1 (O)
   2  |
   3  |
   4  |
   5  |
   6  |
   7  |
   8 (O) B.a.c (char, 1-byte alignment) (A, 8-byte alignment)
   9  |
  10  |
  11  |
  12  |
  13  |
  14  |
  15  |
  16 (O) B.a.d (double, 8-byte alignment)
  17 (O)
  18 (O)
  19 (O)
  20 (O)
  21 (O)
  22 (O)
  23 (O)
  24 (O) B.a.s (short, 2-byte alignment)
  25 (O)
  26  |
  27  |
  28 (O) B.a.i (int, 4-byte alignment)
  29 (O)
  30 (O)
  31 (O)
  32 (O) B.a.b (byte, 1-byte alignment)
  33  |
  34  |
  35  |
  36  |
  37  |
  38  |
  39  |
  40 (O) B.i (int, 4-byte alignment)
  41 (O)
  42 (O)
  43 (O)
  44  |
  45  |
  46  |
  47  |
Utilization: 45.8% (26 bytes padding)

B yapısının boyunu olabildiğince küçültmek için dört tane align(1) ekleyeceğiz:

align(1)            // <-- A için
struct A {
align(1):           // <-- A'nın üyeleri için
  char c;
  double d;
  short s;
  int i;
  byte b;
}

align(1)            // <-- B için
struct B {
align(1):           // <-- B'nin üyeleri için
  short s;
  A a;
  int i;
}

Öyle yapınca hiç doldurma karakteri kalmıyor ve B'nin büyüklüğü 22'ye iniyor:

===== 22 bytes of B =====
   0 (O) B.s (short, 2-byte alignment) (B, 1-byte alignment)
   1 (O)
   2 (O) B.a.c (char, 1-byte alignment) (A, 1-byte alignment)
   3 (O) B.a.d (double, 8-byte alignment)
   4 (O)
   5 (O)
   6 (O)
   7 (O)
   8 (O)
   9 (O)
  10 (O)
  11 (O) B.a.s (short, 2-byte alignment)
  12 (O)
  13 (O) B.a.i (int, 4-byte alignment)
  14 (O)
  15 (O)
  16 (O)
  17 (O) B.a.b (byte, 1-byte alignment)
  18 (O) B.i (int, 4-byte alignment)
  19 (O)
  20 (O)
  21 (O)
Utilization: 100.0% (0 bytes padding)

Bir söylentiye göre, ve aslında galiba çoğunlukla eski donanımlarda, üyelere erişim daha yavaş olacak çünkü türleri istedikleri hizalamalı adreslere yerleştirmemiş olduk. Örneğin, 'double' türündeki B.a.d, 3 gibi tek sayılı bir yere rastlamış oldu.

Ali

January 15, 2022

On Saturday, 15 January 2022 at 08:02:43 UTC, Ali Çehreli wrote:

>

http://ddili.org/ders/d/bellek_yonetimi.html

Aynı işlevi bir kere daha yazdım...

Hocam, harika olmuş! Görsellik %100 !!!

Ancak deneme fırsatı buldum verimli sonuçlar elde ediliyor...

On Saturday, 15 January 2022 at 08:02:43 UTC, Ali Çehreli wrote:

>

Utilization: 45.8% (26 bytes padding)

B yapısının boyunu olabildiğince küçültmek için dört tane align(1) ekleyeceğiz:

align(1) // <-- A için
struct A {
align(1): // <-- A'nın üyeleri için
char c;
double d;
short s;
int i;
byte b;
}

align(1) // <-- B için
struct B {
align(1): // <-- B'nin üyeleri için
short s;
A a;
int i;
}

Öyle yapınca hiç doldurma karakteri kalmıyor ve B'nin büyüklüğü 22'ye iniyor:

Hocam ben de sadece yerlerini şu şekilde büyükten küçüğe doğru değiştirdim 23 bayta düşürdüm:

struct A {
  double d;
  int i;
  short s;
  byte b;
  char c;
}

struct B {
  A a;
  int i;
  short s;
}

Ama aligment konusunu çok iyi anladım, teşekkürler. Öyle söylentilere de kanmıyoruz biz. Teknoloji/imkan varsa kullanacağız :)

Saygılar...

January 15, 2022

On Saturday, 15 January 2022 at 10:31:13 UTC, Salih Dincer wrote:

>

On Saturday, 15 January 2022 at 08:02:43 UTC, Ali Çehreli wrote:

>

[...]

Hocam, harika olmuş! Görsellik %100 !!!

Ancak deneme fırsatı buldum verimli sonuçlar elde ediliyor...

On Saturday, 15 January 2022 at 08:02:43 UTC, Ali Çehreli wrote:

>

[...]
Hocam ben de sadece yerlerini şu şekilde büyükten küçüğe doğru değiştirdim 23 bayta düşürdüm:

struct A {
  double d;
  int i;
  short s;
  byte b;
  char c;
}

struct B {
  A a;
  int i;
  short s;
}

Ama aligment konusunu çok iyi anladım, teşekkürler. Öyle söylentilere de kanmıyoruz biz. Teknoloji/imkan varsa kullanacağız :)

Saygılar...

Çok geçmiş olsun. Atlattığınıza ve herkesin iyi olduğuna sevindim. Kısıtlı donanımlarda çalışacak birşeyler yazmam gerekirse hizalamayı kullanmak zorunda kalırım herhalde. Ali abinin hizalama ile ilgili yazdıklarına bakacağım.

January 15, 2022

On Saturday, 15 January 2022 at 13:01:17 UTC, Ferhat Kurtulmuş wrote:

>

On Saturday, 15 January 2022 at 10:31:13 UTC, Salih Dincer wrote:

>

On Saturday, 15 January 2022 at 08:02:43 UTC, Ali Çehreli wrote:

>

[...]

Hocam, harika olmuş! Görsellik %100 !!!

Ancak deneme fırsatı buldum verimli sonuçlar elde ediliyor...

On Saturday, 15 January 2022 at 08:02:43 UTC, Ali Çehreli wrote:

>

[...]
Hocam ben de sadece yerlerini şu şekilde büyükten küçüğe doğru değiştirdim 23 bayta düşürdüm:

struct A {
  double d;
  int i;
  short s;
  byte b;
  char c;
}

struct B {
  A a;
  int i;
  short s;
}

Ama aligment konusunu çok iyi anladım, teşekkürler. Öyle söylentilere de kanmıyoruz biz. Teknoloji/imkan varsa kullanacağız :)

Saygılar...

Çok geçmiş olsun. Atlattığınıza ve herkesin iyi olduğuna sevindim. Kısıtlı donanımlarda çalışacak birşeyler yazmam gerekirse hizalamayı kullanmak zorunda kalırım herhalde. Ali abinin hizalama ile ilgili yazdıklarına bakacağım.

Teşekkürler hocam, mutlaka bakın çünkü (neredeyse başım döndü) harika özellikler var! Korona günlerinde Ali hocayla özelden konuşuyorduk; çeşitli dillerin dibini bulduk diyebilirim :)

Bu arada, şöyle bir örnekte de kullandım ve birliklerde bile çalıştığını gördüm:

union DATA {
  size_t data;
  ubyte[4] dX;
}

struct Foo {
  ubyte id;
  DATA _data;

  alias _data this;

  this(char id) {
    this.id = cast(ubyte)id;
    "init".writeln;
  }
  ~this(){
    "free".writeln;
  }
}

void main() {

  auto foo = Foo('a');
       foo.data = 0x0000FFFF;

  foreach(data; foo.dX) {
    data.writeln;
  }
  printLayout!Foo;
}

İçeriğinde statik şeyler olmasa (bir appender!string kurup formattedWrite!"" ile uğraştım ama olmadı) struct'lara standart toString() olarak kullanırdım. Ali hocam, hatta dilin standart kütüphanesine böyle bir görüntüleyici koymalarını isterdim.

Teşekkürler...

January 15, 2022
On 1/15/22 02:31, Salih Dincer wrote:

> yerlerini şu şekilde büyükten küçüğe doğru
> değiştirdim 23 bayta düşürdüm:
>
> ```d
> struct A {
>    double d;
>    int i;
>    short s;
>    byte b;
>    char c;
> }

Evet, eskilerin çok uyguladığı yöntemdir. Sorunu, ilgili kavramları uzak yerlere yerleştirttiğinden kod daha az hoşa gidebilir. :)

Ali