Pelestarian 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, kebenaran mengharuskan urutan sumber dipertahankan; namun, pemesanan bisa mahal secara komputasi. 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 pemesanan.

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

Contoh berikut menunjukkan kueri paralel yang tidak berurutan 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 tersebut. Operator kueri PLINQ mempartisi urutan sumber ke dalam beberapa sub-urutan yang diproses sebagai tugas bersamaan. Jika pelestarian pesanan tidak ditentukan, hasil dari setiap partisi diserahkan ke tahap kueri berikutnya dalam urutan arbitrer. Selain itu, partisi dapat menghasilkan subset hasilnya sebelum terus memproses elemen yang tersisa. Urutan yang dihasilkan bisa berbeda setiap saat. Aplikasi Anda tidak dapat mengontrol ini karena tergantung pada bagaimana sistem operasi menjadwalkan alur.

Contoh berikut mengambil alih perilaku default dengan menggunakan AsOrdered operator pada urutan sumber. Ini memastikan bahwa Take metode mengembalikan 1000 kota pertama dalam urutan 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 pelestarian 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 kueri lainnya. Dengan kata lain, operator seperti OrderBy dan ThenBy diperlakukan seolah-olah mereka diikuti dengan panggilan ke AsOrdered.

Operator dan Pengurutan Kueri

Operator kueri berikut memperkenalkan pelestarian pesanan ke dalam semua operasi berikutnya dalam kueri, atau hingga 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 ini menunjukkan operator berikut.

Operator Hasil ketika urutan sumber diurutkan Hasil ketika urutan sumber tidak diurutkan
Aggregate Output nondeterministik untuk operasi nonasosiatif atau nonkommutatif Output nondeterministik untuk operasi nonasosiatif atau nonkommutatif
All Tidak berlaku Tidak berlaku
Any Tidak berlaku Tidak berlaku
AsEnumerable Tidak berlaku Tidak berlaku
Average Output nondeterministik untuk operasi nonasosiatif atau nonkommutatif Output nondeterministik untuk operasi nonasosiatif atau nonkommutatif
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 baru yang diurutkan
OrderByDescending Menyusun ulang urutan Memulai bagian baru yang diurutkan
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 (diindeks) Hasil yang diurutkan Hasil yang tidak diurutkan.
SelectMany Hasil yang diurutkan. Hasil yang tidak diurutkan
SelectMany (diindeks) 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. Nondeterministic. Melakukan SkipWhile pada urutan arbitrer saat ini
Sum Output nondeterministik untuk operasi nonasosiatif atau nonkommutatif Output nondeterministik untuk operasi nonasosiatif atau nonkommutatif
Take Mengambil elemen pertama n Mengambil elemen apa pun n
TakeWhile Hasil yang diurutkan Nondeterministic. Melakukan SkipWhile pada urutan arbitrer 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 (diindeks) Hasil yang diurutkan Hasil yang tidak diurutkan
Zip Hasil yang diurutkan Hasil yang tidak diurutkan

Hasil yang tidak diurutkan tidak secara aktif diacak; mereka tidak memiliki logika pemesanan khusus yang diterapkan padanya. Dalam beberapa kasus, kueri yang tidak berurutan dapat mempertahankan urutan sumber. Untuk kueri yang menggunakan operator Pilih terindeks, PLINQ menjamin bahwa elemen output akan keluar dalam urutan peningkatan indeks, tetapi tidak menjamin indeks mana yang akan ditetapkan ke elemen mana.

Lihat juga