Aliran Data TPL vs Semafor biasa

Saya memiliki persyaratan untuk membuat proses yang terukur. Proses ini sebagian besar memiliki operasi I/O dengan beberapa operasi CPU kecil (terutama deserialisasi string). Proses menanyakan database untuk daftar url, kemudian mengambil data dari url ini, membatalkan serialisasi data yang diunduh ke objek, kemudian menyimpan beberapa data ke dalam dinamika crm dan juga ke database lain. Setelah itu saya perlu memperbarui database pertama yang urlnya diproses. Salah satu persyaratannya adalah membuat derajat paralelisme dapat dikonfigurasi.

Awalnya saya berpikir untuk mengimplementasikannya melalui serangkaian tugas dengan menunggu dan membatasi paralelisme menggunakan Semaphore - cukup sederhana. Kemudian saya membaca beberapa postingan dan jawaban di sini dari @Stephen Cleary yang merekomendasikan penggunaan TPL Dataflow dan menurut saya ini bisa menjadi kandidat yang baik. Namun saya ingin memastikan bahwa saya "memperumit" kode dengan menggunakan Dataflow untuk tujuan yang baik. Saya juga mendapat saran untuk menggunakan Metode ekstensi ForEachAsync yang juga mudah digunakan, namun saya tidak yakin apakah ini tidak akan menyebabkan overhead memori karena caranya mempartisi koleksi.

Apakah TPL Dataflow merupakan pilihan yang baik untuk skenario seperti itu? Apa yang lebih baik daripada metode Semaphore atau ForEachAsync - manfaat apa yang sebenarnya akan saya peroleh jika saya menerapkannya melalui TPL DataFlow pada setiap opsi lainnya (Semaphore/ForEachASync)?


person BornToCode    schedule 31.07.2018    source sumber
comment
Tpl Dataflow lebih baik untuk pekerjaan CPU. Untuk panggilan I/o async saya akan menggunakan Task.WhenAll dengan berbagai tugas   -  person Peter Bons    schedule 31.07.2018
comment
@PeterBons - Skenario saya sebagian besar memiliki panggilan I/O, tetapi juga sedikit pekerjaan cpu (misalnya deserialisasi konten file), saya hanya bisa mengimplementasikannya dengan semaphore, tetapi mendapat kesan bahwa saya akan mendapatkan kinerja dengan menggunakan Tpl Dataflow , tapi saya masih belum yakin saya sepenuhnya memahami manfaat Dataflow sehingga saya dapat menentukan apakah manfaat tersebut layak dilakukan karena mungkin akan membuat kode saya lebih kompleks daripada hanya menggunakan semaphore?   -  person BornToCode    schedule 31.07.2018
comment
Saya sangat tertarik untuk mendapatkan pendapat ahli tentang hal ini. Saya melakukan hal yang persis sama seperti milik Anda dan tidak dapat memutuskan antara semaphore dan TPL Dataflow. Saya cenderung menggunakan ActionBlock dengan MaxDegreeOfParallelism sebagai dapat dikonfigurasi. Dari apa yang saya pahami, TPL mengelola threadpool untuk Anda dengan cara yang efisien, tetapi ada beberapa masalah lainnya. Saya ingin membuatnya sederhana, cukup batasi jumlah tugas yang dijalankan dalam satu waktu, apakah itu yang Anda lakukan juga?   -  person Polynomial Proton    schedule 31.07.2018
comment
oh btw, Lihat jawaban ini dari @Stephen Cleary. TPL Dataflow is great, especially if you're looking to limit work in one part of a larger pipeline Namun, jika hanya ada satu tindakan untuk membatasi maka semaphore sudah cukup.   -  person Polynomial Proton    schedule 31.07.2018
comment
@TheUknown - Kabar baik, kami mendapat jawaban dari ahlinya :) Tujuan saya bukan hanya membatasi jumlah tugas tetapi juga memastikan seluruh proses selesai secepat mungkin, mengetahui bagian yang menulis ke Crm merupakan hambatan utama. Terima kasih telah memberikan referensi komentar Anda untuk jawaban yang lain, juga informatif dan sesuai dengan situasi saya.   -  person BornToCode    schedule 01.08.2018


Jawaban (2)


Proses ini sebagian besar memiliki operasi IO dengan beberapa operasi CPU kecil (terutama deserialisasi string).

Itu hanya I/O. Kecuali jika string tersebut besar, deserialisasi tidak akan layak untuk diparalelkan. Jenis pekerjaan CPU yang Anda lakukan akan hilang dalam kebisingan.

Jadi, Anda sebaiknya fokus pada asinkroni secara bersamaan.

  • SemaphoreSlim adalah pola standar untuk ini, seperti yang Anda temukan.
  • TPL Dataflow juga dapat melakukan konkurensi (baik bentuk asinkron maupun paralel).

ForEachAsync dapat mengambil beberapa bentuk; perhatikan bahwa dalam postingan blog yang Anda rujuk, ada 5 implementasi berbeda dari metode ini, yang masing-masing valid. "[T]ada banyak semantik berbeda yang mungkin untuk diulang, dan masing-masing akan menghasilkan pilihan desain dan implementasi yang berbeda." Untuk tujuan Anda (tidak menginginkan paralelisasi CPU), Anda tidak boleh mempertimbangkan yang menggunakan Task.Run atau mempartisi. Dalam dunia konkurensi asinkron, implementasi ForEachAsync apa pun hanya akan menjadi gula sintaksis yang menyembunyikan semantik mana yang diimplementasikan, itulah sebabnya saya cenderung menghindarinya.

Ini menyisakan SemaphoreSlim vs. ActionBlock. Saya biasanya menyarankan orang-orang memulai dengan SemaphoreSlim terlebih dahulu, dan mempertimbangkan untuk pindah ke TPL Dataflow jika kebutuhan mereka menjadi lebih kompleks (dengan cara yang tampaknya mereka akan mendapat manfaat dari saluran dataflow).

Misalnya, "Bagian dari persyaratannya adalah membuat derajat paralelisme dapat dikonfigurasi."

Anda dapat memulai dengan mengizinkan tingkat konkurensi - di mana hal yang dibatasi adalah satu keseluruhan operasi (mengambil data dari url, membatalkan serialisasi data yang diunduh ke objek, bertahan dalam dinamika crm dan ke database lain, dan memperbarui database pertama). Di sinilah SemaphoreSlim akan menjadi solusi sempurna.

Namun Anda mungkin memutuskan ingin memiliki beberapa tombol: katakanlah, satu tingkat konkurensi untuk berapa banyak url yang Anda unduh, dan tingkat konkurensi terpisah untuk bertahan, dan tingkat konkurensi terpisah untuk memperbarui database asli. Dan kemudian Anda juga perlu membatasi "antrian" di antara titik-titik ini: hanya sejumlah objek yang dideserialisasi dalam memori, dll. - untuk memastikan bahwa url cepat dengan database lambat tidak menyebabkan masalah dengan aplikasi Anda yang menggunakan terlalu banyak Penyimpanan. Jika ini adalah semantik yang berguna, maka Anda sudah mulai mendekati masalah dari perspektif aliran data, dan itulah poin yang mungkin lebih baik Anda gunakan dengan pustaka seperti TPL Dataflow.

person Stephen Cleary    schedule 31.07.2018
comment
Terima kasih banyak atas jawaban ini, detail dan jelas dan Anda bahkan merujuk ke semua opsi yang saya sebutkan termasuk ForEachAsync! +100 :) - person BornToCode; 01.08.2018

Berikut adalah nilai jual dari pendekatan Semaphore:

  1. Kesederhanaan

Dan inilah nilai jual dari pendekatan TPL Dataflow:

  1. Paralelisme tugas di atas paralelisme data
  2. Pemanfaatan sumber daya secara optimal (bandwidth, CPU, koneksi database)
  3. Tingkat paralelisme yang dapat dikonfigurasi untuk setiap operasi heterogen
  4. Mengurangi jejak memori

Mari kita tinjau implementasi Semaphore berikut sebagai contoh:

string[] urls = FetchUrlsFromDB();
var cts = new CancellationTokenSource();
var semaphore = new SemaphoreSlim(10); // Degree of parallelism (DOP)
Task[] tasks = urls.Select(url => Task.Run(async () =>
{
    await semaphore.WaitAsync(cts.Token);
    try
    {
        string rawData = DownloadData(url);
        var data = Deserialize(rawData);
        PersistToCRM(data);
        MarkAsCompleted(url);
    }
    finally
    {
        semaphore.Release();
    }
})).ToArray();
Task.WaitAll(tasks);

Implementasi di atas memastikan bahwa maksimal 10 url akan diproses secara bersamaan pada saat tertentu. Namun tidak akan ada koordinasi antara alur kerja paralel ini. Jadi, misalnya, sangat mungkin bahwa pada saat tertentu seluruh 10 alur kerja paralel akan mengunduh data, pada saat lain kesepuluh alur kerja tersebut akan melakukan deserialisasi data mentah, dan pada saat lain kesepuluh alur kerja tersebut akan menyimpan data ke CRM. Ini jauh dari ideal. Idealnya, Anda ingin agar kemacetan seluruh operasi, baik adaptor jaringan, CPU, atau server database, bekerja tanpa henti sepanjang waktu, dan tidak kurang dimanfaatkan (atau benar-benar menganggur) pada berbagai momen acak.

Pertimbangan lainnya adalah seberapa banyak paralelisasi yang optimal untuk setiap operasi heterogen. 10 DOP mungkin optimal untuk komunikasi dengan web, namun terlalu rendah atau terlalu tinggi untuk komunikasi dengan database. Pendekatan Semaphore tidak memungkinkan adanya tingkat penyesuaian seperti itu. Satu-satunya pilihan Anda adalah berkompromi dengan memilih nilai DOP di antara nilai optimal ini.

Jika jumlah url sangat besar, katakanlah 1.000.000, maka pendekatan Semaphore di atas juga menimbulkan pertimbangan penggunaan memori yang serius. Sebuah url mungkin memiliki ukuran rata-rata 50 byte, sedangkan Task yang terhubung ke CancellationToken mungkin 10 kali lebih berat atau lebih. Tentu saja Anda dapat mengubah penerapannya dan menggunakan SemaphoreSlim dengan cara yang lebih cerdas sehingga tidak menghasilkan banyak tugas, namun hal ini bertentangan dengan nilai jual utama (dan satu-satunya) dari pendekatan ini, yaitu kesederhanaannya.

Pustaka Aliran Data TPL memecahkan semua masalah ini, dengan mengorbankan kurva pembelajaran (yang kecil) yang diperlukan untuk dapat menjinakkan alat canggih ini.

person Theodor Zoulias    schedule 11.06.2020