2016-03-31 18 views
-2
private void GenerateRecords(JobRequest request) 
{ 
    for (var day = 0; day < daysInRange; day++) 
    { 
      foreach (var coreId in request.CoreIds) 
      { 
       foreach (var agentId in Enumerable.Range(0, request.AgentsCount).Select(x => Guid.NewGuid())) 
       { 
        for (var copiesDone = 0; copiesDone < request.CopiesToMake; copiesDone++) 
        { 
         foreach (var jobInfoRecord in request.Jobs) 
         { 
          foreach (var status in request.Statuses) 
          { 
            //DoSomeWork(); 
          } 
         } 
        } 
       } 
      } 
    } 
} 

Birçok yinelemenin performansını artırmak için herhangi bir yol var mı? Bu döngülerin hepsine gerçekten ihtiyacım var, ama (hızlanma) yinelemelerini nasıl geliştirebilirim diye merak ediyordum? Belki linq kullanarak?Birçok foreach döngüsünün performansı nasıl artırılır?

+0

Depends, her "foreach" değeriyle ne yapıyorsunuz? –

+0

Hiçbir şey, sadece yeni nesneler oluşturun – Anatoly

+0

Döngüler sorunsa, döngü açmayı kullanmayı deneyebilirsiniz. Veya her zaman iş parçacığı kullanma seçeneğiniz vardır. – Carra

cevap

4

LINQ'u kestikten sonra, koleksiyon numaratörlerinizin ve çöp toplayıcınızın merhametindesiniz. Eğer foreach döngüler dışında mümkün olduğunca performansı sıkmak istiyorum ve size veri yapıları üzerinde kontrole sahip olursa

, sen yapı enumerator'ler sahip toplama türlerini kullanmak emin olun (yani List<T>, ImmutableArray<T>). Daha da iyisi, uygun olan yerlerde genel jenerik diziler kullanın. Yapılandırılmamış bir numaralandırıcısına rağmen, en azından .NET'teki en hızlı tahsilat türüdür, en azından Enable/En iyileştirmelerde etkinleştirildiğinde (bu durumda derleyici, dizilerdeki foreach döngüleriniz için aynı IL yayarsa) for döngüleri için olduğu gibi, IEnumerable<T>'u uygulayan türleri kullanma ile ilişkilendirilen ayırma ve yöntem çağrılarını da azaltır.

Roslyn durumunuza faydalıdır sıcak kod yolları için bir set of guidelines sahiptir:

  • kaçının LINQ.
  • Yapı numaralandırıcısı olmayan koleksiyonlar üzerinde foreach kullanmaktan kaçının.

Şimdi, yukarıdaki herhangi bir performans kritik senaryoda geçerlidir. Bununla birlikte, koleksiyon yineleme performansının sizin durumunuzdaki darboğaz olduğunu biraz şüpheliyim. DoSomeWork'un istediğinden daha uzun sürmesi daha olasıdır. Hangi kod parçasının en çok dikkat edilmesi gerektiğine dair kesin cevabı almak için GenerateRecords yöntem çağrınızı izlemelisiniz.

DoSomeWork uygulamanızın en uygun olduğunu düşünüyorsanız, iş yükünüzü paralelleştirmeyi düşünün.

sizin DoSomeWork uygulama saf olduğunu ve Parallel.For veya Parallel.ForEach aracılığıyla döngü tekrarlamalar bazı parallelise mümkün olabilir harici değişken halde (yani sınıf değişkenleri) dayanmaz şartıyla. En dıştaki döngü, bunun için özellikle iyi bir aday gibi gözüküyor, ancak istenen performans özelliklerine ulaşıncaya kadar paralel döngünün yerleştirilmesi ile oynamak zorunda kalabilirsiniz. Başlangıç ​​noktası olarak burada öneriyoruz budur:

private void GenerateRecords(JobRequest request) 
{ 
    Parallel.For(0, daysInRange, day => 
    { 
     foreach (var coreId in request.CoreIds) 
     { 
      for (var i = 0; i < request.AgentsCount; i++) 
      { 
       var agentId = Guid.NewGuid(); 

       for (var copiesDone = 0; copiesDone < request.CopiesToMake; copiesDone++) 
       { 
        foreach (var jobInfoRecord in request.Jobs) 
        { 
         foreach (var status in request.Statuses) 
         { 
          //DoSomeWork(); 
         } 
        } 
       } 
      } 
     } 
    }); 
} 
+0

ilk Roslyn yönergesini seviyorum - basit ve anlaşılır :) –

1

Kaç kez //DoSomeWork() yapmanız gerekiyor? Bu israflardan biri var mı? Değilse, döngüler önemli değil.

Anlatmanın yolu, bunu Visual Studio IDE altında çalıştırın ve çalışırken "Duraklat" düğmesine basın. Ardından çağrı yığınını görüntüleyin.

//DoSomeWork()'un birkaç talimattan daha az bir süre almaması durumunda, duraklama //DoSomeWork()'a inecektir. Bunu 10 kez yaparsanız, bu işleve giren örneklerin oranı, harcadığı sürenin kabaca oranıdır. 10 örnekten 8 tanesi bu işleve ulaşırsa ve bunlardan 2 tanesi döngülere (büyük olasılıkla en içteki döngü) gelirse, döngüyü açmış olsanız veya maliyeti 0'a düşürdüyseniz bile, % 20.

Çabalarınızı harcayacağınız şey, en fazla maliyet ne olursa olsun.

1

LINQ, döngüler için "eski okul" ile karşılaştırıldığında hiçbir şeyi hızlandırmadığı doğrudur. Bunun yerine LINQ genellikle küçük bir maliyete sahiptir. Ancak, probleminize bağlı olarak (bunu daha iyi anlamak için ölçmeniz gerekir), çözümünüzü paralel hale getirerek performansı artırabilirsiniz. Ve burada LINQ kullanışlı olabilir.

var query = from day in Enumerable.Range(0, request.daysInRange) 
      from coreId in request.CoreIds 
      from agentId in Enumerable.Range(0, request.AgentsCount).Select(x => Guid.NewGuid()) 
      from copiesDone in Enumerable.Range(0, request.CopiesToMake) 
      from jobInfoRecord in request.Jobs 
      from status in request.Statuses 
      select new { 
       Day = day, 
       CoreId = coreId, 
       AgentId = agentId, 
       CopiesDone = copiesDone, 
       JobInfoRecord = jobInfoRecord, 
       Status = status 
      }; 

çemberinde yaptığını iş size Select kullanarak öğeleri yansıtabilirsiniz yeni bir nesne yaratmak olduğunu varsayarsak:

Tek IEnumerable<T> içine tüm döngüler için değiştirebilir LINQ kullanılarak oluşturulan:

:

var results = query.Select(item => /* do some work and return something */); 

Ancak, AsParallel() takıldığında hafif bir değişiklik ile kod paralel hale PLINQ kullanabilirsiniz 210

var results = query.AsParallel().Select(item => /* do some work and return something */); 

4 çekirdekli bir işleminiz varsa, yapılan işlem CPU bağlı olduğu sürece hızda yaklaşık 4 kat artış bekleyebilirsiniz.

Paralel hata kodunu ayıklamak zor olabilir, ancak hata ayıkladığınız her hangi bir sorunun, paralel olarak yürütülmekte olan kodla ilgili olmadığını varsayarak, .AsParallel()'u kaldırarak paralelleştirmeyi kolayca kapatabilirsiniz. Bu çok uygun olabilir.

+0

Sorguyu yinelemek A SorgulamaParallel()' sonuçları Diğer PLINQ yöntem çağrılarının yokluğunda * hayır * paralelleştirme ("Seç", "Nerede vb.) - bu durumda herhangi bir durum yoktur. ParallelQuery 'da" GetEnumerator "öğesini çağırdığınızda, sıralı işleme geri dönersiniz - ve hemen gerçekleşen örneğinizde. –

+0

* * *, * .AsParallel() 'çağrılarını büyük LINQ sorgunuza takabilirsiniz, bu durumda aşırı yüklenme çözünürlüğü 'Seç', 'SeçMany', 'Toplu', 'Nerede', OrderBy' ve 'ParallelEnumerable' içinde tanımlanan diğer aşırı yükler, ve aslında, paralel olarak çalışır. –

+0

@KirillShlenskiy: Geri bildirim için teşekkürler. Cevabımı güncelledim. –