2009-11-28 20 views
7

G/Ç bağımlı görevim var. WithDegreeOfParallelism = 10 ve WithExecution = ForceParallelism modunu kullanıyorum, ancak yine de sorgu yalnızca iki iş parçacığı kullanıyor. Niye ya?PLINQ neden sadece iki iş parçacığı kullanıyor?

PLINQ'nin genellikle çekirdek sayımla eşdeğer bir paralellik derecesi seçeceğini anlıyorum, ancak neden daha yüksek bir paralellik için özel isteğimi görmezden geliyor? Eğer bir olsaydı

static void Main(string[] args) 
{ 
    TestParallel(0.UpTo(8)); 
} 

private static void TestParallel(IEnumerable<int> input) 
{ 
    var timer = new Stopwatch(); 
    timer.Start(); 
    var size = input.Count(); 

    if (input.AsParallel(). 
     WithDegreeOfParallelism(10). 
     WithExecutionMode(ParallelExecutionMode.ForceParallelism). 
     Where(IsOdd).Count() != size/2) 
     throw new Exception("Failed to count the odds"); 

    timer.Stop(); 
    Console.WriteLine("Tested " + size + " numbers in " + timer.Elapsed.TotalSeconds + " seconds"); 
} 

private static bool IsOdd(int n) 
{ 
    Thread.Sleep(1000); 
    return n%2 == 1; 
} 
+2

Kaç tane işlemci/çekirdek var? – LukeH

+2

İki. Ama özellikle paralellik derecesini 10 olarak belirttim. – ripper234

+0

G/Ç bağlı bir göreviniz varsa ve bunu paralel olarak birden fazla iş parçacığında çalıştırırsanız hızı artırırsa, muhtemelen ilk etapta G/Ç bağlı değil. sadece kötü yazılmıştır (örneğin, eşzamanlılık yerine uyumsuzluk okur). –

cevap

8

PLINQ buradaki bilgileri sadece cpu üzerinde 2 çekirdek varsa, mümkün olduğunca çabuk yapmak istediğim gerçekleştirmek için Uygun thread sayısına bulmaya çalışır, bu sayı büyük olasılıkla 2'dir Dört çekirdekli, 4 başlığın göründüğünü görmeniz daha muhtemeldir, ancak çift çekirdekli bir makinede 4 iş parçacığı oluşturmak, yalnızca 2 iş parçacığı aynı anda etkin olabileceğinden performansı gerçekten iyileştirmez. Ayrıca, IO tabanlı işlemlerde, herhangi bir fazla iş parçacığının gerçekleştirilen ilk IO işlemi üzerinde basit bir şekilde bloke olması muhtemeldir.

+4

Gerçekten de sorumu cevaplamıyor - özellikle bir paralellik derecesi = 10 talep etsem bile neden iki iş parçacığı kullanmayı tercih ediyor? (Güncel soru) – ripper234

+3

@ ripper234: MSDN belgelerinden: "Paralellik derecesi, sorguyu işlemek için kullanılacak eşzamanlı yürütme ** sayısı ** 'dir." WithDegreeOfParallelism', sadece PLINQ’nin * * * * * * * * artık kullanmaması gereken bir ipucudur. http://msdn.microsoft.com/en-us/library/dd383719%28VS.100%29.aspx – LukeH

+3

Yani, IO-bağlı görevler için PLINQ'yi etkin bir şekilde kullanmanın bir yolu yok mu? – ripper234

4

sorguda kullanımı paralellik derecesini ayarlar maksimumdur. Paralellik derecesi , eşzamanlı olarak maksimum sayısıdır, sorgusu için kullanılacak görevleri yürütür. Buradan

:

MSDN

+0

Varsayılan olarak, PLINQ ana bilgisayardaki tüm işlemcileri en fazla ** 64 ** değerine kadar kullanır. PLINQ ile WithDegreeOfParallelism (Of TSource) yöntemini kullanarak belirli sayıda işlemci kullanamazsınız. http://msdn.microsoft.com/en-us/library/dd383719.aspx –

2

O PLINQ parçacığı sayısını ayarlamaktadır görünür. Yukarıdaki kodu bir süre (true) döngüsüne sardığımda, ilk iki yinelemenin çalışması iki saniye sürdü, ancak üçüncü ve üstü sadece bir saniye sürdü. PLINQ, çekirdeklerin boş olduğunu ve iplik sayısını artırdığını anladı. Etkileyici!

+1

Bunun olması için, gerçekten WithDegreeOfParallelism'i belirtmeniz gerektiğini unutmayın, aksi takdirde PLINQ makinenizdeki çekirdek sayısını sınırlar. – ripper234

0

IO hariç Rory'ye katılıyorum. Disk IO ile test edilmedi, ancak ağ IO, CPU üzerinde çekirdekler olduğundan daha fazla iş parçacığı ile daha etkili olabilir.

Basit testi (ağ hızı sabit değildir gibi birkaç kez saymak her iş parçacığı ile testi çalıştırmak için daha doğru olacaktır, ama yine de) olduğunu kanıtlamak için: CDN dan 500x500px görüntü ile

[Test] 
    public void TestDownloadThreadsImpactToSpeed() 
    { 
     var sampleImages = Enumerable.Range(0, 100) 
      .Select(x => "url to some quite large file from good server which does not have anti DSS stuff.") 
      .ToArray();    

     for (int i = 0; i < 8; i++) 
     { 
      var start = DateTime.Now; 
      var threadCount = (int)Math.Pow(2, i); 
      Parallel.For(0, sampleImages.Length - 1, new ParallelOptions {MaxDegreeOfParallelism = threadCount}, 
         index => 
          { 
           using (var webClient = new WebClient()) 
           { 
            webClient.DownloadFile(sampleImages[index], 
                  string.Format(@"c:\test\{0}", index)); 
           } 
          }); 

      Console.WriteLine("Number of threads: {0}, Seconds: {1}", threadCount, (DateTime.Now - start).TotalSeconds); 
     } 
    } 

Sonucu SSD ile 8 çekirdekli makinesi kullanılarak: 1 olarak, Saniye: parçacığı 25,3904522
sayısı: 2, saniye: parçacığı

Sayı 4, Saniye: 9,9325681parçacığı 10,8986233
sayısı parçacığı sayısı: 8, saniye: 3,7352137
parçacığı sayısı: 16, Saniye: parçacığı 3,3071892
sayısı: 32, saniye: 3,1421797
parçacığı sayısı: 64, saniye: 3,1161782
parçacığı sayısı: 128, Saniye: 3,7272132

Son sonuç bu konuda biz yalnızca 100 görüntü :) 8-64 konuları büyük değil kullanılarak

zaman farklılıkları indirmek zorunda çünkü ben öncelikle düşünmek böyle bir zaman, ama 8 çekirdekli makine.Eğer 2 çekirdekli makine (ucuz endüzör dizüstü bilgisayar) olsaydı, 8 iş parçacığı kullanmak için zorlamak 8 iş parçacığı 64 iş parçacığı kullanmak için zorlama daha fazla etkiye sahip olacağını düşünüyorum.

+0

Bu sayıları ortalama 10,000 yineleme mi saydınız? – ChrisF

+0

Daha önce değindim, her bir iplik sayısı ile testi birkaç kez çalıştırmak daha doğru olacaktır. Her neyse, nokta, ağ IO yapıyorsanız, düşük cpu sayısı olan makineler için daha fazla iş parçacığı zorlamaktır. – Giedrius

+0

Paralel seçenekler> = 8 için göz ardı ediliyor gibi görünüyor. Paralel gövdede bazı hata ayıklama çıktısı ekleyin ve sanırım bir seferde sadece en fazla 8'in çalışmakta olduğunu ve bunun daraldığını göreceksiniz. – crokusek

İlgili konular