July 15, 2012

Hata da olabilir ama sistemi fazla yüklediğimizdendir. Her bininci iş parçacığını beklemek yerine her on tanede onunu da beklemeyi dener misin. Bakalım bir fark oluyor mu...

       auto görev = task!kombinasyonuKullan(i, kombinasyon.dup);
       görev.executeInNewThread();
       if ((i % 10) == 0) thread_joinAll();

Ali

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

July 16, 2012

Hemen denedim ama sıralı olana (3-4 sn.) göre çok yavaş?
'real 0m27.212s
user 0m17.658s
sys 0m33.530s
'

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

July 16, 2012

Bu arad koşutluk derecesi ile thread'lar arasındaki ilişki tam olarak nedir? İşlemci tek çekirdekli bir makine de olsa birden fazla iş parçacığına müsaade etmeyecek mi?

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

July 16, 2012

Alıntı (acehreli):

>

Hiç anlayamadığım bir hata var. Küçük bir program ile gösterebiliyorsanız hata raporu açmak isteyebilirsiniz:

http://d.puremagic.com/issues/
Belki de hata bizdedir...

Aşağıdaki gibi bir deneme yaptım da çok çekirdeklinin farkını şimdi gördüm! Normalde basit bir 3 GHz.'lik bilgisayarda ve aşağıdaki algoritmayla (isPrime), 18 basamaklı 18 sayıdan asal olanlarını tespit etmesi 1 dk.'dan fazla vakit alıyor. Ancak parallelism'i kullanınca 30 sn. civarında...:)

import std.stdio, std.parallelism;

void isPrime(ulong p, size_t i) {
	    bool notPrime = true;
	    for(ulong n = 2; n * n <= p; n++) {
	    	    if(p % n == 0) {
	    	    	    notPrime = false;
	    	    	    break;
	    	    }
	    }
	    if(notPrime) 	    writefln("%d: %u is prime", i, p);
	    else 	    	    writefln("%d: %u", i, p);
}

void main() {
   ulong[] primeNums = [ 100109100129100151, 113127131137139149, 115578717622022981,
			  	  	  117116115114112111, 117491318992185252, 137153163127255511,
			  	  	  161111111111111111, 166667166667000003, 212345678987654321,
				  	    213335552557777777, 255455445544554553, 261798184036870849,
				  	    300000224101777931, 337190719854678690, 359681422627177289,
				  	    421538917598915629, 443231282319131071, 496481100121144169,
				  	    618681508598750923, 777777722155555333 ];
   foreach(i, p; primeNums) {
	    //isPrime(p, i+1);/*
	    auto xTask = task!isPrime(p, i+1);
	    xTask.executeInNewThread(); //*/
   }
}

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

July 16, 2012

Buna bir şans daha tanıyorum. :)

parallel()'in ancak InputRange ile çalışabildiğini ve foreach desteğini opApply() ile veren türleri dışladığını anlamıştık. Şimdi onun yapmadığını çok kısıtlı da olsa kendim std.concurrency'yi doğrudan kullanarak deniyorum.

Aynı DilimKombinasyonu'nu kullanıyorum ama kombinasyonuKullan()'ın aldığı parametreyi immutable(int[]) yapıyorum yoksa veri paylaşımını kilitlerle yönetmek gerekecekti:

import std.concurrency;
import std.conv;

/* Dikkat: Bu sefer immutable(int[]) alıyor. */
void kombinasyonuKullan(size_t i, immutable int[] kombinasyon)
{
   writefln("baş - %s: %s", i, kombinasyon);

   foreach (x; 0 .. 100_000) {
       faktöriyel(123);
   }

   // Thread.sleep(dur!("msecs")(10));
   writefln("son - %s: %s", i, kombinasyon);
}

Asıl önemli olan işlev bu:

/* Belirtilen işlemi belirtilen aralığın bütün elemanları üzerinde koşut
* olarak işletir. Hiçbir anda koşutlukDerecesi'nden daha fazla işlem
* işletilmez.
*
* (Yetersizlik: Her ne kadar esnekmiş gibi görünse de aralığın elemanlarının
*               int[] türünde olmaları gerekir.)
*/
void koşutİşlet(Aralık)(void function(size_t, immutable int[]) işlem,
                       Aralık aralık,
                       size_t koşutlukDerecesi = totalCPUs)
{
   size_t işleyen = 0;    /* Kaç adet işlemin işlemekte olduğu. */

   /* Etkin işlemlerin belirtilen sayıya kadar inmelerini bekler. */
   void bekle(size_t kaçaKadar)
   {
       while (işleyen > kaçaKadar) {
           /* Not: spawnLinked() ile başlatılan işlemler sonlandıklarında
            * LinkTerminated hatasını atarlar (ve o hata burada olduğu gibi
            * mesaj olarak da alınabilir). */
           receiveOnly!LinkTerminated();
           --işleyen;
       }
   }

   foreach (i, eleman; aralık) {
       /* Eğer koşutluk sınırındaysak işleme yer açılana kadar bekle. */
       bekle(koşutlukDerecesi - 1);

       /* Yeni bir işlem başlat. */
       spawnLinked(işlem, i, to!(immutable int[])(eleman));
       ++işleyen;
   }

   /* Kalan işlemlerin bitmelerini bekle. */
   bekle(0);
}

Artık sistem belirtilen koşutlukDerecesi'nden fazla yüklenmiyor:

void main()
{
   int[] dilim;
   foreach (i; 0 .. 7) {
       dilim ~= i;
   }

   auto kombinasyonlar = DilimKombinasyonu(dilim, dilim.length/2);
   koşutİşlet(&kombinasyonuKullan, kombinasyonlar);
}

Ali

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

July 16, 2012

Hiç anlayamadığım bir hata var. Küçük bir program ile gösterebiliyorsanız hata raporu açmak isteyebilirsiniz:

http://d.puremagic.com/issues/

Evet, tek çekirdekli ortamda birden fazla iş parçacığını başlatmaması gerekir. (Tabii işlev parametresi olarak farklı bir değer verilmemişse.)

Ali

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

July 22, 2012

Farklı Derleme Arasındaki Fark (2,82x) ve Sonuçları:
'Koşut işletiyorum
17756.456 ms.
Sırayla işletiyorum:
50132.682 ms.
'
Ancak kodda değişiklik yaptım. Tek dosyada derlenebilmesi ve süre hesabının ekran belleğinden bağımsız yapılabilmesi için şöyle bir kopyala, yapıştır ve derlelik hale getirdim...:)

Derlerken de sondan -release'a kadar silin lütfen: ' dmd kombinasyon2 -release -version=1'

module içModül;

import std.range;
import std.exception;
import std.string;
import std.conv;
import std.algorithm;
import std.datetime;
/**
* Bir RandomAccessRange aralığının belirtilen sayılı kombinasyonlarını üreten
* aralık.
*/
struct Kombinasyon(Aralık)
   if (isRandomAccessRange!Aralık)
{
private:

   Aralık aralık;         // Asıl aralık

   size_t[] indeksler;    // Bütün kombinanyonların şu andaki 'front'unu
                          // temsil eden ve asıl aralığın hangi elemanlarını
                          // içerdiğini belirleyen indeksler.

   /* Başlangıçta veya popFront() sonucunda geçersizleşen indeksleri
    * düzenler. ('baş'tan önceki indekslere dokunmaz.) */
   void indeksDüzenle(size_t baş)
   in {
       assert(baş >= 1);

   } body {
       foreach (i; baş .. indeksler.length) {
           /* Bir önceki indeksin bir büyüğü. */
           indeksler[i] = indeksler[i - 1] + 1;
       }
   }

public:

   this(Aralık aralık, size_t kaçlı)
   {
       enforce(kaçlı <= aralık.length,
               format("%s elemanlı aralğın %s elemanlı kombinasyonu"
                      " oluşturulamaz.", aralık.length, kaçlı));

       if (!aralık.empty) {
           this.aralık = aralık;
           this.indeksler = new size_t[](kaçlı);
           indeksDüzenle(1);
       }

   }

   bool empty() const pure @property
   {
       immutable sıfırlı_mı = indeksler.empty;
       immutable ilkİndeksGeçersiz_mi = indeksler[0] ==
                                        (aralık.length - indeksler.length + 1);

       /* İlk indeks yasal olmadığı zaman sonuna geldik demektir. */
       return sıfırlı_mı || ilkİndeksGeçersiz_mi;
   }

   /*
    * İndekslerin gösterdikleri elemanlardan oluşan bir aralık döndürür.
    *
    * (Not: Aslında bu sonucu bir InputRange aralığı olarak döndürmek de
    * mümkündür. Böylece o bile tembel olarak kullanılabilir ve üstelik asıl
    * aralığın elemanlarının kopyalanmalarının pahalı olduğu veya hiç mümkün
    * olmadığı durumlarda büyük kazanç sağlayabilir.)
    */
   ElementType!Aralık[] front() const pure @property
   {
       auto sonuç = new ElementType!Aralık[](indeksler.length);

       foreach (i, indeks; indeksler) {
           sonuç[i] = aralık[indeks];
       }

       return sonuç;
   }

   void popFront()
   {
       /*
        * Sonuncu indeksi bir arttırarak başlar ve gerektikçe daha önceki
        * indekslere geçerek onları da bir arttırır. Çıkmadan önce indeksleri
        * gerektiği gibi düzenler ama bunu ilk indeks için yapmaz çünkü ilk
        * indeksin düzensizliği aralığın sonunu belirlemektedir.
        */


       /*
        * Burada eşdeğeri olan şu iki döngü de kullanılabilirdi:
        *
        * Bunun sakıncası, >=0'ın doğru işleyebilmesi için i'nin işaretli bir
        * tür olmasının gerekmesi ve o yüzden to!int() gibi bir yöntemle tür
        * dönüşümü gerekmesidir:
        *   for (int i = to!int(indeksler.length) - 1; i >= 0; --i) {
        *
        * Bunun sakıncası da emekliye ayrılacağı söylenen foreach_reverse'ten
        * yararlanmasıdır:
        *   foreach_reverse (i, ref indeks; indeksler) {
        */
       foreach (i; retro(iota(0, indeksler.length))) {
           ++indeksler[i];

           if (indeksler[i] < (aralık.length - (indeksler.length - 1 - i))) {
               indeksDüzenle(i+1);
               break;
           }
       }
   }
}
import std.stdio;
import std.parallelism;
import içModül;

// Yavaş bir faktöriyel
ulong faktöriyel(ulong n)
{
   return n == 0 ? 1 : n * faktöriyel(n - 1);
}

void kombinasyonuKullan(size_t i, int[] kombinasyon)
{
   //writefln("baş - %s: %s", i, kombinasyon);

   foreach (x; 0 .. 100_000) {
       faktöriyel(123);
   }

   // Thread.sleep(dur!("msecs")(10));
   //writefln("son - %s: %s", i, kombinasyon);
}

 /**
  * Kolaylık işlevi
  */
Kombinasyon!Aralık kombinasyonlar(Aralık)(Aralık aralık, size_t kaçlı)
{
   return Kombinasyon!Aralık(aralık, kaçlı);
}

void main()
{
   int[] dilim;
   foreach (i; 0 .. 12) {
       dilim ~= i;
   }

   auto aralık = kombinasyonlar(dilim, dilim.length / 2);
   StopWatch lap; /*** Salih Ekledi ***/


   version (1) {
       lap.start(); /*** Salih Ekledi ***/
       writeln("Sırayla işletiyorum:");

       /* foreach aralıklarla kullanıldığında döngü sayacı otomatik
        * değildir. O yüzden i'yi açıkça elle arttırdım. Bunun yerine zip ve
        * sequence'tan da yararlananılabilir
        *
        *   import std.range;
        *   foreach (i, eleman; zip(sequence!"n"(), aralık)) {
        */
       size_t i = 0;
       foreach (eleman; aralık) {
           kombinasyonuKullan(i, eleman);
           ++i;
       }
   } else {
       lap.start(); /*** Salih Ekledi ***/
       writeln("Koşut işletiyorum");
       foreach (i, eleman; parallel(aralık, 1)) {
           kombinasyonuKullan(i, eleman);
       }
   }
   lap.stop();  /*** Salih Ekledi ***/
   writefln("%.3f ms.", lap.peek.usecs/1000.0);
}

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

July 22, 2012

Aslında hata bende imiş ve iletimi düzenledim. Sonuç almak için her zaman bir kaç test yapmak gerekiyor. Tek testte hata olabiliyor. Kusura bakma lütfen...

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

July 22, 2012

Allah rahatlık versin...:)

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

July 22, 2012

Buna iki şans daha tanımaya karar verdim. :) Birincisi aşağıda. Eğer bu da iyi sonuç vermezse iş parçacıklarının kontrolsüz biçimde başlatılmalarından şüpheleneceğim ve bir de elle oluşturduğum bir iş parçacığı grubu (thread pool) ile deneyeceğim.

Bundan önceki kombinasyonlar özyinelemeli olarak oluşturuldukları için opApply()'dan yararlanıyorlardı. opApply() program işleyişini eline geçirir ve asıl döngüyü kendi içinde işletir. O yüzden parallel()'i opApply() ile gerçekleştirilen aralıklarla kullanamayacağımızı farketmiştik. O yüzden de koşutİşlet() diye bir işlev yazmak zorunda kalmıştık.

koşutİşlet()'i bir kenara bırakarak yine parallel()'i devreye sokmaya karar verdim ve özyinelemeli yerine yinelemeli (iterative) bir kombinasyon aralığı yazdım. Bu, her kombinasyona asıl aralığın hangi elemanlarının dahil olduklarını belirleyen indekslerden yararlanıyor. Bunu InputRange olarak gerçekleştirdiğim için parallel() ile de kullanılabiliyor.

import std.range;
import std.exception;
import std.string;
import std.conv;
import std.algorithm;

/**
* Bir RandomAccessRange aralığının belirtilen sayılı kombinasyonlarını üreten
* aralık.
*/
struct Kombinasyon(Aralık)
   if (isRandomAccessRange!Aralık)
{
private:

   Aralık aralık;         // Asıl aralık

   size_t[] indeksler;    // Bütün kombinanyonların şu andaki 'front'unu
                          // temsil eden ve asıl aralığın hangi elemanlarını
                          // içerdiğini belirleyen indeksler.

   /* Başlangıçta veya popFront() sonucunda geçersizleşen indeksleri
    * düzenler. ('baş'tan önceki indekslere dokunmaz.) */
   void indeksDüzenle(size_t baş)
   in {
       assert(baş >= 1);

   } body {
       foreach (i; baş .. indeksler.length) {
           /* Bir önceki indeksin bir büyüğü. */
           indeksler[i] = indeksler[i - 1] + 1;
       }
   }

public:

   this(Aralık aralık, size_t kaçlı)
   {
       enforce(kaçlı <= aralık.length,
               format("%s elemanlı aralğın %s elemanlı kombinasyonu"
                      " oluşturulamaz.", aralık.length, kaçlı));

       if (!aralık.empty) {
           this.aralık = aralık;
           this.indeksler = new size_t[](kaçlı);
           indeksDüzenle(1);
       }

   }

   bool empty() const pure @property
   {
       immutable sıfırlı_mı = indeksler.empty;
       immutable ilkİndeksGeçersiz_mi = indeksler[0] ==
                                        (aralık.length - indeksler.length + 1);

       /* İlk indeks yasal olmadığı zaman sonuna geldik demektir. */
       return sıfırlı_mı || ilkİndeksGeçersiz_mi;
   }

   /*
    * İndekslerin gösterdikleri elemanlardan oluşan bir aralık döndürür.
    *
    * (Not: Aslında bu sonucu bir InputRange aralığı olarak döndürmek de
    * mümkündür. Böylece o bile tembel olarak kullanılabilir ve üstelik asıl
    * aralığın elemanlarının kopyalanmalarının pahalı olduğu veya hiç mümkün
    * olmadığı durumlarda büyük kazanç sağlayabilir.)
    */
   ElementType!Aralık[] front() const pure @property
   {
       auto sonuç = new ElementType!Aralık[](indeksler.length);

       foreach (i, indeks; indeksler) {
           sonuç[i] = aralık[indeks];
       }

       return sonuç;
   }

   void popFront()
   {
       /*
        * Sonuncu indeksi bir arttırarak başlar ve gerektikçe daha önceki
        * indekslere geçerek onları da bir arttırır. Çıkmadan önce indeksleri
        * gerektiği gibi düzenler ama bunu ilk indeks için yapmaz çünkü ilk
        * indeksin düzensizliği aralığın sonunu belirlemektedir.
        */


       /*
        * Burada eşdeğeri olan şu iki döngü de kullanılabilirdi:
        *
        * Bunun sakıncası, >=0'ın doğru işleyebilmesi için i'nin işaretli bir
        * tür olmasının gerekmesi ve o yüzden to!int() gibi bir yöntemle tür
        * dönüşümü gerekmesidir:
        *   for (int i = to!int(indeksler.length) - 1; i >= 0; --i) {
        *
        * Bunun sakıncası da emekliye ayrılacağı söylenen foreach_reverse'ten
        * yararlanmasıdır:
        *   foreach_reverse (i, ref indeks; indeksler) {
        */
       foreach (i; retro(iota(0, indeksler.length))) {
           ++indeksler[i];

           if (indeksler[i] < (aralık.length - (indeksler.length - 1 - i))) {
               indeksDüzenle(i+1);
               break;
           }
       }
   }
}

/**
* Kolaylık işlevi
*/
Kombinasyon!Aralık kombinasyonlar(Aralık)(Aralık aralık, size_t kaçlı)
{
   return Kombinasyon!Aralık(aralık, kaçlı);
}

Onu yine aynı deneme koduyla şöyle denedim:

import std.stdio;
import std.parallelism;
import kombinasyon;

// Yavaş bir faktöriyel
ulong faktöriyel(ulong n)
{
   return n == 0 ? 1 : n * faktöriyel(n - 1);
}

void kombinasyonuKullan(size_t i, int[] kombinasyon)
{
   writefln("baş - %s: %s", i, kombinasyon);

   foreach (x; 0 .. 100_000) {
       faktöriyel(123);
   }

   // Thread.sleep(dur!("msecs")(10));
   writefln("son - %s: %s", i, kombinasyon);
}

void main()
{
   int[] dilim;
   foreach (i; 0 .. 12) {
       dilim ~= i;
   }

   auto aralık = kombinasyonlar(dilim, dilim.length / 2);

   version (sirayla_islet) {
       writeln("Sırayla işletiyorum:");

       /* foreach aralıklarla kullanıldığında döngü sayacı otomatik
        * değildir. O yüzden i'yi açıkça elle arttırdım. Bunun yerine zip ve
        * sequence'tan da yararlananılabilir
        *
        *   import std.range;
        *   foreach (i, eleman; zip(sequence!"n"(), aralık)) {
        */
       size_t i = 0;
       foreach (eleman; aralık) {
           kombinasyonuKullan(i, eleman);
           ++i;
       }

   } else {
       writeln("Koşut işletiyorum");
       foreach (i, eleman; parallel(aralık, 1)) {
           kombinasyonuKullan(i, eleman);
       }
   }
}

Daha önceki bütün denemeler gibi bu da benim ortamımda işe yaradı. :) Dört çekirdek de devreye girdiği için çalışma süresi kabaca üçte birine iniyor. Salih, senin ortamında durum nasıl? Bu da yavaş mı?

Ali

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