我在我的电脑上编写了一个测试工具来比较这些方法的时间。
规格:
Windows 7 Professional 64位
Intel Core 2 Quad Q9550 @ 2.83GHz
4x1GiB Corsair Dominator DDR2 1066 (PC2-8500)
using System;
using System.Linq;
namespace Testbench
{
class Program
{
static void Main(string[] args)
{
var digits1 = Enumerable.Range(0, 500).ToArray();
var digits2 = digits1.ToArray();
Test("Regular Loop", () =>
{
int result = 0;
for (int i = 0; i < digits1.Length; i++)
{
result += digits1[i] * digits2[i];
}
return result;
});
Test("Enumerable \"Loop\"", () => Enumerable.Range(0, digits1.Length).Sum(i => digits1[i] * digits2[i]));
Test("Using Zip", () => digits1.Zip(digits2, (x, y) => x * y).Sum());
Test("Using Indexed Select", () => digits1.Select((n, i) => n * digits2[i]).Sum());
Test("Using Indexed Select with ElementAt", () => digits1.Select((n, i) => n * digits2.ElementAt(i)).Sum());
Test("Parallel Enumerable \"Loop\"", () => ParallelEnumerable.Range(0, digits1.Length).Sum(i => digits1[i] * digits2[i]));
Test("Using Parallel Zip", () => digits1.AsParallel().Zip(digits2.AsParallel(), (x, y) => x * y).Sum());
Test("Using Parallel Indexed Select", () => digits1.AsParallel().Select((n, i) => n * digits2[i]).Sum());
Test("Using Parallel Indexed Select with ElementAt", () => digits1.AsParallel().Select((n, i) => n * digits2.ElementAt(i)).Sum());
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
Console.WriteLine();
}
static void Test<T>(string testName, Func<T> test, int iterations = 1000000)
{
Console.WriteLine(testName);
Console.WriteLine("Iterations: {0}", iterations);
var results = Enumerable.Repeat(0, iterations).Select(i => new System.Diagnostics.Stopwatch()).ToList();
var timer = System.Diagnostics.Stopwatch.StartNew();
for (int i = 0; i < results.Count; i++)
{
results[i].Start();
test();
results[i].Stop();
}
timer.Stop();
Console.WriteLine("Time(ms): {0,3}/{1,10}/{2,8} ({3,10})", results.Min(t => t.ElapsedMilliseconds), results.Average(t => t.ElapsedMilliseconds), results.Max(t => t.ElapsedMilliseconds), timer.ElapsedMilliseconds);
Console.WriteLine("Ticks: {0,3}/{1,10}/{2,8} ({3,10})", results.Min(t => t.ElapsedTicks), results.Average(t => t.ElapsedTicks), results.Max(t => t.ElapsedTicks), timer.ElapsedTicks);
Console.WriteLine();
}
}
}
32位目标:
常规循环
迭代次数:1000000
时间(毫秒):0 / 0 / 0(1172)
时钟周期:3 / 3.101365 / 526(3244251)
可枚举的“循环”
迭代次数:1000000
时间(毫秒):0 / 4.3E-05 / 25(9054)
时钟周期:24 / 24.93989 / 69441(25052172)
使用Zip
迭代次数:1000000
时间(毫秒):0 / 2.4E-05 / 16(16282)
时钟周期:41 / 44.941406 / 45395(45052491)
使用索引选择
迭代次数:1000000
时间(毫秒):0 / 5.3E-05 / 32(13473)
时钟周期:34 / 37.165088 / 89602(37280177)
使用带有ElementAt的索引选择
迭代次数:1000000
时间(毫秒):0 / 1.5E-05 / 6(160215)
时钟周期:405 / 443.154147 / 17821(443306156)
并行可枚举的“循环”
迭代次数:1000000
时间(毫秒):0 / 0.000103 / 29(17194)
时钟周期:38 / 47.412312 / 81906(47576133)
使用并行Zip
迭代次数:1000000
时间(毫秒):0 / 9.4E-05 / 19(21703)
时钟周期:49 / 59.859005 / 53200(60051081)
使用并行索引选择
迭代次数:1000000
时间(毫秒):0 / 0.000114 / 27(20579)
时钟周期:45 / 56.758491 / 75455(56943627)
使用带有ElementAt的并行索引选择
迭代次数:1000000
时间(毫秒):0 / 8.1E-05 / 19(61137)
时钟周期:144 / 168.97909 / 53320(169165086)
64位目标:
正常循环
迭代次数:1000000
时间(毫秒):0 / 0 / 0(506)
滴答声:1 / 1.254137 / 1491(1401969)
可枚举的“循环”
迭代次数:1000000
时间(毫秒):0 / 2.9E-05 / 15(10118)
滴答声:27 / 27.850086 / 41954(27995994)
使用Zip
迭代次数:1000000
时间(毫秒):0 / 2.2E-05 / 13(17089)
滴答声:45 / 47.132834 / 38506(47284608)
使用索引选择
迭代次数:1000000
时间(毫秒):0 / 3.1E-05 / 12(14057)
滴答声:37 / 38.740923 / 33846(38897274)
使用具有ElementAt的索引选择
迭代次数:1000000
时间(毫秒):0 / 3.8E-05 / 29(117412)
滴答声:304 / 324.711279 / 82726(324872753)
并行可枚举的“循环”
迭代次数:1000000
时间(毫秒):0 / 9.9E-05 / 28(24234)
滴答声:38 / 66.79389 / 77578(67054956)
使用并行Zip
迭代次数:1000000
时间(毫秒):0 / 0.000111 / 24(30193)
滴答声:46 / 83.264037 / 69029(83542711)
使用并行索引选择
迭代次数:1000000
时间(毫秒):0 / 6.5E-05 / 20(28417)
滴答声:45 / 78.337831 / 56354(78628396)
使用具有ElementAt的并行索引选择
迭代次数:1000000
时间(毫秒):0 / 9.2E-05 / 16(65233)
滴答声:112 / 180.154663 / 44799(180496754)
(0*0) + (1*1) + (2*2) + ...
这个吧? - Bill the Lizard