Hasil
Tujuannya adalah untuk menghitung semua kemungkinan bentuk poliform dari sejumlah kotak tertentu. Karena ini adalah perhitungan yang sangat berat untuk jumlah yang lebih besar, saya ingin menggunakan banyak inti yang dimiliki komputer saya.
Masalah
Saya membuat masalah lebih mudah untuk dijelaskan dan diuji dengan membuat skenario berikut:
1) for each value of 2, 3, 5, and 7:
2) find all multiples (up to a certain value) and add them to the same List
3) remove all duplicates from said list
Dalam program terakhir saya, langkah 2 jauh lebih luas dan berat secara komputasi, oleh karena itu saya lebih memilih untuk membagi tugas dua dalam banyak nilai yang ingin saya periksa berdasarkan nilai pada langkah 1.
Apa yang Saya Coba
Saya membuat aplikasi winforms dengan C# Core dengan 5 tombol mencoba berbagai variasi paralelisme yang saya temukan di sini di Stackoverflow dan tempat lain di internet:
Berikut kodenya (kelihatannya banyak, tapi hanya 5 variasi dari ide yang sama), semuanya memberikan hitungan untuk memeriksa apakah menghasilkan hasil yang sama + berapa lama waktu yang dibutuhkan:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Parallelism
{
public partial class Form1 : Form
{
private readonly int Repeat = 10000000;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var watch = System.Diagnostics.Stopwatch.StartNew();
List<int> output = new List<int>();
foreach (int x in new int[] { 2, 3, 5, 7 })
{
for (int i = 0; i < Repeat; i++)
{
output.Add(x * i);
}
}
output = output.Distinct().ToList();
watch.Stop();
(sender as Button).Text += $", c:{output.Count} - {watch.ElapsedMilliseconds}ms";
}
private void button2_Click(object sender, EventArgs e)
{
var watch = System.Diagnostics.Stopwatch.StartNew();
ConcurrentBag<int> output = new ConcurrentBag<int>();
Task task = Task.WhenAll(
Task.Run(() => button2_Calculation(2, output)),
Task.Run(() => button2_Calculation(3, output)),
Task.Run(() => button2_Calculation(5, output)),
Task.Run(() => button2_Calculation(7, output))
);
task.Wait();
HashSet<int> output2 = new HashSet<int>(output);
watch.Stop();
(sender as Button).Text += $", c:{output2.Count} - {watch.ElapsedMilliseconds}ms";
}
private void button2_Calculation(int x, ConcurrentBag<int> output)
{
for (int i = 0; i < Repeat; i++)
{
output.Add(x * i);
}
}
private void button3_Click(object sender, EventArgs e)
{
var watch = System.Diagnostics.Stopwatch.StartNew();
List<int> output = new List<int>();
foreach (int x in (new int[] { 2, 3, 5, 7 }).AsParallel())
{
for (int i = 0; i < Repeat; i++)
{
output.Add(x * i);
}
}
output = output.Distinct().ToList();
watch.Stop();
(sender as Button).Text += $", c:{output.Count} - {watch.ElapsedMilliseconds}ms";
}
private void button4_Click(object sender, EventArgs e)
{
var watch = System.Diagnostics.Stopwatch.StartNew();
ConcurrentBag<int> output = new ConcurrentBag<int>();
Dictionary<int, Task> runningTasks = new Dictionary<int, Task>();
foreach (int x in new int[] { 2, 3, 5, 7 })
{
int value = x;
runningTasks.Add(x, Task.Factory.StartNew(() => button2_Calculation(value, output)));
}
foreach (Task t in runningTasks.Select(c => c.Value))
t.Wait();
HashSet<int> output2 = new HashSet<int>(output);
watch.Stop();
(sender as Button).Text += $", c:{output2.Count} - {watch.ElapsedMilliseconds}ms";
}
private void button5_Click(object sender, EventArgs e)
{
var watch = System.Diagnostics.Stopwatch.StartNew();
ConcurrentBag<int> output = new ConcurrentBag<int>();
Parallel.ForEach(new int[] { 2, 3, 5, 7 }, x => button5_Calculation(x, output));
HashSet<int> output2 = new HashSet<int>(output);
watch.Stop();
(sender as Button).Text += $", c:{output2.Count} - {watch.ElapsedMilliseconds}ms";
}
private void button5_Calculation(int x, ConcurrentBag<int> output)
{
for (int i = 0; i < Repeat; i++)
output.Add(x * i);
}
}
}
Hasil Sejauh Ini
Sejauh ini semua metode di atas menghasilkan durasi yang sama antara 1s - 1,5s. Sebenarnya, terkadang eksekusi serial normal tampak jauh lebih cepat. Bagaimana ini mungkin? Saya berharap dengan 8 core (16 core virtual) pembagian tugas akan menghasilkan kecepatan keseluruhan yang lebih cepat?
Bantuan apa pun sangat dihargai!
Masa depan
Setelah mempelajari lebih lanjut tentang cara menerapkan paralelisme dengan benar, saya berharap juga menjalankan keseluruhan perhitungan di thread lain/Async agar GUI tetap responsif.
Sunting:
Tanggapan ke @Pac0: Ini implementasi saya atas saran Anda. Tampaknya tidak ada banyak perbedaan:
private void button6_Click(object sender, EventArgs e)
{
var watch = System.Diagnostics.Stopwatch.StartNew();
ConcurrentBag<HashSet<int>> bag = new ConcurrentBag<HashSet<int>>();
var output = Parallel.ForEach(new int[] { 2, 3, 5, 7 }, x =>
{
HashSet<int> temp = new HashSet<int>();
for (int i = 0; i < Repeat; i++)
temp.Add(x * i);
bag.Add(temp);
});
HashSet<int> output2 = new HashSet<int>();
foreach (var hash in bag)
output2.UnionWith(hash);
watch.Stop();
(sender as Button).Text += $", c:{output2.Count} - {watch.ElapsedMilliseconds}ms";
}
Repeat = 100000000
dan melihat hasilnya? - person Sowmyadhar Gourishetty   schedule 20.08.2020HashSet
per perhitungan paralel, lalu setelah semuanya selesai, lakukan penyatuan hashset (yang akan menangani duplikat). - person Pac0   schedule 20.08.2020for
:for (int i = 0; i < Repeat; i++)
. Bagaimana Anda memastikan bahwa masalahnya ada, dan bukan pada kode yang menghapus duplikat? - person Joshua Robinson   schedule 20.08.2020