我有这个数组:var arr = new int[] { 1, 1, 0, -1, -1 };
我需要计算正数,负数和零数的数量。我用foreach
loop和with 做到了Linq
,我尝试通过使用以下Stopwatch
代码比较两种方法之间的性能:
int pos = 0, neg = 0, zeros = 0;
int p = 0, n = 0, z = 0;
Stopwatch sw = new Stopwatch();
sw.Start();
pos = arr.Sum(e => e > 0 ? 1 : 0);
neg = arr.Sum(e => e < 0 ? 1 : 0);
zeros = arr.Sum(e => e == 0 ? 1 : 0);
sw.Stop();
Stopwatch sw2 = new Stopwatch();
sw2.Start();
foreach (var item in arr)
{
if (item > 0) p++;
else if (item < 0) n++;
else z++;
}
sw2.Stop();
Console.WriteLine("Elapsed={0}", sw.Elapsed); //Elapsed=00:00:00.0008311
Console.WriteLine("Elapsed2={0}", sw2.Elapsed); //Elapsed2=00:00:00.0000028
结果告诉我,该foreach
循环Linq
(28毫秒)比方法(8311毫秒)好得多,所以我的问题是为什么性能存在所有这些差异?
我什至尝试进行三个foreach
循环,一个循环计算负数,一个循环计算正数,第三循环计算零度,但是性能仍然比Linq方法好!
在此先感谢您的帮助!
让我们以一些不同的方式进行赛马:
private static string UnderTest(int size) {
int pos = 0, neg = 0, zeros = 0;
int p = 0, n = 0, z = 0;
Random random = new Random(0);
int[] arr = Enumerable
.Range(0, size)
.Select(x => random.Next(-1, 2))
.ToArray();
GC.Collect(GC.MaxGeneration);
Stopwatch sw = new Stopwatch();
sw.Start();
// Three Linq loops (expected to be 3 three times slower)
pos = arr.Sum(e => e > 0 ? 1 : 0);
neg = arr.Sum(e => e < 0 ? 1 : 0);
zeros = arr.Sum(e => e == 0 ? 1 : 0);
sw.Stop();
Stopwatch sw2 = new Stopwatch();
sw2.Start();
// Just 1 loop
foreach (var item in arr) {
if (item > 0) p++;
else if (item < 0) n++;
else z++;
}
sw2.Stop();
return $"{sw.Elapsed} vs. {sw2.Elapsed} ratio: {((double)sw.Elapsed.Ticks) / sw2.Elapsed.Ticks:f3}";
}
争夺不同array size
:
int[] loops = new int[] {
1000,
10_000,
100_000,
1_000_000,
10_000_000,
100_000_000,
1000, // <- 1000 again
};
string report = string.Join(Environment.NewLine, loops
.Select(loop => $"loops: {loop,10} : {UnderTest(loop)}"));
Console.Write(report);
结果:
loops: 1000 : 00:00:00.0006471 vs. 00:00:00.0000114 ratio: 56.763 // <- Warming up
loops: 10000 : 00:00:00.0003195 vs. 00:00:00.0001074 ratio: 2.975
loops: 100000 : 00:00:00.0037131 vs. 00:00:00.0010910 ratio: 3.403
loops: 1000000 : 00:00:00.0351574 vs. 00:00:00.0118858 ratio: 2.958
loops: 10000000 : 00:00:00.3729617 vs. 00:00:00.1198276 ratio: 3.112
loops: 100000000 : 00:00:03.7002508 vs. 00:00:01.1808595 ratio: 3.134
loops: 1000 : 00:00:00.0000322 vs. 00:00:00.0000099 ratio: 3.253 // <- Expected
发生了什么:我们有基于3
Linq的循环(3
调用Sum
),因此Linq 慢了3倍(不足为奇)。但是,当系统加载程序集,编译 IL代码等时,我们在最初运行时会有一个异常值。因此,您会产生所谓的预热效果。
很好的答案。但是,请您解释一下为什么创建数组后要收集垃圾
loops
?@Cid:谢谢!一个更好的地方
GC.Collect(GC.MaxGeneration);
是在比赛之前(在内UnderTest
):我们不希望GC
偶尔开始比赛并干扰测量这是有道理的,我不习惯于手动调用GC,因此直接在代码中进行收集将防止垃圾在街上堆积(堆,),并自动调用收集器?
@Cid:我们尝试避免不必要的过程(尤其是垃圾收集,这会阻止世界)干扰。如果我们希望收集所有垃圾(例如,包含数百万个项目的数组),那么希望以后的运行不会被中断(例如,在中
foreach
)。