PLINQ'te hızlandırma

Bu makalede, doğru sonuçları verirken mümkün olduğunca verimli olan PLINQ sorguları yazmanıza yardımcı olacak bilgiler sağlanır.

PLINQ'un birincil amacı, çok çekirdekli bilgisayarlarda sorgu temsilcilerini paralel olarak yürüterek LINQ to Objects sorgularının yürütülmesini hızlandırmaktır. PLINQ, bir kaynak koleksiyondaki her öğenin işlenmesi bağımsız olduğunda ve tek tek temsilciler arasında paylaşılan durum bulunmadığında en iyi performansı gösterir. Bu tür işlemler LINQ to Objects ve PLINQ'ta yaygındır ve genellikle "keyifli paralel" olarak adlandırılır çünkü birden çok iş parçacığında kolayca zamanlamaya olanak sağlarlar. Ancak, tüm sorgular tamamen hoş paralel işlemlerden oluşmaz. Çoğu durumda sorgu, paralelleştirilemeyen veya paralel yürütmeyi yavaşlatan bazı işleçler içerir. Tamamen keyifli bir şekilde paralel olan sorgularda bile PLINQ yine de veri kaynağını bölümlemeli ve iş parçacıklarında çalışmayı zamanlamalı ve genellikle sorgu tamamlandığında sonuçları birleştirmelidir. Tüm bu işlemler paralelleştirmenin hesaplama maliyetine eklenir; paralelleştirme eklemenin bu maliyetleri ek yük olarak adlandırılır. PLINQ sorgusunda en iyi performansı elde etmek için amaç, hoş bir şekilde paralel olan parçaları en üst düzeye çıkarmak ve ek yük gerektiren parçaları en aza indirmektir.

PLINQ Sorgu Performansını Etkileyen Faktörler

Aşağıdaki bölümlerde paralel sorgu performansını etkileyen en önemli faktörlerden bazıları listelanmaktadır. Bunlar, her durumda sorgu performansını tahmin etmek için tek başına yeterli olmayan genel deyimlerdir. Her zaman olduğu gibi, çeşitli temsili yapılandırmalara ve yüklere sahip bilgisayarlarda belirli sorguların gerçek performansını ölçmek önemlidir.

  1. Genel çalışmanın hesaplama maliyeti.

    Hız elde etmek için PLINQ sorgusunun ek yükü dengelemek için yeterince keyifli paralel çalışması olmalıdır. Çalışma, her temsilcinin hesaplama maliyetiyle kaynak koleksiyondaki öğe sayısı çarpılarak ifade edilebilir. Bir işlemin paralelleştirilebildiğini varsayarsak, işlem açısından ne kadar pahalı olursa, hızlandırma fırsatı da o kadar artar. Örneğin, bir işlevin yürütülmesi bir milisaniye sürerse, 1000 öğeden fazla sıralı sorgunun bu işlemi gerçekleştirmesi bir saniye sürerken, dört çekirdekli bir bilgisayardaki paralel sorgu yalnızca 250 milisaniye sürebilir. Bu, 750 milisaniyelik bir hız verir. İşlevin her öğe için bir saniye yürütülmesi gerekiyorsa, hız 750 saniye olur. Temsilci çok pahalıysa, PLINQ kaynak koleksiyonda yalnızca birkaç öğeyle önemli bir hız sunabilir. Buna karşılık, önemsiz temsilcileri olan küçük kaynak koleksiyonları genellikle PLINQ için iyi adaylar değildir.

    Aşağıdaki örnekte queryA, Select işlevinin çok fazla çalışma içerdiği varsayılarak PLINQ için muhtemelen iyi bir adaydır. Select deyiminde yeterli çalışma olmadığından queryB büyük olasılıkla iyi bir aday değildir ve paralelleştirme ek yükü hızlandırmanın çoğunu veya tümünü kaydırır.

    Dim queryA = From num In numberList.AsParallel()  
                 Select ExpensiveFunction(num); 'good for PLINQ  
    
    Dim queryB = From num In numberList.AsParallel()  
                 Where num Mod 2 > 0  
                 Select num; 'not as good for PLINQ  
    
    var queryA = from num in numberList.AsParallel()  
                 select ExpensiveFunction(num); //good for PLINQ  
    
    var queryB = from num in numberList.AsParallel()  
                 where num % 2 > 0  
                 select num; //not as good for PLINQ  
    
  2. Sistemdeki mantıksal çekirdek sayısı (paralellik derecesi).

    Bu nokta, önceki bölümün bariz bir kaydıdır ve keyifli paralel sorgular daha fazla çekirdeği olan makinelerde daha hızlı çalıştırılır çünkü iş daha eşzamanlı iş parçacıkları arasında bölünebilir. Genel hızlandırma miktarı, sorgunun genel çalışmasının yüzde kaçının paralelleştirilebilir olduğuna bağlıdır. Ancak, tüm sorguların sekiz çekirdekli bir bilgisayarda dört çekirdek bilgisayarla iki kat daha hızlı çalışacağını varsaymayın. Sorguları en iyi performans için ayarlarken, çeşitli sayıda çekirdeğe sahip bilgisayarlarda gerçek sonuçları ölçmek önemlidir. Bu nokta 1. noktayla ilgilidir: Daha büyük bilgi işlem kaynaklarından yararlanmak için daha büyük veri kümeleri gerekir.

  3. İşlem sayısı ve türü.

    PLINQ, kaynak dizideki öğelerin sırasını korumanın gerekli olduğu durumlar için AsOrdered işlecini sağlar. Siparişle ilişkili bir maliyet vardır, ancak bu maliyet genellikle mütevazıdır. GroupBy ve Join işlemleri de benzer şekilde ek yük oluşturur. PLINQ, kaynak koleksiyondaki öğeleri herhangi bir sırada işlemesine izin verildiğinde en iyi performansı gösterir ve hazır oldukları anda bunları bir sonraki işlece geçirir. Daha fazla bilgi için bkz . PLINQ'ta Sipariş Koruması.

  4. Sorgu yürütme biçimi.

    ToArray veya ToList'i çağırarak bir sorgunun sonuçlarını depoluyorsanız, tüm paralel iş parçacıklarının sonuçları tek veri yapısında birleştirilmelidir. Bu, kaçınılmaz bir hesaplama maliyeti içerir. Benzer şekilde, bir foreach (Visual Basic'te Her biri için) döngüsü kullanarak sonuçları yinelerseniz, çalışan iş parçacıklarının sonuçlarının numaralandırıcı iş parçacığında serileştirilmesi gerekir. Ancak yalnızca her iş parçacığının sonucuna göre bazı eylemler gerçekleştirmek istiyorsanız, bu işi birden çok iş parçacığında gerçekleştirmek için ForAll yöntemini kullanabilirsiniz.

  5. Birleştirme seçeneklerinin türü.

    PLINQ, çıktısını arabelleğe almak ve öbekler halinde ya da tüm sonuç kümesi üretildikten sonra aynı anda üretecek şekilde yapılandırılabilir ya da üretilen sonuçları tek tek akışa almak için yapılandırılabilir. Önceki sonuç, genel yürütme süresinin azalmasına, ikincisi ise sonuçta elde edilen öğeler arasındaki gecikme süresinin azalmasına neden olur. Birleştirme seçeneklerinin her zaman genel sorgu performansı üzerinde önemli bir etkisi olmasa da, bir kullanıcının sonuçları görmek için ne kadar beklemesi gerektiğini denetledikleri için algılanan performansı etkileyebilirler. Daha fazla bilgi için bkz . PLINQ'ta Birleştirme Seçenekleri.

  6. Bölümleme türü.

    Bazı durumlarda, dizine alınabilen bir kaynak koleksiyonu üzerindeki PLINQ sorgusu dengesiz bir iş yüküne neden olabilir. Bu durumda, özel bir bölümleyici oluşturarak sorgu performansını artırabilirsiniz. Daha fazla bilgi için bkz . PLINQ ve TPL için Özel Bölümleyiciler.

PLINQ Sıralı Modu Seçtiğinde

PLINQ her zaman sorguyu en az sorgunun sırayla çalıştırılacağı kadar hızlı yürütmeyi dener. PLINQ, kullanıcı temsilcilerinin hesaplama açısından ne kadar pahalı olduğuna veya giriş kaynağının ne kadar büyük olduğuna bakmasa da, belirli "şekiller" sorgusunu arar. Özellikle sorgu işleçlerini veya genellikle bir sorgunun paralel modda daha yavaş yürütülmesine neden olan işleç birleşimlerini arar. Bu tür şekiller bulduğunda, PLINQ varsayılan olarak sıralı moda geri döner.

Ancak, belirli bir sorgu performansını ölçtkten sonra, aslında paralel modda daha hızlı çalıştığını belirleyebilirsiniz. Böyle durumlarda, PLINQ'ye sorguyu ParallelExecutionMode.ForceParallelismWithExecutionMode paralelleştirmesini bildirmek için yöntemi aracılığıyla bayrağını kullanabilirsiniz. Daha fazla bilgi için bkz . Nasıl yapılır: PLINQ'ta Yürütme Modunu Belirtme.

Aşağıdaki listede PLINQ'un varsayılan olarak sıralı modda yürüteceği sorgu şekilleri açıklanmaktadır:

  • Özgün dizinleri kaldırmış veya yeniden düzenlenmiş bir sıralama veya filtreleme işlecinden sonra Select, indexed Where, indexed SelectMany veya ElementAt yan tümcesi içeren sorgular.

  • Take, TakeWhile, Skip, SkipWhile işleci içeren ve kaynak dizideki dizinlerin özgün sırada olmadığı sorgular.

  • Veri kaynaklarından birinin özgün sıralı dizini yoksa ve diğer veri kaynağı dizine alınamıyorsa (dizi veya IList(T) gibi) Zip veya SequenceEquals içeren sorgular.

  • Dizine eklenebilir veri kaynaklarına uygulanmadığı sürece Concat içeren sorgular.

  • Dizine eklenebilir bir veri kaynağına uygulanmadığı sürece Ters içeren sorgular.

Ayrıca bkz.