【问题标题】:Why is multithreaded quicksort slower than singlethreaded in C#为什么多线程快速排序比 C# 中的单线程慢
【发布时间】:2017-12-26 20:33:45
【问题描述】:

我知道以前有人问过这个问题,我找到的答案都是关于抢占和同步开销等。但是,我仍然很想知道我自己的情况的答案。所以这就是交易。

我在Intel Core i7-2670QM CPU(4 核,8 线程)上运行,我编写了以下代码:

using System;
using System.Diagnostics;
using System.Threading;

namespace _T
{
    class Program
    {
        private static void stquicksort(object parameter)
        {
            object[] parameters = (object[])parameter;

            int[] array = (int[])parameters[0];
            int left = (int)parameters[1];
            int right = (int)parameters[2];

            if (left >= right) return;

            int temp = (left + right) / 2;
            int pivot = array[temp];

            array[temp] = array[right];

            int j = left;
            for (int i = left; i < right; i++)
            {
                if (array[i] < pivot)
                {
                    if (i != j)
                    {
                        temp = array[i];
                        array[i] = array[j];
                        array[j++] = temp;
                    }
                    else j++;
                }
            }

            array[right] = array[j];
            array[j] = pivot;

            stquicksort(new object[] { array, left, j - 1 });
            stquicksort(new object[] { array, j + 1, right });
        }
        private static void mtquicksort(object parameter)
        {
            object[] parameters = (object[])parameter;

            int[] array = (int[])parameters[0];
            int left = (int)parameters[1];
            int right = (int)parameters[2];

            if (left >= right) return;

            int temp = (left + right) / 2;
            int pivot = array[temp];

            array[temp] = array[right];

            int j = left;
            for (int i = left; i < right; i++)
            {
                if (array[i] < pivot)
                {
                    if (i != j)
                    {
                        temp = array[i];
                        array[i] = array[j];
                        array[j++] = temp;
                    }
                    else j++;
                }
            }

            array[right] = array[j];
            array[j] = pivot;

            Thread t = new Thread(mtquicksort);
            t.Start(new object[] { array, left, j - 1 });
            mtquicksort(new object[] { array, j + 1, right });
            t.Join();
        }

        private static void dump(int[] array)
        {
            Console.Write("Array:");
            foreach (int el in array) Console.Write(" " + el);
            Console.WriteLine();
        }

        private static void Main(string[] args)
        {
            while (true)
            {
                Console.Write("Enter the number of elements: ");
                int count = Convert.ToInt32(Console.ReadLine());
                if (count < 0) break;

                Random rnd = new Random();

                int[] array1 = new int[count];
                for (int i = 0; i < array1.Length; i++)
                    array1[i] = rnd.Next(1, 100);
                int[] array2 = (int[])array1.Clone();

                Stopwatch sw = new Stopwatch();

                sw.Reset(); sw.Start();
                stquicksort(new object[] { array1, 0, array1.Length - 1 });
                sw.Stop();

                Console.WriteLine("[ST] Time needed: " + sw.ElapsedMilliseconds + "ms");

                sw.Reset(); sw.Start();
                mtquicksort(new object[] { array2, 0, array2.Length - 1 });
                sw.Stop();

                Console.WriteLine("[MT] Time needed: " + sw.ElapsedMilliseconds + "ms");
            }

            Console.WriteLine("Press any key to exit . . .");
            Console.ReadKey(true);
        }
    }
}

stquicksort 是单线程的,mtquicksort 是多线程的,是的,我故意这样保留 st 参数,因此两个版本的装箱/拆箱开销是相同的(如果有的话)。我已经发布了解决方案(禁用了所有调试),输出有点难过:

Enter the number of elements: 100  
[ST] Time needed: 0ms  
[MT] Time needed: 323ms  

Enter the number of elements: 1000  
[ST] Time needed: 0ms  
[MT] Time needed: 7476ms  

Enter the number of elements: 1000  
[ST] Time needed: 0ms  
[MT] Time needed: 7804ms  

Enter the number of elements: 1000  
[ST] Time needed: 0ms  
[MT] Time needed: 7474ms  

Enter the number of elements: 10  
[ST] Time needed: 0ms  
[MT] Time needed: 32ms  

Enter the number of elements: 100  
[ST] Time needed: 0ms  
[MT] Time needed: 339ms  

再说一遍,问题是先发制人的,是不是代码中的缺陷?更重要的是,解决这个问题的正确方法是什么。

【问题讨论】:

  • 为什么不计算独立于您的快速排序的线程成本.....您可以回答自己的问题
  • @KeithNicholas 当然,我正在摆弄它,但想听听其他一些建议和意见,以及在这个具体情况下:)
  • 您应该尝试对更多元素进行排序,例如 1M、10M、100M
  • @Jason 试过了,async/await & tasks 实际上会导致 stackoverflow,而使用线程它仍然非常非常慢。

标签: c# multithreading quicksort


【解决方案1】:

生成线程是一项相当昂贵的操作。这不是瞬时的,因此您看到的大量时间不是执行排序所需的额外时间,而是产生胎面所需的时间。当您生成一个新线程以使其值得时,该线程必须运行一段时间。

.NET 和 C# 确实有一个任务系统 任务类似于线程,只是它们在线程池上操作,而不是每次都产生一个新线程。这允许您执行多线程任务,而无需为每个任务创建一个新线程的高成本。

尝试用这个替换你的线程代码。

Task t = Task.Run(()=>mtquicksort(new object[] { array, left, j - 1 }));
t.Wait();

请注意,您必须使用 System.Threading.Tasks 命名空间

【讨论】:

  • 感谢您的详细解答。我已经尝试过任务,是的,它们被证明更快,但单线程似乎更快。再次感谢:)
  • 欢迎您。乐意效劳!任务可能仍然较慢,原因有两个,首先它们不是免费的,而且你的生成 (n log n)。此外,为了充分利用任务系统,您必须使用 async 和 await 语法。尽管您可能仍然无法获得性能提升。
  • 是的,是的,我知道其中的复杂性,但仍然没想到会发生这种情况。无论如何,Async/Await 实际上是我想尝试的下一件事,呵呵,再次感谢。 :)
猜你喜欢
  • 2021-03-11
  • 1970-01-01
  • 2014-07-21
  • 2015-07-09
  • 2013-11-22
  • 1970-01-01
  • 1970-01-01
  • 2020-08-15
  • 2011-02-22
相关资源
最近更新 更多