【发布时间】:2020-10-09 00:43:22
【问题描述】:
我正在用 C# 并行化一个应用程序,并且正在测试使用隐式线程与显式线程之间的性能差异。两种技术都使用System.Threading 库,隐式线程的特点是使用Parallel.For 循环,而显式线程涉及创建、启动和加入线程,同时还计算块大小、调用工作函数等。
我发现通过在 8 个内核上使用显式线程(经过 50 次试验后速度提高了大约 1.2 倍),我可以比程序的原始顺序版本实现更好的加速。我了解这两种技术之间的潜在差异,但是,我不确定为什么显式版本似乎更快。我认为也许隐式版本会更快,因为任务会自动安排,而不是手动任务和线程创建。是否有理由(除了我的结果中可能存在错误)显式版本会更快?
作为参考,下面可以看到相关代码的总结版本。
float[][] stft_implicit(Complex[] x, int wSamp)
{
//...
Parallel.For(0, size, new ParallelOptions { MaxDegreeOfParallelism = MainWindow.NUM_THREADS }, ii =>
{
Complex[] tempFFT = IterativeFFT.FFT(all_temps[ii], twiddles, wSamp);
fft_results[ii] = tempFFT;
});
//...
}
float[][] stft_explicit(Complex[] x, int wSamp)
{
//...
length = (int)(2 * Math.Floor((double)N / (double)wSamp) - 1);
chunk_size = (length + MainWindow.NUM_THREADS - 1) / MainWindow.NUM_THREADS;
Thread[] threads = new Thread[MainWindow.NUM_THREADS];
for (int i = 0; i < MainWindow.NUM_THREADS; i++)
{
threads[i] = new Thread(fft_worker);
threads[i].Start(i);
}
for (int i = 0; i < MainWindow.NUM_THREADS; i++)
{
threads[i].Join();
}
//...
}
public void fft_worker(object thread_id)
{
int ID = (int)thread_id;
Complex[] temp = new Complex[wSamp];
Complex[] tempFFT = new Complex[wSamp];
int start = ID * chunk_size;
int end = Math.Min(start + chunk_size, length);
for (int ii = start; ii < end; ii++)
{
//...
tempFFT = IterativeFFT.FFT(temp, twiddles, wSamp);
//...
}
}
【问题讨论】:
-
既然你知道你的方法之间的区别,我怀疑你的基准测试错误,但仍然有很小的机会 - 显示基准测试代码(特别是你预热线程池的部分)会消除它愚蠢和冒犯的担忧。
-
however, I am not sure why the explicit version seems to be faster.很可能因为你忘了打电话给docs.microsoft.com/en-us/dotnet/api/…。如果该数字是 NUM_THREADS,那么来自线程池的线程将比直接创建它们“更慢”可用(因为线程池在启动新线程方面是保守的)。 -
I thought that perhaps the implicit version would be faster as tasks would be scheduled automatically, as opposed to manual task and thread creation.自动并不一定意味着更快。它通常意味着“更好的整体资源分配”——这可能意味着更慢,但更好的重用(例如)。 -
当您提供“相关代码的摘要版本”时,您不允许我们选择运行您的代码并自行测试。您应该提供minimal reproducible example,包括源数据和基准测试结果摘要。然后,我们可以验证我们得到了相同的结果——可能是环境问题影响了你的结果,但不是我们的——然后我们可以重构代码以确保你实际测量的是你认为你正在测量的东西,或者这样我们可以展示表现良好的替代方案。
-
感谢大家的回复。由于总体项目的范围很大,最小的可重复答案是不可行的,我只是从理论和教育的角度对隐式线程和显式线程的行为进行了回答,而不是特定于技术方面的示例参考代码。我会接受@Theodor Zoulias 的回答,并且很高兴由于缺乏明确性而关闭了这个问题。
标签: c# multithreading performance parallel-processing task-parallel-library