如何确定并行foreach循环是否比foreach循环具有更好的性能?
本文关键字:循环 foreach 更好 性能 何确定 并行 是否 | 更新日期: 2023-09-27 18:16:42
我刚刚在。net Fiddle中做了一个简单的测试,对100个长度为1000的随机整数数组进行排序,看看使用Paralell.ForEach
循环是否比普通的旧foreach
循环更快。
这是我的代码(我把它放在一起很快,所以请忽略代码的重复和整体糟糕的外观)
using System;
using System.Net;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
public class Program
{
public static int[] RandomArray(int minval, int maxval, int arrsize)
{
Random randNum = new Random();
int[] rand = Enumerable
.Repeat(0, arrsize)
.Select(i => randNum.Next(minval, maxval))
.ToArray();
return rand;
}
public static void SortOneThousandArraysSync()
{
var arrs = new List<int[]>(100);
for(int i = 0; i < 100; ++i)
arrs.Add(RandomArray(Int32.MinValue,Int32.MaxValue,1000));
Parallel.ForEach(arrs, (arr) =>
{
Array.Sort(arr);
});
}
public static void SortOneThousandArraysAsync()
{
var arrs = new List<int[]>(100);
for(int i = 0; i < 100; ++i)
arrs.Add(RandomArray(Int32.MinValue,Int32.MaxValue,1000));
foreach(var arr in arrs)
{
Array.Sort(arr);
};
}
public static void Main()
{
var start = DateTime.Now;
SortOneThousandArraysSync();
var end = DateTime.Now;
Console.WriteLine("t1 = " + (end - start).ToString());
start = DateTime.Now;
SortOneThousandArraysAsync();
end = DateTime.Now;
Console.WriteLine("t2 = " + (end - start).ToString());
}
}
和下面是两次点击Run后的结果:
t1 = 00:00:00.0156244
t2 = 00:00:00.0156243
…
t1 = 00:00:00.0467854
t2 = 00:00:00.0156246
…
所以,有时更快,有时差不多。
可能解释:
- 在我运行的第二次测试中,同步随机数组与异步随机数组相比"更加无序"
- 它与。net Fiddle上运行的进程有关。在第一种情况下,并行操作基本上像非并行操作一样运行,因为没有任何线程可以让我的小提琴接管。(或类似的东西)
想法吗?
只有在循环中的代码需要花费大量时间来执行时才应该使用Parallel.ForEach()
。在这种情况下,创建多个线程、对数组进行排序,然后将结果合并到一个线程上,要比在单个线程上简单地进行排序花费更多的时间。例如,执行以下代码片段中的Parallel.ForEach()
所花费的时间比执行普通的ForEach循环要少:
public static void Main(string[] args)
{
var numbers = Enumerable.Range(1, 10000);
Parallel.ForEach(numbers, n => Factorial(n));
foreach (var number in numbers)
{
Factorial(number);
}
}
private static int Factorial(int number)
{
if (number == 1 || number == 0)
return 1;
return number * Factorial(number - 1);
}
但是,如果我将var numbers = Enumerable.Range(1, 10000);
更改为var numbers = Enumerable.Range(1, 1000);
,则ForEach循环比Parallel.ForEach()
快。
当处理小任务(不需要花费大量时间来执行)时,请查看Partitioner类;在你的例子中:
public static void SortOneThousandArraysAsyncWithPart() {
var arrs = new List<int[]>(100);
for (int i = 0; i < 100; ++i)
arrs.Add(RandomArray(Int32.MinValue, Int32.MaxValue, 1000));
// Let's spread the tasks between threads manually with a help of Partitioner.
// We don't want task stealing and other optimizations: just split the
// list between 8 (on my workstation) threads and run them
Parallel.ForEach(Partitioner.Create(0, 100), part => {
for (int i = part.Item1; i < part.Item2; ++i)
Array.Sort(arrs[i]);
});
}
我得到以下结果(i7 3.2GHz 4内核HT, .Net 4.6 IA-64) -平均运行100次:
0.0081 Async (foreach)
0.0119 Parallel.ForEach
0.0084 Parallel.ForEach + Partitioner
正如你所看到的,foreach
仍然在顶部,但Parallel.ForEach + Partitioner
非常接近冠军
检查算法的性能是一件棘手的事情,小规模的性能很容易受到代码外部各种因素的影响。请在这里查看我对一个几乎重复的问题的回答,以获得深入的解释,以及一些基准测试模板的链接,您可以调整这些模板以更好地衡量您的算法性能。