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. ]