ParallelHelper sınıfı

sınıfı, ParallelHelper paralel kodla çalışmak için yüksek performanslı API'ler içerir. Belirli bir veri kümesi veya yineleme aralığı veya alanı üzerinde paralel işlemleri hızlı bir şekilde ayarlamak ve yürütmek için kullanılabilecek performans odaklı yöntemler içerir.

Platform API'leri:ParallelHelper, IAction, IAction2D, IRefAction<T>, IInAction<T>

Nasıl çalışır?

Türü ParallelHelper üç ana kavram etrafında oluşturulur:

  • Hedef yineleme aralığı üzerinde otomatik toplu işlem gerçekleştirir. Bu özellik, kullanılabilir CPU çekirdeklerinin sayısına göre çalışma birimlerinin doğru sayısını otomatik olarak zamanlar. Bu özellik, her paralel yineleme için paralel geri çağırmayı bir kez çağırma yükünü azaltır.
  • C# dilinde genel türlerin uygulanma biçiminden büyük ölçüde yararlanıyor. struct türleri, Action<T> gibi temsilciler yerine belirli arabirimleri uygulayan türleri kullanır. Bu yaklaşımı kullanarak, JIT derleyicisi kullanılan her bir geri çağırma türünü "görebilir", bu da mümkün olduğunda geri çağırmayı tamamen satır içi olarak çizebileceği anlamına gelir. Bu yaklaşım, özellikle çok küçük geri çağırmalar kullandığınızda her paralel yinelemenin yükünü büyük ölçüde azaltır. Temsilci çağırma ek yükü aksi takdirde önemsiz olur. Ayrıca, geri çağırma olarak bir struct tür kullanarak kapanışta yakalanan değişkenleri el ile işlemeniz gerekir. Bu gereksinim, this işaretçisinin örnek yöntemler veya her geri çağırmayı önemli ölçüde yavaşlatabilecek diğer değerler tarafından yanlışlıkla yakalanmasını engeller. Bu yaklaşım, gibi ImageSharpdiğer performans odaklı kitaplıklarda kullanılan yaklaşımla aynıdır.
  • Dört farklı yineleme türünü temsil eden dört api türünü kullanıma sunar: 1B ve 2B döngüler, yan etkisi olan öğeler yinelemesi ve yan etkisi olmayan öğeler yinelemesi. Eylem türlerinin her birinin, API'lere geçirdiğiniz geri çağırmalara uygulamanız gereken karşılık gelen bir interface türü struct vardır ParallelHelper. Bu arabirimler , IAction, IAction2Dve IRefAction<T>arabirimleridirIInAction<T>. Bu arabirimleri kullanarak amacını açıkça ifade eden kod yazabilirsiniz ve API'ler dahili olarak daha fazla iyileştirme gerçekleştirebilir.

Sözdizimi

float[] dizisindeki tüm öğelerini işlemek ve bunların her birini 2 ile çarpmak istediğimizi düşünelim. Bu durumda herhangi bir değişken yakalamamız gerekmez: yalnızca IRefAction<T>, interface ve ParallelHelper öğelerini kullanabiliriz ve bu öğeler her bir öğeyi geri çağrımıza otomatik olarak beslemek amacıyla yükleyecektir. Tek gereken, bir ref float bağımsız değişken alacak ve gerekli işlemi gerçekleştirecek geri çağırmamızı tanımlamaktır:

// Be sure to include this using at the top of the file:
using Microsoft.Toolkit.HighPerformance.Helpers;

// First declare the struct callback
public readonly struct ByTwoMultiplier : IRefAction<float>
{
    public void Invoke(ref float x) => x *= 2;
}

// Create an array and run the callback
float[] array = new float[10000];

ParallelHelper.ForEach<float, ByTwoMultiplier>(array);

API ile ForEach yineleme aralıklarını belirtmemiz gerekmez: ParallelHelper koleksiyonu toplu işler ve her giriş öğesini otomatik olarak işler. Ayrıca, bu özel örnekte bağımsız değişken olarak struct'u geçirmemize gerek bile yoktu: başlatılması gereken alanlar içermediğinden, ParallelHelper.ForEach türünü tür bağımsız değişkeni olarak belirtebiliriz: bu API, struct'nin kendi kendine yeni bir örneğini oluşturur ve bunu çeşitli öğeleri işlemek için kullanır.

Kapanış kavramını tanıtmak için dizi öğelerini çalışma zamanında belirtilen bir değerle çarpmak istediğimizi varsayalım. Bunu yapmak için geri struct çağırma türümüzde bu değeri "yakalamamız" gerekir. Bunu şu şekilde yapabiliriz:

public readonly struct ItemsMultiplier : IRefAction<float>
{
    private readonly float factor;

    public ItemsMultiplier(float factor)
    {
        this.factor = factor;
    }

    public void Invoke(ref float x) => x *= this.factor;
}

// ...

ParallelHelper.ForEach(array, new ItemsMultiplier(3.14f));

artık öğesinin struct bir sabit kullanmak yerine öğeleri çarpmak için kullanmak istediğimiz faktörü temsil eden bir alan içerdiğini görebiliriz. Çağırdığımızda ForEach ile ilgilendiğimiz faktörle geri çağırma türümüzün açıkça bir örneğini oluşturuyoruz. Ayrıca, bu durumda C# derleyicisi de kullanmakta olduğumuz tür bağımsız değişkenlerini otomatik olarak tanıyabilir, böylece bunları yöntem çağrısından birlikte atlayabiliriz.

Geri çağırmadan erişmemiz gereken değerler için alanlar oluşturma yaklaşımı, hangi değerleri yakalamak istediğimizi açıkça bildirmemize olanak tanır ve bu da kodun daha etkileyici olmasına yardımcı olur. Bu, bazı yerel değişkenlere erişen bir lambda işlevi veya yerel işlev bildirdiğimizde C# derleyicisinin arka planda yaptığı işlemle tamamen aynıdır.

Bu kez bir dizinin tüm öğelerini paralel olarak başlatmak için API'yi kullanan For başka bir örnek aşağıda verilmiştir. Bu kez hedef diziyi doğrudan yakaladığımıza ve yöntemimiz için geri çağırma mekanizmasında IActioninterface kullanarak geçerli paralel yineleme dizinini bağımsız değişken olarak elde ettiğimize dikkat edin.

public readonly struct ArrayInitializer : IAction
{
    private readonly int[] array;

    public ArrayInitializer(int[] array)
    {
        this.array = array;
    }

    public void Invoke(int i)
    {
        this.array[i] = i;
    }
}

// ...

ParallelHelper.For(0, array.Length, new ArrayInitializer(array));

Not

Geri çağırma türleri struct-s olduğundan, bunlar başvuruyla değil paralel çalışan her iş parçacığına kopya yoluyla geçirilir. Bu, geri çağırma türlerinde alan olarak depolanan değer türlerinin de kopyalanacağı anlamına gelir. C# derleyicisinin alanlarının değerlerini değiştirmemize izin vermemesi için geri çağırmayı struct olarak readonly işaretlemek, bu ayrıntıyı hatırlamak ve hatalardan kaçınmak için iyi bir uygulamadır. Bu yalnızca bir değer türünün örnek alanları için geçerlidir: eğer bir geri struct çağırma herhangi bir türde alan static veya başvuru alanı içeriyorsa, bu değer paralel iş parçacıkları arasında doğru şekilde paylaşılır.

Yöntemler

Bunlar, ParallelHelper tarafından sunulan ve IAction, IAction2D, IRefAction<T> ve IInAction<T> arabirimlerine karşılık gelen 4 ana API'dir. Türü ParallelHelper , yineleme aralıklarını veya giriş geri çağırma türünü belirtmenin çeşitli yollarını sunan bu yöntemler için bir dizi aşırı yükleme de sunar. For ve For2D, IAction ve IAction2D örnekleri üzerinde çalışır ve bu mekanizmalar, her paralel yinelemenin dizinleriyle doğrudan erişilemeyen bir koleksiyonla eşleştirilmesi gerekmeyen bazı paralel işler yapılması gerektiğinde kullanılması amaçlanır. ForEach aşırı yüklemeleri, IRefAction<T> ve IInAction<T> örnekleri üzerinde çalışır ve paralel yinelemeler, doğrudan dizine alınabilen bir koleksiyondaki öğelere eşlendiğinde bu yüklemeler kullanılabilir. Bu durumda ayrıca dizinleme mantığını soyutlayarak, her paralel çağrının sadece üzerinde çalışılan giriş öğesi hakkında endişelenmesi gerekir ve o öğenin nasıl alınacağı konusunda endişelenmesine gerek kalmaz.

Örnekler

Birim testlerinde daha fazla örnek bulabilirsiniz.