平行的.调用提供了最小的性能提升(如果有的话)

本文关键字:如果 性能 调用 | 更新日期: 2023-09-27 18:10:59

我编写了一个简单的控制台应用程序来测试Parallel的性能。基于Microsoft在msdn上的示例调用:

public static void TestParallelInvokeSimple()
    {
        ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 1 }; // 1 to disable threads, -1 to enable them
        Parallel.Invoke(parallelOptions,
            () =>
                {
                    Stopwatch sw = new Stopwatch();
                    sw.Start();
                    Console.WriteLine("Begin first task...");
                    List<string> objects = new List<string>();
                    for (int i = 0; i < 10000000; i++)
                    {
                        if (objects.Count > 0)
                        {
                            string tempstr = string.Join("", objects.Last().Take(6).ToList());
                            objects.Add(tempstr + i);
                        }
                        else
                        {
                            objects.Add("START!");
                        }
                    }
                    sw.Stop();
                    Console.WriteLine("End first task... {0} seconds", sw.Elapsed.TotalSeconds);
                },
            () =>
                {
                    Stopwatch sw = new Stopwatch();
                    sw.Start();
                    Console.WriteLine("Begin second task...");
                    List<string> objects = new List<string>();
                    for (int i = 0; i < 10000000; i++)
                    {
                        objects.Add("abc" + i);
                    }
                    sw.Stop();
                    Console.WriteLine("End second task... {0} seconds", sw.Elapsed.TotalSeconds);
                },
            () =>
                {
                    Stopwatch sw = new Stopwatch();
                    sw.Start();
                    Console.WriteLine("Begin third task...");
                    List<string> objects = new List<string>();
                    for (int i = 0; i < 20000000; i++)
                    {
                        objects.Add("abc" + i);
                    }
                    sw.Stop();
                    Console.WriteLine("End third task... {0} seconds", sw.Elapsed.TotalSeconds);
                }
            );
    }

ParallelOptions可以很容易地启用/禁用线程。

当我禁用线程时,我得到以下输出:

Begin first task...
End first task... 10.034647 seconds
Begin second task...
End second task... 3.5326487 seconds
Begin third task...
End third task... 6.8715266 seconds
done!
Total elapsed time: 20.4456563 seconds
 Press any key to continue . . .

当我通过设置MaxDegreeOfParallelism为-1来启用线程时,我得到:

Begin third task...
Begin first task...
Begin second task...
End second task... 5.9112167 seconds
End third task... 13.113622 seconds
End first task... 19.5815043 seconds
done!
Total elapsed time: 19.5884057 seconds

实际上与顺序处理的速度相同。因为任务1花费的时间最长——大约10秒,所以我希望线程总共花费大约10秒来运行所有3个任务。那么发生了什么呢?为什么是Parallel。调用单独运行较慢的任务,还是并行运行?

顺便说一句,我在使用Parallel时看到了完全相同的结果。在一个真实的应用程序中调用,同时执行许多不同的任务(其中大多数是运行查询)。

如果你认为是我的电脑,再想想…它已经推出1年了,8GB内存,windows 8.1, Intel酷睿I7 2.7GHz 8核cpu。当我一遍又一遍地运行测试时,我的电脑并没有过载。我的电脑从来没有达到最大值,但在运行时明显显示cpu和内存增加。

平行的.调用提供了最小的性能提升(如果有的话)

我还没有对此进行分析,但是这里的大部分时间可能都花在为这些列表和小字符串分配内存上了。这些"任务"除了以最少的输入和几乎没有处理时间来增加列表之外,实际上并没有做任何事情。

认为:

objects.Add("abc" + i);

本质上只是创建一个新的字符串,然后将其添加到列表中。创建这样一个小字符串在很大程度上只是一个内存分配练习,因为字符串存储在堆上。此外,分配给List的内存将很快填满——每次它填满时,列表都会为自己的存储重新分配更多的内存。

现在,堆分配是在一个进程中序列化的——一个进程中的四个线程不能同时分配内存。内存分配请求是按顺序处理的,因为共享堆就像任何其他共享资源一样,需要防止并发混乱。

所以你有三个极度内存饥渴的线程,它们可能花费大部分时间等待对方完成获得新的内存。

用CPU密集型工作填充这些方法(例如:做一些数学等),你会看到结果非常不同。教训是,并不是所有的任务都可以有效地并行化,也不是所有的任务都像你想象的那样并行化。例如,可以通过在自己的进程中运行每个任务来加快上面的速度——例如,使用自己的私有内存空间,就不会争用内存分配。