Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
ParallelHelper berisi API performa tinggi untuk bekerja dengan kode paralel. Ini berisi metode berorientasi performa yang dapat digunakan untuk menyiapkan dan menjalankan operasi paralel dengan cepat melalui himpunan data atau rentang atau area perulangan tertentu.
API Platform:
ParallelHelper,IAction,IAction2D,IRefAction<T>,IInAction<T>
Cara kerjanya
ParallelHelper jenis dibangun di sekitar tiga konsep utama:
- Ini melakukan batching otomatis di atas rentang iterasi target. Ini berarti bahwa ia secara otomatis menjadwalkan jumlah unit kerja yang tepat berdasarkan jumlah inti CPU yang tersedia. Ini dilakukan untuk mengurangi overhead pemanggilan panggilan balik paralel sekali untuk setiap perulangan paralel tunggal.
- Ini sangat memanfaatkan cara jenis generik diimplementasikan dalam C#, dan menggunakan
structjenis yang mengimplementasikan antarmuka tertentu alih-alih delegasi sepertiAction<T>. Ini dilakukan sehingga pengkompilasi JIT akan dapat "melihat" setiap jenis panggilan balik individu yang digunakan, yang memungkinkannya untuk menginline callback sepenuhnya, jika memungkinkan. Ini dapat sangat mengurangi overhead dari setiap iterasi paralel, terutama ketika menggunakan panggilan balik yang sangat kecil, yang akan memiliki biaya sepele sehubungan dengan pemanggilan delegasi saja. Selain itu, menggunakanstructjenis sebagai panggilan balik mengharuskan pengembang untuk menangani variabel yang ditangkap secara manual dalam penutupan, yang mencegah penangkapanthispointer yang tidak disengaja dari metode instans dan nilai lain yang dapat sangat memperlambat setiap pemanggilan panggilan balik. Ini adalah pendekatan yang sama yang digunakan di pustaka berorientasi performa lainnya sepertiImageSharp. - Ini mengekspos 4 jenis API yang mewakili 4 jenis iterasi yang berbeda: perulangan 1D dan 2D, perulangan item dengan efek samping dan perulangan item tanpa efek samping. Setiap jenis tindakan memiliki jenis yang sesuai
interfaceyang perlu diterapkan kestructpanggilan balik yang diteruskan keParallelHelperAPI: ini adalahIAction,IAction2D,IRefAction<T>danIInAction<T><T>. Ini membantu pengembang untuk menulis kode yang lebih jelas mengenai niatnya, dan memungkinkan API untuk melakukan pengoptimalan lebih lanjut secara internal.
Sintaks
Katakanlah kita tertarik untuk memproses semua item dalam beberapa float[] array, dan untuk mengalikan masing-masing dengan 2. Dalam hal ini, kita tidak perlu mengambil variabel apa pun: kita hanya dapat menggunakan IRefAction<T>interface dan ParallelHelper akan memuat setiap item untuk disalurkan ke panggilan balik secara otomatis. Yang diperlukan adalah mendefinisikan panggilan balik kami, yang akan menerima ref float argumen dan melakukan operasi yang diperlukan:
// 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);
ForEach Dengan API, kita tidak perlu menentukan rentang iterasi: ParallelHelper akan mengumpulkan koleksi dan memproses setiap item input secara otomatis. Selain itu, dalam contoh spesifik ini kami bahkan tidak perlu meneruskan kami struct sebagai argumen: karena tidak berisi bidang apa pun yang perlu kami inisialisasi, kami hanya dapat menentukan jenisnya sebagai argumen jenis saat memanggil ParallelHelper.ForEach: API tersebut kemudian akan membuat instans baru itu struct sendiri, dan menggunakannya untuk memproses berbagai item.
Untuk memperkenalkan konsep penutupan, misalkan kita ingin mengalikan elemen array dengan nilai yang ditentukan saat runtime. Untuk melakukannya, kita perlu "menangkap" nilai tersebut dalam jenis panggilan balik struct kita. Kita bisa melakukannya seperti itu:
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));
Kita dapat melihat bahwa struct sekarang berisi bidang yang mewakili faktor yang ingin kita gunakan untuk mengalikan elemen, alih-alih menggunakan konstanta. Dan saat memanggil ForEach, kami secara eksplisit membuat instans jenis panggilan balik kami, dengan faktor yang kami minati. Selain itu, dalam hal ini pengkompilasi C# juga dapat secara otomatis mengenali argumen jenis yang kita gunakan, sehingga kita dapat menghilangkannya bersama-sama dari pemanggilan metode.
Pendekatan membuat bidang untuk nilai yang perlu kita akses dari panggilan balik ini memungkinkan kita secara eksplisit menyatakan nilai apa yang ingin kita tangkap, yang membantu membuat kode lebih ekspresif. Ini adalah hal yang sama persis dengan yang dilakukan pengkompilasi C# di belakang layar ketika kita mendeklarasikan fungsi lambda atau fungsi lokal yang mengakses beberapa variabel lokal juga.
Berikut adalah contoh lain, kali ini menggunakan For API untuk menginisialisasi semua item array secara paralel. Perhatikan bagaimana kali ini kami menangkap array target secara langsung, dan kami menggunakan IActioninterface untuk panggilan balik kami, yang memberikan metode kami indeks perulangan paralel saat ini sebagai argumen:
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));
Catatan
Karena jenis panggilan balik adalah struct-s, mereka diteruskan oleh salin ke setiap utas yang berjalan paralel, bukan dengan referensi. Ini berarti bahwa jenis nilai yang disimpan sebagai bidang dalam jenis panggilan balik juga akan disalin. Praktik yang baik untuk mengingat detail itu dan menghindari kesalahan adalah menandai panggilan balik struct sebagai readonly, sehingga pengkompilasi C# tidak akan membiarkan kita memodifikasi nilai bidangnya. Ini hanya berlaku untuk bidang instans dari jenis nilai: jika panggilan balik struct memiliki static bidang jenis apa pun, atau bidang referensi, nilai tersebut akan dibagikan dengan benar di antara utas paralel.
Metode
Ini adalah 4 API utama yang diekspos oleh ParallelHelper, sesuai dengan IActionantarmuka , , IAction2DIRefAction<T> dan IInAction<T> . Jenis ini ParallelHelper juga mengekspos sejumlah kelebihan beban untuk metode ini, yang menawarkan sejumlah cara untuk menentukan rentang perulangan, atau jenis panggilan balik input.
For dan For2D bekerja pada IActionIAction2D dan instans, dan mereka dimaksudkan untuk digunakan ketika beberapa pekerjaan paralel perlu dilakukan yang tidak perlu memetakan ke koleksi yang mendasar yang dapat langsung diakses dengan indeks dari setiap iterasi paralel. Kelebihan ForEach beban sebagai gantinya wotk pada IRefAction<T> instans dan IInAction<T> , dan mereka dapat digunakan ketika iterasi paralel langsung memetakan ke item dalam koleksi yang dapat diindeks secara langsung. Dalam hal ini mereka juga mengabstraksi logika pengindeksan, sehingga setiap pemanggilan paralel hanya perlu khawatir pada item input untuk dikerjakan, dan bukan tentang cara mengambil item tersebut juga.
Contoh
Anda dapat menemukan lebih banyak contoh dalam pengujian unit.
.NET Community Toolkit