Preservasi Pesanan di PLINQ

Dalam PLINQ, tujuannya adalah untuk memaksimalkan performa sambil mempertahankan kebenaran. Kueri harus berjalan secepat mungkin tetapi masih menghasilkan hasil yang benar. Dalam beberapa kasus, ketepatan memerlukan urutan sumber untuk dipertahankan; namun, pengurutan bisa memerlukan biaya komputasi tinggi. Oleh karena itu, secara default, PLINQ tidak mempertahankan urutan sumber. Dalam hal ini, PLINQ menyerupai LINQ ke SQL, tetapi tidak seperti LINQ ke Objek, yang mempertahankan urutan.

Untuk mengganti perilaku default, Anda dapat mengaktifkan pelestarian urutan dengan menggunakan operator AsOrdered pada rangkaian sumber. Anda kemudian dapat menonaktifkan pelestarian pesanan nanti dalam kueri dengan menggunakan metode AsUnordered. Dengan kedua metode, kueri diproses berdasarkan heuristik yang menentukan apakah akan menjalankan kueri sebagai paralel atau berurutan. Untuk informasi selengkapnya, lihat Memahami Speedup di PLINQ.

Contoh berikut menunjukkan kueri paralel yang tidak diurutkan yang memfilter semua elemen yang cocok dengan kondisi, tanpa mencoba mengurutkan hasilnya dengan cara apa pun.

var cityQuery =
    (from city in cities.AsParallel()
     where city.Population > 10000
     select city).Take(1000);
Dim cityQuery = From city In cities.AsParallel()
                Where city.Population > 10000
                Take (1000)

Kueri ini tidak selalu menghasilkan 1000 kota pertama dalam urutan sumber yang memenuhi kondisi, melainkan beberapa set 1000 kota yang memenuhi kondisi. Operator kueri PLINQ mempartisi urutan sumber ke dalam beberapa urutan berikutnya yang diproses sebagai tugas bersamaan. Jika retensi urutan tidak ditentukan, hasil dari setiap partisi diserahkan ke tahap berikutnya dari kueri dalam urutan sembarang. Selain itu, partisi dapat menghasilkan subset hasilnya sebelum terus memproses elemen yang tersisa. Urutan yang dihasilkan mungkin berbeda setiap saat. Aplikasi Anda tidak dapat mengontrol ini karena tergantung pada cara sistem operasi menjadwalkan thread.

Contoh berikut mengambil alih perilaku default dengan menggunakan AsOrdered operator pada urutan sumber. Ini memastikan bahwa metode Take mengembalikan 1000 kota pertama dalam deretan sumber yang memenuhi kondisi.

var orderedCities =
    (from city in cities.AsParallel().AsOrdered()
     where city.Population > 10000
     select city).Take(1000);

Dim orderedCities = From city In cities.AsParallel().AsOrdered()
                    Where city.Population > 10000
                    Take (1000)

Namun, kueri ini mungkin tidak berjalan secepat versi yang tidak diurutkan karena harus melacak urutan asli di seluruh partisi dan pada waktu penggabungan memastikan bahwa pengurutan konsisten. Oleh karena itu, kami sarankan Anda hanya menggunakan AsOrdered saat diperlukan, dan hanya untuk bagian kueri yang memerlukannya. Ketika preservasi pesanan tidak lagi diperlukan, gunakan AsUnordered untuk menonaktifkannya. Contoh berikut mencapai ini dengan menyusun dua kueri.

var orderedCities2 =
    (from city in cities.AsParallel().AsOrdered()
     where city.Population > 10000
     select city).Take(1000);

var finalResult =
    from city in orderedCities2.AsUnordered()
    join p in people.AsParallel()
    on city.Name equals p.CityName into details
    from c in details
    select new
    {
        city.Name,
        Pop = city.Population,
        c.Mayor
    };

foreach (var city in finalResult) { /*...*/ }
Dim orderedCities2 = From city In cities.AsParallel().AsOrdered()
                     Where city.Population > 10000
                     Select city
                     Take (1000)

Dim finalResult = From city In orderedCities2.AsUnordered()
                  Join p In people.AsParallel() On city.Name Equals p.CityName
                  Select New With {.Name = city.Name, .Pop = city.Population, .Mayor = city.Mayor}

For Each city In finalResult
    Console.WriteLine(city.Name & ":" & city.Pop & ":" & city.Mayor)
Next

Perhatikan bahwa PLINQ mempertahankan urutan yang dihasilkan oleh operator yang memberlakukan pesanan untuk sisa kueri. Dengan kata lain, operator seperti OrderBy dan ThenBy diperlakukan seolah-olah mereka diikuti oleh panggilan ke AsOrdered.

Operator dan Penyusunan Kueri

Operator kueri berikut memperkenalkan pelestarian pesanan ke dalam semua operasi berikutnya dalam kueri, atau sampai AsUnordered dipanggil:

Operator kueri PLINQ berikut mungkin dalam beberapa kasus memerlukan urutan sumber yang diurutkan untuk menghasilkan hasil yang benar:

Beberapa operator kueri PLINQ bersifat berbeda, tergantung pada apakah urutan sumbernya diurutkan atau tidak diurutkan. Tabel berikut mencantumkan operator ini.

Pengoperasi Hasil ketika urutan sumber diurutkan Hasil ketika urutan sumber tidak diurutkan
Aggregate Output nondeterministik untuk operasi nonasosiatif atau nonkomutatif Output nondeterministik untuk operasi nonasosiatif atau nonkomutatif
All Tidak berlaku Tidak berlaku
Any Tidak berlaku Tidak berlaku
AsEnumerable Tidak berlaku Tidak berlaku
Average Output nondeterministik untuk operasi nonasosiatif atau nonkomutatif Output nondeterministik untuk operasi nonasosiatif atau nonkomutatif
Cast Hasil yang diurutkan Hasil yang tidak diurutkan
Concat Hasil yang diurutkan Hasil yang tidak diurutkan
Count Tidak berlaku Tidak berlaku
DefaultIfEmpty Tidak berlaku Tidak berlaku
Distinct Hasil yang diurutkan Hasil yang tidak diurutkan
ElementAt Mengembalikan elemen yang ditentukan Elemen arbitrer
ElementAtOrDefault Mengembalikan elemen yang ditentukan Elemen arbitrer
Except Hasil yang tidak diurutkan Hasil yang tidak diurutkan
First Mengembalikan elemen yang ditentukan Elemen arbitrer
FirstOrDefault Mengembalikan elemen yang ditentukan Elemen arbitrer
ForAll Menjalankan secara nondeterministik secara paralel Menjalankan secara nondeterministik secara paralel
GroupBy Hasil yang diurutkan Hasil yang tidak diurutkan
GroupJoin Hasil yang diurutkan Hasil yang tidak diurutkan
Intersect Hasil yang diurutkan Hasil yang tidak diurutkan
Join Hasil yang diurutkan Hasil yang tidak diurutkan
Last Mengembalikan elemen yang ditentukan Elemen arbitrer
LastOrDefault Mengembalikan elemen yang ditentukan Elemen arbitrer
LongCount Tidak berlaku Tidak berlaku
Min Tidak berlaku Tidak berlaku
OrderBy Menyusun ulang urutan Memulai bagian berurutan baru
OrderByDescending Menyusun ulang urutan Memulai bagian berurutan baru
Range Tidak berlaku (default yang sama dengan AsParallel ) Tidak berlaku
Repeat Tidak berlaku (default yang sama dengan AsParallel) Tidak berlaku
Reverse Membalikkan Tidak melakukan apa-apa
Select Hasil yang diurutkan Hasil yang tidak diurutkan
Select (terindeks) Hasil yang diurutkan Hasil yang tidak diurutkan.
SelectMany Hasil yang diurutkan. Hasil yang tidak diurutkan
SelectMany (terindeks) Hasil yang diurutkan. Hasil yang tidak diurutkan.
SequenceEqual Perbandingan yang diurutkan Perbandingan yang tidak diurutkan
Single Tidak berlaku Tidak berlaku
SingleOrDefault Tidak berlaku Tidak berlaku
Skip Melompati elemen n pertama Melompati elemen n apa pun
SkipWhile Hasil yang diurutkan. Nondeterministik. Melakukan fungsi SkipWhile pada urutan acak saat ini
Sum Output nondeterministik untuk operasi nonasosiatif atau nonkomutatif Output nondeterministik untuk operasi nonasosiatif atau nonkomutatif
Take Mengambil elemen pertama n Mengambil elemen apa pun n
TakeWhile Hasil yang diurutkan Nondeterministik. Menggunakan fungsi TakeWhile pada urutan acak saat ini
ThenBy Suplemen OrderBy Suplemen OrderBy
ThenByDescending Suplemen OrderBy Suplemen OrderBy
ToArray Hasil yang diurutkan Hasil yang tidak diurutkan
ToDictionary Tidak berlaku Tidak berlaku
ToList Hasil yang diurutkan Hasil yang tidak diurutkan
ToLookup Hasil yang diurutkan Hasil yang tidak diurutkan
Union Hasil yang diurutkan Hasil yang tidak diurutkan
Where Hasil yang diurutkan Hasil yang tidak diurutkan
Where (terindeks) Hasil yang diurutkan Hasil yang tidak diurutkan
Zip Hasil yang diurutkan Hasil yang tidak diurutkan

Hasil tanpa urutan tidak diacak aktif; tidak menerapkan logika pengurutan khusus. Dalam beberapa kasus, kueri yang tidak diurutkan dapat mempertahankan urutan sumber. Untuk kueri yang menggunakan operator Pilih terindeks, PLINQ menjamin bahwa elemen output akan keluar dalam urutan peningkatan indeks, tetapi tidak membuat jaminan tentang indeks mana yang akan ditetapkan ke elemen mana.

Lihat juga