Thread overview
Bir malloc() denemesi ve GC olayına giriş!
Aug 06, 2013
Salih Dinçer
Aug 06, 2013
Salih Dinçer
Aug 06, 2013
Salih Dinçer
Aug 06, 2013
agora
Aug 06, 2013
Salih Dinçer
Aug 06, 2013
Salih Dinçer
Aug 07, 2013
Salih Dinçer
Aug 07, 2013
Salih Dinçer
August 06, 2013

Merhaba,

Şurada (http://ddili.org/forum/thread/1209) başlayan tartışma 'vizyonumu genişletti'ğini söyleyerek devam edersek; D Programlama Dili'nin slice(dilim) olanakları, henüz tanışmadıysanız size de çok şey katabilir...

Hali hazırdaki Ali hocanın dersiyle başlayın: http://ddili.org/ders/d/dilimler.html

Karmaşık gibi görünen (aslında değil, her şey tereyağından kıl alır gibi leziz!) kodlara geçmeden evvel, lütfen iki çift söz etmeme izin verin. Sanki vermeseniz yazmayacağım...:)

Alıntı ("BELLEKBOL SAHASI"):

>

Dış mekanda, senelerdir kullanılmayan bir basketbol sahası düşünün. Size orayı, yarım gün içinde süpürme görevi verdiler. Çünkü öğleden sonra seyirciler gelmeye başlayacak ve burada önemli bir maç oynanacak. N'aparsınız?

Elbette bir köşeden başlayıp diğer çapraz köşeye kadar süpürme ahmaklığına girişmezsiniz. Çünkü öyle yaparsanız zamanla süpürdüğünüz çer çöp artacak ve logaritmik olarak yavaşlayacaksınızdır. Neticede böyle bir şeye kalkışırsanız, size verilen sürenin ortalarında hızlanmak isteyecek ve tozu dumana katmaya başlayacaksınız ve patron ise sizi hemen kovacak...:)

(Patron kontrole gelir...)

  • Bu kadar süre n'aptın? Her yer bıraktığım gibi duruyor!
  • Kovuldun...:)

Belki başınıza böyle bir şey gelmemiştir. Ama kullandığımız bilgisayarlardaki bellek birimlerini temizlemek arasında neredeyse hiç bir fark olmadığını söyleyebilirim. Tabi gerçek hayatta rüzgar faktörü var ki yarım günlük bir iş, bütün gün sürebilir...:)

(Nereden mi biliyorum!)

Askerde, devrem ile böyle bir maceraya gönüllü (aslında zorunlu!) olarak girişmiştik de ondan. Basketbol "sahası nedir ki canım!" demeyin. Tek kişi olmadığınız halde bütün çöpleri yığmak ve oradan uzaklaştırmak hiç de kolay bir şey değil. Peki doğrusu nedir? Tabi ki başladığınız yerdeki çöpü bir miktar süpürdükten sonra diğer arkadaşınıza (onun ismi GC olsun) teslim etmek. O da çöp yığınını arabasına yükleyip peşinizden gelecek ve süpürülen diğer yığınları toplamaya devam edecek.

(Kodlara az kaldı...)

Şimdi, gerçek hayattan bu basit örneği niye verdiğim ortada. Bir basketbol sahası da tıpkı bilgisayarınızdaki belleğin bir bölümü gibidir; her santimetre karesine bir bayt veri işlendiğini hayal edin. Ancak günümüz belleklerini yeryüzüne yaydığımızda milyonlarca basketbol sahası olduğunu ve bütün bunları düzenli aralıklar ile temizleme göreviniz olduğunu düşünürseniz olay hiç de basit değil. Bunu ben de yeni fark ettim; gerçek hayatta tecrübe etmeme rağmen...:)

Her şey bir Memory Manager sınıfı yazmak ile başladı ki Talha Zekeriya Durmuş buna önayak olmuştur. Elbette aşağıdaki kodlar sadece bir temel ve geleneksel malloc() işlevine benzese de dilimler kullanıldığı için çok farklı. Ayrıca GC olaylarına girmeye başlasak da sanırım hala çok uzağız. Çünkü hiç bir şey göründüğü gibi basit değil...

(Nereye! Hemen kormayın canım...:))

Basitten başlayalım! Dilimleri bilenler pekala 2 adet işaretçi ile belleğin (dizinin) bir bölümünü işaret ettiğini hatırlayacaktır. Geleneksel yöntemleri uygulasaydık; istenen boyutta bellek birimine, başka bir veri yazılmayacağının garantisi ile birlikte 1 adet işaretçi gönderecektik. Biz ise dilim gönderiyoruz ama bu öyle tek başına gezinen sarhoş bir dilim değil...:)

>   struct Düğüm {
>     size_t konum;
>     char[] dilim;
>     Düğüm* önceki,
>            sonraki;
>     bool boş_Mu;
>   }
> ```

> Dilimimiz, şimdilik sadece char değişkenlerini göstereceğini varsayalım. Bu durumda yukarıdaki gibi bir kaç arkadaşı ile birlikte bir yapı oluşturabiliriz. Bilgiler Türkçe olduğu için, dilimizi kullananlar bu arkadaşların görevini hemen anlayacaklar. Aslında bu yapı bir bağlı listenin temeli. Bunu önceki ile sonraki işaretçilerden anlamışsınızdır.
>
> Özetle, dilimi Düğüm ismindeki bir taşıyıcı vasıtasıyla hayatta tutacağız ve yanındaki arkadaşları ile bütün dilimleri birbirlerine bağlayacağız. Eğer birinin kullanımı biterse ya onu boş olarak (boş_mu = true) işaretleyeceğiz, ya da komşusu da boş ise bunları birleştirmeyi (süpürmeyi) tercih edeceğiz. Ama maceramız bitmeyecek; bir de süpürdüklerimizi toplamamız gerekecek ki bu sanırım, ilk makaleyi epeyce aşan bir mevzu...
>
> Lütfen aşağıdaki kodu, yukarıdaki yapı ile birlikte derleyiniz. Henüz geliştirme devam ettiği için ileride daha farklı özelliklere ile donatılacaktır. Şimdiden koda hakim olmanızda fayda var, çünkü işler karışacak...:)
>

-- 
[ Bu gönderi, <http://ddili.org/forum>'dan dönüştürülmüştür. ]
August 06, 2013
import std.conv, std.string, std.stdio;

void main() {

 with( new MEM(16) ) {
   memory = "Bu"           /* 1 */
            "Bir"          /* 2 */
            "Veri"         /* 3 */
            "Grubu"        /* 4 */
            "..".dup; // 15 ve 16. baytlar...

   char[]*[] dilimler;

   foreach(boyut; 2..6) {
     dilimler ~= malloc(boyut);
   }

   "Dilimlerim:".writeln;
   foreach(ref dilim; dilimler) {
     writefln("@%s", &dilim);
   }
   writeln;

   void düğümleriGöster() {
     foreach(ref düğüm; dizin.values) {
       writefln("@%s: %s", düğüm, *düğüm);
     }
     writeln();
   }

   toString.writeln();

   //foreach(i; [ 1, 2, 3, 4 ]) { /* 1, 2 ve 3 kalıyor! */
   foreach(i; [ 4, 3, 2, 1 ]) { /* SORUN YOK */
   //foreach(i; [ 3, 4, 1, 2 ]) { /* HEPTEN FELAKET */
     free(dilimler[i - 1]);
     düğümleriGöster();
   }
 }
}

class MEM {
 private:
   char[] memory;
   size_t freeLocated, memSize;
   Düğüm* öncekiDüğüm;

 public:
   Düğüm*[char[]*] dizin;

   immutable byteSize = 8;

 this(size_t minSize) {
   size_t isOverload = minSize % byteSize;
   memory = new char[](minSize - isOverload);
   memSize = memory.length;
 }

 char[]* malloc(size_t size) @property {
   scope(exit) freeLocated += size;
   auto konum = freeLocated + size;

   if(memSize  < konum) {
     memory.length += memSize * 2;
     memSize = memory.length;
   }
   auto yeniDüğüm = new Düğüm(freeLocated,
                              memory[freeLocated..konum],
                              öncekiDüğüm);

   dizin[&yeniDüğüm.dilim] = yeniDüğüm;

   if(öncekiDüğüm !is null) öncekiDüğüm.sonraki = yeniDüğüm;
   öncekiDüğüm = yeniDüğüm;

   return &yeniDüğüm.dilim;
 }

 int free(char[] * slice) {
   auto düğüm = dizin[slice];
   if(düğüm is null) {
     return -1; // hata kodu: belirsiz dilime işlem yapılamaz!
   }

   auto öncekiDüğüm = düğüm.önceki;
   if(öncekiDüğüm is null) {
     öncekiDüğüm = new Düğüm(0, null, null, null, false);
   }

   auto sonrakiDüğüm = düğüm.sonraki;
   if(sonrakiDüğüm is null) {
     öncekiDüğüm.sonraki = null;
     /****** BURAYA BİR RECURSIVE LAZIM ******
     sonrakiDüğüm = new Düğüm(0, null, null, null, false);//*/
   } else {
     if(öncekiDüğüm.boş_Mu) {
       auto a = öncekiDüğüm.konum;
       auto b = a;
            b += düğüm.dilim.length;
            b += öncekiDüğüm.dilim.length;

       öncekiDüğüm.dilim = memory[a..b];

       öncekiDüğüm.sonraki = sonrakiDüğüm;
       sonrakiDüğüm.önceki = öncekiDüğüm;

     } else if(sonrakiDüğüm.boş_Mu) {
       auto a = düğüm.konum;
       auto b = a;
            b += düğüm.dilim.length;
            b += sonrakiDüğüm.dilim.length;

       sonrakiDüğüm.dilim = memory[a..b];

       sonrakiDüğüm.önceki = öncekiDüğüm;
       öncekiDüğüm.sonraki = sonrakiDüğüm;

     } else {
       düğüm.boş_Mu = true; // düğümü boşa çıkar...
       return 0; // silinmedi kodu: ama boşaltılmaya aday
     }
   }

   this.dizin.remove(&düğüm.dilim);

   return 1; // silindi kodu: bağı kopartıldı, GC'ye emanet...:)
 }

 override string toString() const {
   import std.range : repeat;
   string memImage  = "       DECIMAL MEMORY DUMP        ASCII  \n";
          memImage ~= format("%s\n", repeat('-', memImage.length)) ;

   for(int i; i < memory.length; i += byteSize) {
     auto row = memory[i..i + byteSize];
     foreach(ubyte address; row) {
       if(address < 10) memImage ~= "   ";
       else if(address < 100) memImage ~= "  ";
       else if(address < 256) memImage ~= " ";
       memImage ~= to!string(address);
     }
     memImage ~= " " ~ row ~ "\n";
   }
   return memImage ~ format("%s>Total %s bytes<\n", repeat(' ', 10), memory.length);
 }
}

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

August 06, 2013

Alıntı:

>

/****** BURAYA BİR RECURSIVE LAZIM ******

Belki "buraya bir recursive lazım" uyarısı dikkatinizi çekmiştir. Aslında lazım değil çünkü bu bir felaket olabilir...:)

Eğer 16 bayt'lık küçük bir "bellekbol sahası" olacaksa iç içe özyinelemeli bir işlev ile kullanılmayanları geriye doğru temizleyebiliriz/temizlemeliyiz. İşte bu yüzden böyle bir uyarı koydum. Çünkü olasılıkları düşündükçe "süpür ve topla" faaliyeti o kadar iç içe girmeye ve karmaşıklaşmaya başlıyor ki buna nasıl bir düzen getirilebileceğinden emin değilim...

Ancak her şey gerçek hayatta ki gibi olmalı! CPU belki çok hızlı olabilir ama onu gereksiz yere kullanmak, hele bir recursive işlev içinde meşgul etmek duruma göre kilitlenmelere yol açabilir. Çünkü burada bütün bir yeryüzünü kaplayan basketbol sahalarından bahsediyoruz...:)

(ŞiMDİLİK BU KADAR, DEVAM EDECEĞİZ...)

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

August 06, 2013

Hocam sizi okumak buyuk veriyor elinize saglik

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

August 06, 2013

Bilmukabele, aynı şeyleri paylaşıyoruz...:)

Aslında örnek hazırlamak bana daha çok zevk veriyor. Sizler de (şu an yarım bir sınıf da olsa...) örnek hazırlarsanız çok sevinirim. Bakın az önce 1-2 dk.'da şu örneği hazırladım da gayet hoş görünüyor. Belki gereksiz ama potansiyeli var:

void main() {
 auto dict = new MEM(64);
      dict.writeln;

 string[] words = [ "a", "an", "two", "this",
                    "extremely", "fast", "car" ];

 foreach(word; words) {
   auto mem = dict.malloc(word.length);
        (*mem).birebirAktar(word.dup);
 }

 dict.writeln;

 char[] slice;
 foreach(ref word_ptr; dict.dizin.keys) {
   slice = cast(char[])(*word_ptr);
   slice.writeln;
 }
 slice[0] = 'k';

 dict.writeln;
}

Alıntı (ÇIKTISI):

>

... (son kısmı)
'a
an
two
this
extremely
fast
car
DECIMAL MEMORY DUMP ASCII

97 97 110 116 119 111 116 104 aantwoth
105 115 101 120 116 114 101 109 isextrem
101 108 121 102 97 115 116 107 elyfastk
97 114 255 255 255 255 255 255 ar������
255 255 255 255 255 255 255 255 ��������
255 255 255 255 255 255 255 255 ��������
255 255 255 255 255 255 255 255 ��������
255 255 255 255 255 255 255 255 ��������
>Total 64 bytes<
'
İngilizce araba (car), Türkçe kar'a dönüştü...:)

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

August 06, 2013

Bu örneği birebirAktar() yapıldıktan sonrasını geliştirelim. Ayrıca ilgili işlevi de vermiş olayım:
Alıntı:

>
>   void birebirAktar(T)(T[] hedef, T[] aktar) {
>     size_t SINIR = hedef.length <= aktar.length ?
>                    hedef.length - 1 : // hedef büyükse =>
>                    aktar.length - 1;
>     do hedef[SINIR] = aktar[SINIR]; while(SINIR--);
>   }
> ```

>

dict.writeln;
char[]* slice; // <--Dikkat: bu sefer son dilimi değil, dilimin işaretçisini alıyoruz...
foreach(ref word_ptr; dict.dizin.keys) {
slice = cast(char[]*)word_ptr;
(*slice).writeln;
}

dict.free(slice); // DENEME: 2, çünkü free() işlevi işaretçi alıyor!

string deneme = "deneme";
auto dilim = dict.malloc(deneme.length);
(*dilim).birebirAktar(deneme.dup);

dict.writeln;


Bu örnek, ilk paylaştığım kod ile çalıştırıldığında görevini yerine getiriyor ama istenilen sonucu vermiyor. Çünkü freeLocated silinen bellek alanı kadar geriye çekilmesi gerekiyordu:
Alıntı:
>
>
>
:    :    :
freeLocated -= düğüm.dilim.length; // <-- eklediğim tek satırlık çözüm...:)
this.dizin.remove(&düğüm.dilim);

return 1; // silindi kodu: bağı kopartıldı, GC'ye emanet...:)

}

>

Netice de kodu düzenleyip çalıştırdığımızda "car" silinmiş ve üzerine "deneme" yazılmış oluyor. Yani hem eski bellek geri verildi, hem de daha geniş bir alan açılmış oldu:

Alıntı (ÇIKTISI):

>

' DECIMAL MEMORY DUMP ASCII

97 97 110 116 119 111 116 104 aantwoth
105 115 101 120 116 114 101 109 isextrem
101 108 121 102 97 115 116 100 elyfastd
101 110 101 109 101 255 255 255 eneme���
255 255 255 255 255 255 255 255 ��������
255 255 255 255 255 255 255 255 ��������
255 255 255 255 255 255 255 255 ��������
255 255 255 255 255 255 255 255 ��������
>Total 64 bytes<
'

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

August 07, 2013

Hocam öncelikle çok teşekkür ederim. Yazdığınız kodları hız açısından test ettim ama oldukça yavaş çıktı bizim bu sistemi yapmak istememizin sebebi hız kazanmaktı aslında :)

		auto my = Clock.currTime();
		for (int i; i < 1_000_000; i++){
			auto y = cast(int*) GC.malloc(4);
			destroy(*y);
		}
		auto end = Clock.currTime();
		writeln(end-my);
		auto my = Clock.currTime();
		for (int i; i < 1_000_000; i++){
			auto y = malloc(4);
			free(y);
		}
		auto end = Clock.currTime();
		writeln(end-my);

İsterseniz sizde test edebilirsiniz.

Kodlar oldukça leziz duruyor ama new kullanımlarını iptal etmeliyiz diye düşünüyorum.

Zekeriya

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

August 06, 2013

Bu konuyla doğrudan ilgili olmadığını biliyorum ama birebirAktar'ı standart işlevlerle de yazabiliriz:

import std.range;
import std.algorithm;

void birebirAktar(T)(T[] hedef, T[] aktar) {
   const adet = min(hedef.length, aktar.length);
   copy(aktar.take(adet), hedef);
}

Yukarıdaki, elemanları teker teker atamak gerektiğinde kullanılabilir ve senin kodunun eşdeğeri olur. Ama nasıl olsa genel olarak aralıklarla değil de özel olarak dilim üzerinde işlediği için belki aşağıdaki daha da yalın bir işlev olur ve hatta perde arkasında memcpy çağrılıyordur ve belki bir miktar daha hızlıdır:

void birebirAktar(T)(T[] hedef, T[] aktar) {
   const adet = min(hedef.length, aktar.length);
   hedef[0..adet] = aktar[0..adet];
}

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

August 07, 2013

Ali hocam katkılar için teşekkürler, bir ara bunları birbirleriyle kapıştırır ve hangisi hızlıysa onu kullanırız...:)

Zekeriyacığım, aslında bu ana konudan kopan bağımsız bir başlık. Yani bir makale ve/veya yazı dizisi yazmak istedim. Çünkü konu çok ilgimi çekiyor. Yavaş olacağı aşikar ama mesele eğlenmekte...:)

Bu arada dilimleri çok seviyorum ve bunlar ile yapılabilecek başka ne var tespit etmeye çalışıyorum. Dilimlerin işaretçisini elde etmek ve onları yaşatmak bana biraz sıkıntı verdi ama hallettim. Ali hocamın işaretçileri yıldızsız kullanabileceğini söylediğinde de her şey daha leziz olmaya başladı.

Katkılar için tekrar teşekkürler, peki bu sınıf ile başka ne örnekler yapılabilir?

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

August 07, 2013

Bir şeyin altını çizmek istiyorum: Geleneksel yönteme göre önemli bir fark var, bu sınıfta önceden ayrılan (reservation) bir bölümü sınıf yaşadığı müddetçe kullanıyoruz. Yani fiziki olarak yan yana duran büyük bir dizi belleği her zaman işgal etmektedir.

Aslında biz burada adeta "simulation" yapıyoruz ama farklı tekniklerle. Eğer tüm olasılıkları uyarlayabilirsek çok şey öğrenmiş olacağız...:)

Ayrıca bazı uygulamalarda verimli şekillerde kullanılabiliriz. Nasıl mı?

Henüz bunu düşünüyorum ama SDL'deki yapıların içine yedirebilirsem, defalarca sahnede oluşturulan yeni bir nesne (graphic) belleğin farklı bölümlerinde oluşmak yerine aynı bölgede meydana gelebilir. Lütfen şuraya bakın:

http://ddili.org/forum/post/7424

SDL ilintileri üzerine çalıştığımda, karşılaştığım iç içe yapılara hayran olmuştum. Aslında biz ekrana farklı renklerdeki geometrik şekillerden oluşan bir çizit (graphic) yerleştirdiğimizde defalarca yapılar arasında gezinme oluyor. Buna rağmen 100 FPS'i geçen (sanırım bir kaç GHz.'lik işlemci şart!) sahne güncellemeleri mümkün olabiliyor...:)

Yani vurgulamak istediğim; geri verilen (bırakılan) nesneler GC'ye emanet edilip bir başkası talep edilirken, algoritmanın keyfine göre ya başka bir bellek bölgesi ya da geri verilen bellek değerlendiriliyor. Oysa 3B (Üç Boyutlu) bir çizit, birden fazla nesneden oluşuyor ve kamera açısına göre kimisi görünmüyor kimisi yeni değerlere göre yeniden oluşuyor. Neyse anlatmam güç, kimse anlamayacak!

Bu teknikte bir potansiyel olduğunu hissediyorum ama henüz çıkaramadım...:)

İlgili bağlantılar...

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]