Burak Selim Şenyurt, blogundaki eski bir yazısında bir okuyucusunun şu sorusunu cevaplamış: "Madem Parallel.For veya Parallel.ForEach ile herşey daha hızlı oluyor, niye normal for ve foreach döngülerini bu formasyona sokmuyorlarda ek bir şeyler ilave ediyorlar".
Burak Selim Şenyurt, konuyu performans açısından açıklamaya çalışmış. Ancak sorunun gerçek cevapları multi-threading ile ilgili.
Paralel olan ve olmayan for arasındaki fark performanstan ziyade; işlemlerin “paralel” olarak farklı threadler ile yapılması. TPL (Task Parallel Library), ThreadPool’u kullanarak döngü içerisindeki işlemlerin paralel olarak yürütülmesini sağlıyor. Dolayısıyla okurun sorduğu tarzda bir değişiklik yapmak “breaking change” olacaktır. Neler bozulacaktır?
1) Paralel olarak çalıştıralacak işlemlerin, yani döngü içerisindeki kodun, thread-safe olması gerekmektedir. Aksi taktirde beklenmedik bazı sorunlarla (Örn: Race conditions) karşılaşabilirsiniz. Örnek olarak Burak Selim Şenyurt’un kullandığı kodda Random.Next thread-safe değildir ve Microsoft’un belirttiği üzere muhtemelen her çalıştığında 0 dönecektir. Dolayısıyla her for döngüsünü değişiklik yapmadan parallel.for’a çeviremezsiniz.
2) Birden fazla işlemin paralel olarak çalıştırılması her zaman performans kazandırmaz. “Pure function” olmayan bir methodu, thread-safe yapabilmek için lock kullanmanız gerekir. Lock’un kendisi masraflı olmasa bile bir thread’in, başka bir thread tarafından kilitlenen kaynağın serbest kalmasını beklemesi paralel işlemlerin performansını düşürecektir. Burak Selim Şenyurt, thread yaratmanın masrafından bahsetmiş. Ancak paralel işlemlerde aksi belirtilmediği sürece processor sayısı kadar thread kullanılır. Bu threadler de Threadpool tarafından hazırda (idle) bekletilir. Threadpool oldukça optimize çalışır. İhtiyaç duyulduğunda ParallelOptions veya ThreadPool.SetMinThreads kullanılarak idle thread sayısı arttırılabilir ve thread oluşturmanın bedeli düşürülebilinir. Ancak paralel çalışmaya optimize edilmemiş bir kod, bazı senaryolarda performansın çok daha düşmesine sebep olabilir. Paralel çalıştırma, CPU’yu yoğun kullanan işlemler için tercih edilmelidir. İşlemler paralel olarak diğer processorlarda da çalışacağı için işlem süresi azalacaktır. Ancak “blocking” kodlar threadin beklemesine sebep olacak ve dolayısıyla performans kaybı yaşanacaktır. Bu tarz durumlarda async işlemler daha faydalı olacaktır.
3) Paralel olarak yapılan işlemlerde, işlem sırası değişebilir. Dolayısıyla 1, 2, 3, 4 olarak gelmesini beklediğiniz sonuçlar 1, 4, 3, 2 şeklinde sıralanabilir. Eğer işlemlerin sırası önemliyse bu da ekstra değişiklik yapmanıza sebep olacaktır.
Alternatif başka sebepler de olabilir. Ancak okuyucunun talep ettiği tarz bir değişiklik yapılması, varolan uygulamaların bozulmasına sebep olacaktır. Eğer uygulamanızda “bottleneck” yaratan bir kod varsa paralel veya async işlemleri deneyebilirsiniz. . Paralel çalışmaya gerçekten ihtiyaç duyulmadığında, kodu parallel çalıştırmaya çalışmak sadece acıya sebep olacaktır.
Threading ve Parallel Programming ile ilgili Joseph Albahari’nin materyalini okuyabilirsiniz.
0 comments:
Post a Comment