26 Mart 2013 Salı

Paralel Örüntüler

Aşağıda paralel çalışırken karşıma çıkan örüntülerle ilgili notlarım var.

ThreadPool ile Birbirinden Bağımsız İşlerin Çalıştırılması
Bu en kolay örüntü. ExecutorService'e verilen işin sonucu beklememiz gerekmiyor. Özellikle execute() metodu bu örüntü ile kullanmak için yazılmış.

ThreadPool Olmadan Çalıştırılan Tek Bir İşin Bitmesinin Beklenmesi
Hemen hemen tüm thread kütüphaneleri join isminde bir metod sağlıyorlar. Aşağıdaki bir kaç örnek var.
C#
Örneği buradan aldım.

ThreadPool ile Çalıştırılan Tek Bir İşin Bitmesinin Beklenmesi
C#
Örneği buradan aldım.

Eğer TPL kullanılıyorsa aşağıdaki gibi yapılabilir.

Task t = Task.Factory.StartNew(() => Console.WriteLine("Started"););
t.Wait();

ThreadPool ile Çalıştırılan Tek Bir İşin Bitmesinin Beklenmesi ve Sonucunun Alınması
Java

Executor.execute metodu ile
Bu örüntüde FutureTask kullanılıyor. FutureTask ile işin bitip bitmediği kontrol edilebilir ve gerekirse iş iptal de edilebilir ancak dikkat edilmesi gereken bir nokta var. FutureTask tekrar tekrar kullanılamaz

 FutureTask ya Runnable ya da Callable arayüzününden birisini saran bir sınıf. Runnable arayüzü en eski arayüz ve bir sonuç tipi döndüremiyor çünkü sadece void run() metoduna sahip. Callable arayüzü ise bir sonuç döndürebiliyor çünkü V call() metoduna sahip.

Buradan aldığım örnekte cache içinde önceden hesaplanmış bir sonuç varsa kullanılıyor. Yoksa hesaplam işleminin bitmesi bekleniyor.

public class Cache<K, V> {
    ConcurrentMap<K, FutureTask<V>> map = new ConcurrentHashMap();
    Executor executor = Executors.newFixedThreadPool(8);
   
    public V get(final K key) {
        FutureTask<V> f = map.get(key);
        if (f == null) {
            Callable<V> c = new Callable<V>() {
            public V call() {
                // return value associated with key
            }
        };
        f = new FutureTask<V>(c);
        FutureTask old = map.putIfAbsent(key, f);
        if (old == null)
            executor.execute(f);
        else
            f = old;
        }
        return f.get();
    }
}
Executor.submit metodu ile
Örneği buradan aldım.Future.get() metodu bu sefer timeout mekanizması ile beraber kullanılıyor.

ThreadPool ile Çalıştırılan Birden Fazla İşin Sonucunun Alınması
Java
Bu örüntüde CompletionService kullanılıyor. Buradan aldığım örnekte tek bir işin sonucunun gelmesi ile diğer görevler iptal ediliyor.
void solve(Executor e, Collection<Callable<Result>> solvers)
    throws InterruptedException {
        CompletionService<Result> ecs = new ExecutorCompletionService<Result>(e);
        int n = solvers.size();
        List<Future<Result>> futures = new ArrayList<Future<Result>>(n);
        Result result = null;
        try {
            for (Callable<Result> s : solvers)
                futures.add(ecs.submit(s));
            for (int i = 0; i < n; ++i) {
                try {
                    Result r = ecs.take().get();
                    if (r != null) {
                        result = r;
                        break;//Diğer işlerin bitmesini bekleme
                    }
                } catch(ExecutionException ignore) {}
            }
        }
        finally {
            for (Future<Result> f : futures)
                f.cancel(true);
        }
        if (result != null)
            use(result);
}
Yukarıdaki kodda dikkat edilmesi gereken nokta Future.cancel() metodunun çağırılması. Bu metod alt tarafta Thread.interrupt() metodunu çağırır. Eğer bizim threadimiz aşağıdaki gibi çalışmıyorsa aslında Future.cancel() metodu işe yaramayabilir.

while(!Thread.currentThread().isInterrupted()){
    try{
        // birşeyler yap
    }
    catch(InterruptedException e){
        Thread.currentThread().interrupt();
    }
}

Paralel For ve ForEach Döngüleri
“Parallel.For” for Java? sorusunda bazı döngülerin nasıl paralel çalıştırılabileceği gösterilmiş.Aşağıdaki soruda C# ile for each döngüsü kurulurken, döngüdeki değişkenin kullanılmaması gerektiği gösterilmiş. Örnek:

24 Mart 2013 Pazar

Kod İçinde Harcanan Zamanın Ölçümü

boost
auto_cpu_timer ile kod parçası içinde geçen zamanı ölçmek mümkün.

#include <boost/timer/timer.hpp>
#include <cmath>

int main()
{
  boost::timer::auto_cpu_timer t;

  long total = 0;

  for (long i = 0; i < 100000000; ++i)
    total + =std::sqrt(123.456L); // burn some time

  return 0;
}
C
Buradaki soruda gösterildiği gibi yapılabilir.

Guava
Stopwatch sınıfı ile kod parçası içinde geçen zamanı milisaniye olarak ölçmek mümkün.
Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
//Process
stopwatch.stop();
stopwatch.toString();

stopwatch.reset();
stopwatch.start();
//Process
stopwatch.stop();
stopwatch.toString();
Buradaki örnekte Stopwatch sınıfına Ticker verilerek nasıl test yapılabileceği gösterilmiş.

 

17 Mart 2013 Pazar

EINTR

EINTR ne işe yarar ?
Uygulamamıza bir signal gönderilirse, POSIX bu sinyali hemen işlenmesini istiyor. Ancak bazen uygulamamız bir sistem çağrısı içinde bloke olmuş durumda bekliyor olabilir. Bu gibi durumlarda sistem çağrısı iptal ediliyor ve signal işleniyor. Sistem çağrısını yapan kod parçasına da işlemi tekrarlaması için hata kodu olarak EINTR atanıyor.

Hangi metodlarda EINTR'ya bakmak lazım.
Anladığım kadarıyla liste bayağı uzun. Örnek olarak Sleep, Posix Eşzamanlılık Yapıları başlıklı yazılara göz atabilirsiniz.


5 Mart 2013 Salı

DDS ve Reliable Multicast

RTPS Nedir?
Data Distribution Service (DDS) ile gelen ve beraber çalışabilirliği sağlayan "Real Time Publish Subsribe (RTPS)" standardı sayesinde farklı DDS yazılımları arasında iletişim kurulabiliyor. Bu konuyu gösteren örnek bir resimi buradan aldım. RTPS'in yeni ismi DDSI olarak değiştirildi, yani Data Distribution Service (DDS) Interoperability Spec oldu.



RTPS, DDS yazılımları piyasaya çıktıktan sonra, ihtiyaç üzerine yaratıldı. Örneğin 2006 senesinde kaleme alınan bu yazıdaki DDS Limitations bölümünde RTPS'e olan ihtiyaç açıkça yazılmış.

RTPS ile Kullanılabilen İletişim Yöntemleri
RTPS standardında Best-Effort ve Reliable iletişimden bahsediliyor. Benim anladığım her iletişim yöntemi için de Multicast UDP kullanılıyor. Ancak Reliable Multicast için gönderen ve alan tarafın, Acknowledgement mesajları ile haberleşmeleri gerekiyor.

Best-Effort Nedir?
Best-effort iletişimini gösteren bir şekli buradan aldım. Kabaca UDP'ye benziyor. Gönderilen verinin gidip gitmediği kontrol edilmiyor.

Bir başka şekilde Best-Effort ile gönderilen verinin ağda kaybolması durumunda, verinin tekrar gönderilmediği görülebilir.

Reliable Nedir?
Reliable iletişimini gösteren bir şekli buradan aldım. Kabaca TCP'ye benziyor. Gönderilen verinin gittiği kontrol ediliyor. Eğer gitmediyse tekrar gönderiliyor.


Ancak RTPS standardında best-effort ve reliable iletişimden bahsedilmesine rağmen reliable multicast ile iletişim kurulmasından bahsedilmiyor. Bu durumda da DDS yazılımları arasında uyumsuzluklar halen olabiliyor.

Esasen reliable multicast yapmak için bir çok protokol var. Örneğin "Pragmatic General Multicast" popüler multicast protokollerinden birisi. DDS üreticileri de reliable multicast yapabilmek için anladığım kadarıyla kendi kendilerin protokoller hazırlamışlar.

OpenDDS
OpenDDS ile gelen reliable multicast protokolü ve açıklaması da burada.

Öncelikle anlatılan reliable multicast ile klasik multicast protokolü arasındaki farka dikkat etmek lazım. Klasik multicast protokolünde, mesaj dağıtım görevi, gruba üye olan IPleri bilen switch üzerine yüklenmiş durumda. Ancak RTPS protokolünde switch yok ve mesajların gidip gitmediği MULTICAST_NAK (Negative Acknowledgement) mesajı ile pasif olarak mesajları dinleyen aboneye yüklenmiş durumda. Aşağıdaki şekil bu durumu gösteriyor.



OpenSplice
OpenSplice forumundaki http://forums.opensplice.org/index.php?/topic/1212-multicast-communication/ adresinde kendi Native networking service'lerinin nasıl çalıştığını söylemiş.

Dedikleri de :

data will be retransmitted (point-to-point) to those nodes that have not sent an acknowledgement


Anladığım kadarıyla OpenDDS ve OpenSplice arasında reliable multicast uygulaması arasında önemli farklar var.

DDS "History QoS" - Tarihçe

Giriş
Benim gibi DDS'e aşina olmayan insanların karşılaştıkları sıkıntılardan birisi de History QoS'in nasıl çalıştığı konusundaki kafa karışıklığıdır.

History Qos, DataWriter ve DataReader sınıflarının etkiler. Bu sınıflar daha ellerindeki veriyi gönderip almamış iken DDS ara katmanına yeni bir güncelleme mesajı gelince o instance için kaç tane geçmiş güncelleme mesajını saklamak istediğimizi bildirir.

Aşağıda CorexDDS'in sayfasında bunu gösteren güzel bir resim var. Eğer Topic bir anahtar (key) alanına sahipse History Qos resimde de gösterildiği gibi instance başına kaç tane mesajın bellekte tutulacağını belirtir.
 
Yine aynı şekilde benzer bir çizimi burada buldum .



KEEP_ALL Seçeneği
Eğer History QoS KEEP_ALL ise DataWriter ve DataReader cacheleri birer LinkedList gibi çalışırlar ve en son gelen mesajları saklamaya devam ederler.

Keep_All ile Reliable Reliability beraber kullanılırsa etkileri aşağıdaki gibi olur.
Keep_All + Reliable : Tüm mesajlar mutlaka gönderilir. Gerekirse DataWriter bir süre bloke edilir. Buna absolute veya strict reliability deniyor. Güncellik problemi olmayan ve mutlaka işlenmesi gereken durumlarda kullanır. Örneğin bir müşterinin verdiği sipariş emirleri hiç bir zaman kaybolmamalıdır ve uygulama okuyuncaya kadar saklanmalıdır. JMS tabanlı ara katman yazılımları genellikle bu modeli benimsemişlerdir ve gönderilen mesajlar birisi okuyuncaya kadar saklanılır.
Keep_All ile Unreliable Reliability beraber kullanılırsa etkileri aşağıdaki gibi olur.
Keep_All + Unreliable : Tüm mesajlar gönderilmeye çalışılır. Güncellik problemi olmayan ve mesajın mutlaka işlenmesi de gerekmeyen durumlarda kullanılabilir. Kullanım şekli biraz UDP broadcast'i andırıyor. Broadcast için gönderilen tüm paketlerin mümkün olduğunca gönderilmeye çalışılması gibi.
KEEP_LAST Seçeneği
Eğer History QoS KEEP_LAST ise DataWriter ve DataReader cacheler birer circular buffer gibi çalışırlar ve en son gelen N tane mesajı  saklamaya devam ederler.

Keep_Last ile Reliability beraber kullanılırsa etkileri aşağıdaki gibi olur.
Keep_Last + Reliable    : En son N tane mesaj mutlak gönderilir. Eğer henüz gönderilmemiş mesaj varsa silinir ve gönderilmeye çalışılmaz. Buna reduced reliability deniyor.
Bu kısmı daha iyi açıklayan bir cümleyi şuradan aldım
"Or, as a tradeoff for less memory, CPU, and network usage, you can choose a reduced level of reliability where only the last N values are guaranteed to be delivered reliably to DataReaders (where N is user-configurable). In the reduced level of reliability, there are no guarantees that data sent before the last N are received; only the last N data packets are monitored and repaired if necessary."
Keep_Last ile Unreliable Reliability beraber kullanılırsa etkileri aşağıdaki gibi olur.  
Keep_Last + Unreliable : En son N tane mesaj gönderilmeye çalışılır. Eğer henüz gönderilmemiş mesaj varsa silinir ve gönderilmeye çalışılmaz. Örneğin radardan gelen en son N tane plot'ın tracker'a sokulması için kullanılabilir.
KEEP_LAST kullanılırken OpenDDS'te circular buffer dolu ise ve yer açmak gerekiyorsa bu durum kabaca şöyle kodlanmıştır.
DataWriterImpl::write()->
WriteDataContainer::obtain_buffer()->
WriteDataContainer::remove_oldest_sample()->//En eski instance silinir
WriteDataContainer::enqueue()->//Silinen instance'a ait daha gönderilmemiş mesaj silinir
PublicationInstance->enqueue_tail_next_instance_sample ()//listenin sonuna eklenir