也许我漏掉了什么,但我相当确定你的基准测试有误。
我用以下方法进行了测试:
Any
扩展方法("LINQ")
- 一个简单的
foreach
循环(你的 "优化" 方法)
- 使用
ICollection.Contains
方法
- 使用优化的数据结构
HashSet<T>
的 Any
扩展方法
这是测试代码:
class Program
{
static void Main(string[] args)
{
var names = Enumerable.Range(1, 10000).Select(i => i.ToString()).ToList();
var namesHash = new HashSet<string>(names);
string testName = "9999";
for (int i = 0; i < 10; i++)
{
Profiler.ReportRunningTimes(new Dictionary<string, Action>()
{
{ "Enumerable.Any", () => ExecuteContains(names, testName, ContainsAny) },
{ "ICollection.Contains", () => ExecuteContains(names, testName, ContainsCollection) },
{ "Foreach Loop", () => ExecuteContains(names, testName, ContainsLoop) },
{ "HashSet", () => ExecuteContains(namesHash, testName, ContainsCollection) }
},
(s, ts) => Console.WriteLine("{0, 20}: {1}", s, ts), 10000);
Console.WriteLine();
}
Console.ReadLine();
}
static bool ContainsAny(ICollection<string> names, string name)
{
return names.Any(s => s == name);
}
static bool ContainsCollection(ICollection<string> names, string name)
{
return names.Contains(name);
}
static bool ContainsLoop(ICollection<string> names, string name)
{
foreach (var currentName in names)
{
if (currentName == name)
return true;
}
return false;
}
static void ExecuteContains(ICollection<string> names, string name,
Func<ICollection<string>, string, bool> containsFunc)
{
if (containsFunc(names, name))
Trace.WriteLine("Found element in list.");
}
}
不用担心 Profiler
类的内部实现。它只是在循环中运行 Action
并使用 Stopwatch
进行计时。 它还确保在每次测试之前调用 GC.Collect()
,以尽可能消除噪声。
以下是结果:
Enumerable.Any: 00:00:03.4228475
ICollection.Contains: 00:00:01.5884240
Foreach Loop: 00:00:03.0360391
HashSet: 00:00:00.0016518
Enumerable.Any: 00:00:03.4037930
ICollection.Contains: 00:00:01.5918984
Foreach Loop: 00:00:03.0306881
HashSet: 00:00:00.0010133
Enumerable.Any: 00:00:03.4148203
ICollection.Contains: 00:00:01.5855388
Foreach Loop: 00:00:03.0279685
HashSet: 00:00:00.0010481
Enumerable.Any: 00:00:03.4101247
ICollection.Contains: 00:00:01.5842384
Foreach Loop: 00:00:03.0234608
HashSet: 00:00:00.0010258
Enumerable.Any: 00:00:03.4018359
ICollection.Contains: 00:00:01.5902487
Foreach Loop: 00:00:03.0312421
HashSet: 00:00:00.0010222
数据非常一致,并传达了以下信息:
- 直接使用 `Any` 扩展方法相比于直接使用 `foreach` 循环要慢约9%。
- 使用最合适的方法(`ICollection.Contains`)与未优化的数据结构(`List`)相比,速度快约50%。
- 使用优化后的数据结构(`HashSet`)在性能方面完全超越了其他方法。
我不知道你从哪里得到了243%。我的猜测是这和所有类型转换有关。如果你正在使用 `ArrayList`,那么你不仅使用了未经优化的数据结构,而且使用了基本上已经过时的数据结构。
我可以预测下一步会发生什么。"是的,我知道你可以更好地进行优化,但这只是一个例子,用来比较LINQ与非LINQ的性能。"
但是,如果你甚至不能在你的示例中深入研究,你如何可能在生产代码中做到这样的深入研究呢?
最重要的是:
如何架构和设计软件与使用特定工具及其时间相比,重要性成倍增长。
如果你遇到性能瓶颈 - LINQ与非LINQ同样可能发生 - 那就解决它们。Eric提出的自动化性能测试是一个很好的建议;这将帮助你及早地识别问题,以便你可以通过真正的解决方案来提高性能(而不是放弃一个使你的效率提高80%,但带来少于10%性能惩罚的神奇工具),并且可以将性能提升2倍、10倍、100倍甚至更多。
创建高性能应用程序不是使用正确的库。它涉及到性能分析、做出良好的设计选择和编写优秀的代码。
cast
... - cjk