【问题标题】:Multithreading Merge Sort -> How to optimize?多线程合并排序 -> 如何优化?
【发布时间】:2022-01-26 12:17:19
【问题描述】:

我尝试多线程合并排序算法。该算法的简单形式是:(Merge() 是一种标准的合并算法)

static void MergeSort(this int[] array, int initial, int final) {
    if (initial == final) return;
    array.MergeSort(initial, (initial + final) / 2);
    array.MergeSort((initial + final) / 2 + 1, final);
    array.Merge(initial, final);
}

而多线程版本是:

static void MergeSortMT(this int[] array, int initial, int final, int mtCount) {
    if (initial == final) return;

    if (mtCount > 0) {
        Task t = Task.Run(() =>
        {
            array.MergeSortMT(initial, (initial + final) / 2, mtCount - 1);
        });
        array.MergeSortMT((initial + final) / 2 + 1, final, mtCount - 1);

        t.Wait();
    }
    else
    {
        array.MergeSort(initial, (initial + final) / 2);
        array.MergeSort((initial + final) / 2 + 1, final);
    }
    array.Merge(initial, final);
}

这里的mtCount表示线程分离会发生多少次。通常整个排序由单个线程运行。如果 mtCount 为 1,总线程数变为 2,如果 mtCount 为 2,总线程数变为 4,以此类推。

以下是一些结果:

Method - Input Size - Times Run - Miliseconds

Merge 1000000 X 1000    606832
Merge 100000 X 1000     51061
Merge 10000 X 1000      4080
Merge 1000 X 1000       314
Merge 100 X 1000        22
Merge 10 X 1000         1

mtCount = 1
MergeMT 1000000 X 1000  258237
MergeMT 100000 X 1000   23498
MergeMT 10000 X 1000    2088
MergeMT 1000 X 1000     202
MergeMT 100 X 1000      57
MergeMT 10 X 1000       44

mtCount = 2
MergeMT 1000000 X 1000  188337
MergeMT 100000 X 1000   16836
MergeMT 10000 X 1000    1825
MergeMT 1000 X 1000     355
MergeMT 100 X 1000      162
MergeMT 10 X 1000       299

mtCount = 3
MergeMT 1000000 X 1000  175220
MergeMT 100000 X 1000   15276
MergeMT 10000 X 1000    1690
MergeMT 1000 X 1000     455
MergeMT 100 X 1000      296
MergeMT 10 X 1000       416

mtCount = 4
MergeMT 1000000 X 1000  197234
MergeMT 100000 X 1000   17046
MergeMT 10000 X 1000    2087
MergeMT 1000 X 1000     684
MergeMT 100 X 1000      657
MergeMT 10 X 1000       596

因此,使用 2 个线程可以将代码优化 2 倍以上。使用 4 个线程影响较小,8 个线程几乎没有任何好处。使用 16 个线程甚至会降低性能,这是可以理解的。 (小的输入大小并不重要。我知道启动一个新线程需要一些时间。)

知道我的计算机有 4 个物理内核和 8 个线程,我的问题是为什么使用 4 或 8 个线程对性能的提升要少得多?

【问题讨论】:

  • 我使用 System.Diagnostics 中的 Stopwatch 类来计算时间。如果我运行 ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads); workerThreads 设置为 8。
  • “我知道创建一个新线程需要一些时间”,您没有回答自己的问题吗?
  • 您是否尝试过使用任务并行库 (TPL) 进行并行化?
  • “我的电脑有 4 个物理核心”作为@Sefe 提示,如果你想在核心之间分配工作,你应该使用 TPL
  • 您可以尝试流式传输最后的合并步骤,以便它们可以在前一个排序完成之前开始。

标签: c# multithreading


【解决方案1】:

因为归并排序由两个主要部分组成:

  1. 递归排序两半
  2. 合并 2 个已排序的半部分

归并排序中的多线程是有效的,因为两个线程可以在第一步异步执行 2 个不同的调用。但是,第二步不能通过多线程来加快,因为它是一个必须在单个线程上运行的线性进程。第二步实际上需要相当长的时间。例如,最后一个(也是最大的)合并调用大约需要整个排序持续时间的三分之一。

【讨论】:

    猜你喜欢
    • 2012-09-17
    • 1970-01-01
    • 1970-01-01
    • 2016-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多