2012-04-13 37 views
5

Algoritimde çok büyük bir veri kümesinde çok sıkı bir döngü oluşturan bir yöntem var. Aslında orijinal olan tek iş parçacığını yazdım, ama çok uzun sürdü. Şimdi onu hızlandırmak istediğim noktaya geldim, bu yüzden şimdi çalışmaları paralel hale getirmek için ThreadPool'u kullanıyorum. Sorun şu ki, CPU kullanımımın% 95-100'e çıkmasını bekledim, ki bu da beklediğim gibi. Ancak, performansım önemli ölçüde arttı, ancak tüm bağlam geçişlerini azaltabilseydim daha iyi yapabileceğimi düşünüyorum. Bu, diğer programların CPU kaynakları için iş parçacığıyla mücadele etmeleri gerektiğinden biraz lagy olmalarına da neden oluyor.ThreadPool geri döngüde geri arama -% 100 CPU

Sorum şu: Bu işi nasıl yapacağım? Düşünebildiğim tek şey, bir seferde çalışan iş parçacığı sayısını sınırlamaktır, ancak bu algoritmamı yavaşlatır, çünkü yalnızca birkaç iş parçacığı bir seferde çalışabilir. Ben sadece algoritma sadece mümkün olduğunca çabuk tamamlamak için çalıştırmak için ihtiyacım var çünkü benim konularımda uyku eklemek istemiyorum.

DÜZENLEME: Birçok kişi TPL'yi kullandığından bahsetmiştir. Bence bu harika bir fikir ama maalesef, .NET 4 kullanarak henüz bir sürüm yayınlamadığı için .NET 3.5 kullanarak takıldığımdan bahsetmeyi unuttum. Bir IEnumerable üzerinde foreach döngüye ana uygulamayı yeniden yazabilirsiniz Eğer

+1

Hız istiyorsanız, neden tüm hız şeylerini çıkarırsınız? İçerik anahtarı işletim sistemi tarafından yapılır, bununla uğraşmazsınız ... – gbianchi

+1

Çözüm, havuzdaki iş parçacıklarının önceliğini azaltmaktır. Bu bir cevap değil çünkü verimli bir şekilde nasıl yapılacağını bilmiyorum :( –

+0

Bireysel görevleri daha büyük hale getirmeniz gerektiği gibi geliyor OTOH, iş parçacığı önerilerinizin çoğunu zaten yapacak kadar akıllı. Daha fazla göreviniz varsa CPU'lardan ziyade, daha fazla iş parçacığı başlatmak yerine onları sıraya koyacaktır –

cevap

6

Bu, kaynak yönetimi ile ilgilidir. Programınız şu anda tüm kaynakları hoplayıyor ve diğer programlar bunlara erişimi azaltıyor. “Sadece algoritmaların mümkün olan en hızlı şekilde çalışmasını sağlamak için ihtiyacım var” parçasını dengelemelisiniz. “Bu, diğer programların CPU kaynakları için iş parçacıkları ile mücadele etmek zorunda kaldığı için biraz laggy olmasına neden oluyor”. Onlar karşılıklı olarak münhasırlar; Uygulamanızın belirli bir makinede olabildiğince hızlı koşmasını ve diğer uygulamaları mükemmel şekilde yanıtlamasını sağlayamazsınız. İşlemcinin herhangi bir zaman diliminde ne kadar yapabildiğinin bir sınırı vardır.

Bildiğim kadarıyla verimlilik kazanımları olarak, yapabileceğiniz birkaç şey vardır:

  • ultra optimize dişli algoritmalarının ThreadPool'da kullanmayın. ThreadPool, "Git ve bunu yap ve bana bittiğini bildir" operasyonları için mükemmel. Bununla birlikte, optimize etmek istiyorsanız, ThreadPool (CPU ve OS'de bulunan üst kısımdaki üst kısımda) ile ek bir iş parçacığı çizelgelemesi eklemenin getirdiği ek yük önlenebilir. Ayrıca bir ThreadPool'daki iş parçacıkları üzerinde daha sınırlı bir kontrole sahip olursunuz, bu da işlem özniteliğini (yük dengelemesine) atama ve bireysel iş parçacıklarının önceliği (bir iş parçacığı daha fazla veya daha az zaman vermek için) gibi optimizasyonların mevcut olmadığı anlamına gelir.Basit Konular oluşturmayı deneyin veya birden fazla işin yapılmasını sağlayan bir dizi stratejiye sahip olan TPL'ye bakmayı deneyin (bunların hepsi ilk etapta iş parçacığı gerektirmez).

  • Evet, iş parçacığının sayısını "kısmak" isteyeceksiniz. Bu, diğer programların programın ihtiyacını azaltarak bazı işlemci zamanlarına izin vermesidir, fakat dediğim gibi, çoklu iş parçacığı içinde de içsel olarak var. Başparmak kuralı, eğer bir işlemciye “yürütme birimleri” olduğu için aktif olarak çalışan iş parçacıklarının sayısından iki kat fazla verilirse (bunlar bir işlemci yongasındaki fiziksel çekirdekler ve bir çekirdeği bölen HyperThreading teknolojisi gibi “mantıksal işlemciler” dir) ikiye ayrılırsa, işletim sistemi daha fazla zamanlama iş parçacığı geçirir ve iş parçacıklarının gerçekten çalışmasını harcayacağından aralarında geçiş yapar ("önbellekleme"). Daha genel anlamda, “ölçek ekonomileri” ne dönüşecek olan azalan geri dönüş yasası vardır; Sonuç olarak, başka bir iş parçacığı eklemek programınızın bu iş parçacığını kullanmadığınızdan daha yavaş çalışmasına neden olur. Evet, ThreadPool sizin için maksimum konuları işliyor, ancak muhtemelen kendi algoritmanızda kendinizi uygulamak için çeşitli özelliklerinin en basitidir.

  • Her bir iş parçacığının en iyi duruma getirildiğinden emin olun. Saf ya da verimsiz algoritmaları arayın (bunlara "O (Aman Tanrım)") ve onları düzenleyin. Çoğu işlemin verimliliğinin bir alt sınırı vardır (işlem türüne göre değişir) ve “erken optimizasyon tüm kötülüklerin köküdür” (kodun gerçekten işe yaraması pahasına performansı optimize etmeyin), ancak Birden çok iş parçacıklı bir ortamda, bir kez çalıştırıldığında bir algoritmanın verimliliğinde yapabileceğiniz herhangi bir kazancın, kaç kez çalıştırdığınızla çarpılacağını anlayın, böylece paralel bir işlemin etkin olduğundan emin olunmanın bir çift bonus olduğunu unutmayın.

+0

+1 sadece O (Aman Tanrım) için - büyük bir cevap ;-) – BrokenGlass

+1

'Başparmak kuralı, eğer bir CPUya aktif olarak çalışan iplerin sayısı iki kattan fazla ise "yürütme birimleri" (bunlar bir işlemci yongasındaki fiziksel çekirdekler ve bir çekirdeği ikiye ayıran HyperThreading teknolojisi gibi "mantıksal işlemciler" dir), daha sonra OS daha fazla zamanlama iş parçacığı geçirecek ve bunlar arasında geçiş yapacaktır ("önbellekleme") aslında iş parçacığı çalıştırmak harcayacak - aslında bunu denediniz mi? Yönetilmeyen kodda, 8 CPU bağlantılı iş parçacığına sahip olmanız veya 800 yapıp yapmamanız fark etmez - kabaca aynı miktarda iş yapılır. –

+0

Çekirdek i7 CPU'm varsa (4 fiziksel çekirdek + 4 sanal çekirdek), 16 iş parçacığı bu kurala göre sınırdır? –

2

Eğer döngü paralel hale PLINQ kullanabilirsiniz. Uygulamanızın kaç çekirdek kullanacağını kontrol etmek için WithDegreeOfParallelism kullanabilirsiniz. Bilgisayarınızdaki tüm çekirdekleri kullanmadan yaşadığınız bazı "gecikme" durumlarını önleyebilirsiniz. Ayrıca, gereksiz kaynak çekişmesini önlemek için iş parçacıklarınızı döngü boyunca nasıl bölümlendireceğinizle uğraşmanıza gerek yoktur. PLINQ sizin için her şeyi yapar.

var arrayOfStuff = new[] { ... }; 
for (var i = 0; i < arrayOfStuff.Length; ++i) 
    DoSomething(arrayOfStuff[i]); 

sipariş sen PLINQ az bir çekirdek kullanan kullanarak parallelize olabilir önemli değil ise geçerli: Hatta

var cores = Math.Max(1, Environment.ProcessorCount - 1); 
arrayOfStuff.AsParallel().WithDegreeOfParallelism(cores).ForAll(DoSomething); 

bu çok basit tek iş parçacıklı döngü var varsayarsak Ana döngüsünüz daha karmaşıksa, bunu paralelleştirebileceğiniz bir yineleyici bloğuna yeniden yazabilirsiniz:

IEnumerable<Stuff> GetStuff() { 
    for (... very complex looping ...) { 
    ... 
    yield return stuff; 
    } 
}