Thread overview
Büyük Sayıları Sayma...
Apr 05, 2013
Salih Dinçer
Apr 05, 2013
Salih Dinçer
Apr 05, 2013
Salih Dinçer
Apr 05, 2013
Salih Dinçer
Apr 05, 2013
Salih Dinçer
Apr 05, 2013
Salih Dinçer
April 05, 2013

Merhaba,

Elbette depolama birimlerimizin sonsuza kadar gitmiyor ama, pekala işlemcinin saymakla bitiremeyeceği bir döngü içinde sayıları saymak isteyebiliriz. Eğer ulong.max dışına taşan bir sayma işlemi gerçekleştirirsek n'apacağız!

Başlangıçta imdadımıza std.bigint (http://dlang.org/phobos/std_bigint.html) yetişmekte. Peki ya biz, elde ettiğimiz sayı üzerinde dört işlem yapmak istemiyorsak; üstelik sayma işleminin CPU kaynaklarını çok kullanmaması gerekiyorsa ne yapabiliriz? Cevap olarak aklıma ulong[] dizisi oluşturmak geçiyor.

Ne dersiniz?

Ama bir sakıncası da her seferinde 1 arttırılan digit[0]'ın ulong.max'a gelip gelmediğini sorgulamamız gerekecek. Yani her döngü içerisinde en az bir kez (diğer digit'ler içinde olabilir!) CMP ve JMP komutları yanısıra RET ile programa kaldığı yerde geri dönmesi fazladan işlemci gücü tüketecek. Düşünsenize; INC falanca komutu yerine bir ton şey ile muhatap olmamız içten bile değil...:(

Off, bir çözüm önerisi olan var mı?

Teşekkürler...

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

April 05, 2013

Şöyle bir şey yaptım:

import std.stdio;

 union BC (T) {
   T[4] DIGS;
   uint CONV;
   struct {
     T DIG1, DIG2, DIG3, DIG4;
   }

   auto inc() {
     asm {
       mov EBX, this;

       DIGIT1: add BC.DIG1.offsetof[EBX], 1;
               jc DIGIT2;
               jmp END;

       DIGIT2: add BC.DIG2.offsetof[EBX], 1;
               jc DIGIT3;
               jmp END;

       DIGIT3: add BC.DIG3.offsetof[EBX], 1;

       END: nop;
     }
   }

   string toString() {
     enum size { s1 = 2^^8, s2 = 2^^16, s3 = 2^^24 }
     ulong result = DIG1;

     result += DIG2 * size.s1;
     result += DIG3 * size.s2;
     result += DIG4 * size.s3;

     //return std.string.format(result);/*
     return std.string.format("%.8b-%.8b-%.8b-%.8b",
            DIG1, DIG2, DIG3, DIG4);//*/
   }
 }

void main() {
 auto test = BC!ubyte([255, 255, 255, 0]);

 test.writeln("(", test.CONV, ")");
 test.inc();
 test.writeln("(", test.CONV, ")");
}/* Çıktısı:
11111111-11111111-11111111-00000000(16777215)
00000000-00000001-11111111-00000000(16711936)
*/

Ama assembly kullandığım için gıcık bir şeyler oluyor; başka veri türlerinde farklı davranışlar sergileyebiliyor. Örneğin yukarıdaki gibi ubyte kullandığınızda, taşma olduğu sırada atlıyor (JC), tamam ama nedense eldeyi (Carry) bir sonraki dizinin elemanına aktarıyor! Bu yüzden de 3. ve 4. digit'ler hiç bir zaman değişmiyor...:(

İşte korktuğum başıma geldi ve yine başladığım yere yani if()'li sürümüne muhtaç kaldım:

Alıntı:

>
>   :  :  :
>     auto inc() {
>       DIG1++;
>       if(!DIG1) DIG2++;
>       else return;
>       if(!DIG2) DIG3++;
>       else return;
>       if(!DIG3) DIG4++;
>       else return;
>     }
>   :  :  :
> }/* Çıktısı:
> 11111111-11111111-11111111-00000000(16777215)
> 00000000-00000000-00000000-00000001(16777216)
> */
> ```

>
Aslında bunun assembly koduna bakıp modelleme yapabilirim. Ama if()'in olduğu yerde bir çift komut (CMP ve atlama komutlarından biri) olduğuna adım gibi eminim. Oysa taşma bayrağına bakan atlama komutu (JC) işimizi görüyor. Bilemiyorum, belki de inline assembly kullanmadan devam etmek de iyi bir fikir olabilir. Çünkü foreach() ile çok daha uzun dizilerin üstesinden gelebiliriz diye düşünüyorum.

Sevgiler, saygılar...

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

Son hali, ne kadar hızlı bilmiyorum...:)

import std.stdio;

 struct BC (T, size_t size) {
   T[size] DIGS;

   auto opUnary(string op)()
   if(op == "++") {
     DIGS[0]++;
     foreach(i, ref DIG; DIGS) {
       if(!DIG) DIGS[i+1]++;
       else return;
     }
   }

   string toString() {
     ulong result;
     foreach(i, DIG; DIGS) {
       if(i) result += DIG * 2 ^^ (8 * i);
       else result = DIG;
     }
     return std.string.format(result);
   }
 }

void main() {
 ubyte[5] data;// = [255, 255, 255, 0, 0];
 auto test = BC!(ubyte, data.length)(data);

 test.writeln;
 test++;
 test.writeln;
}

Tabi ciddi bir sorun var o da ulong.max'i aşan değerleri toString() içinde hesaplatıp ekrana yazdırmak şu halde (data.length > 4) mümkün değil. Tabi bigint kütüphanesini devreye sokabiliriz...:)

Başarılar...

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

April 05, 2013

Az önce test yapma imkanı buldum...:)

Sonuçlara bakılırsa hiç de yavaş değil:

Alıntı:

>

[atelyeweb@sdb Belgeler]$ time ./bigcount
999999
real 0m13.070s
user 0m2.751s
sys 0m0.105s
[atelyeweb@sdb Belgeler]$ dmd bigcount.d
[atelyeweb@sdb Belgeler]$ time ./bigcount
999999
real 0m14.432s
user 0m0.506s
sys 0m0.122s

> void main() {
>   ubyte[5] data;
>   auto test = BC!(ubyte, data.length)(data);
>
>   foreach(i; 0..1_000_000) {
>     i.write("\r");
>     //test++;
>   }
> }
> ```

>

**Dip Not:** İlki test.write sonucudur...

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

:)

Şimdi 2 milyona kadar yaptım, benim geliştirdiğim algoritma neredeyse 1 sn. önce bitirdi. Halbuki fazladan 2 döngü içinde koşuyor:

  • 1.'si, opUnary() içinde ama sayı büyüdükçe ve arasıra (taşma olduğunda) döngü biraz uzun sürüyor,
  • 2.'si, toString() içinde ve bir takım matematiksel işlemler yapılarak format()'dan ve dolayısıyla std.conv ile dönüşm mücadelesi...

Çok ilginç gerçekten, uint.max'a kadar test etmeyi düşünüyorum...

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

April 05, 2013

Güzel bir soru. Bazı işlemcilerin overflow trapping diye bir olanağı var. Belki ondan yararlanılabilir. Bilmiyorum. :)

Ali

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

April 05, 2013

Gösterdiğin iki sonuç arasında ne fark vardı? Birincisini -O ile mi derlemiştin?

Dip not: Anladım. :)

Ali

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

April 06, 2013

Testi objektif bir hale çevirip döngü boyunca standart çıktıya son değerini göndermeyi gizledim...

Bu sefer sonuçlar şaşırtıcı değil...:)

Standart, giriş seviyesinde bir bilgisayarda döngünün uint.max'a ulaşması 10 sn. sürmüyormuş! Benzer niteliklerde bir yapı üzerinde alıştığımız şekilde bir değişkeni arttırdığımızda ise 17, bilemediniz 18 sn. sürüyor. Burdan 2 sonuç çıkıyor:

  • Öncelikle hızlı bir şekilde sayının arttırılması beklediğimiz bir sonuç
  • Ancak dolaylı olarak bir yapı ve opUnary vasıtasıyla eriştiğimizde süre ikiye bile katlanabilirmiş...

Gelelim bu başlığın konusu olan test ise yaklaşık 1 dk. sürmekte. Ama muhtemelen yukarıdaki 2. sonuç nedeniyle gereksiz bir geçikme söz konusu olsa gerek. Tabi böyle bir algoritmanın bir yapı içinde kullanılması, kurulması ve/veya taşınmas en doğru olanı. Test kodu ise şu şekilde:

 struct SAY (T) {
   T xSay;

   auto opUnary(string op)()
   if(op == "++") {
     xSay++;
   }

   string toString() {
     return std.string.format("%s", xSay);
   }
 }

void main() {
 auto test = BC!(ubyte, 5)([0,0,0,0,0]);/*
 auto test = SAY!uint(0);//*/

 foreach(i; 0..uint.max) {
   //test.write("\r");
   test++;
 }
 test.writeln;
}

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