我知道在同一方法中使用return
和yield return
是不可能的。
这是我想要优化的代码:
public IEnumerable<TItem> GetItems(int data)
{
if (this.isSingleSet)
{
return this.singleSet; // is IEnumerable per-se
}
else
{
int index = this.GetSet(data);
foreach(TKey key in this.keySets[index])
{
yield return this.items[key];
}
}
}
重要提示:我知道此代码无法编译。这是我必须优化的代码。
我知道有两种方法可以使这种方法有效:
转换yield return
部分:
...
else
{
int index = this.GetSet(data);
return this.keySets[index].Select(key => this.items[key]);
}
转换return
部分:
if (this.isSingleSet)
{
foreach(TItem item in this.singleSet)
{
yield return item;
}
}
else ...
但两者之间存在很大的速度差异。仅使用return
语句(换句话说,使用Select()
)比yield return
转换要慢得多(比慢6倍)。
您是否还有其他方法可以编写此方法?您是否有任何其他建议信息对性能差异有价值?
我通过在for
循环周围使用秒表来测量两种方法的速度。
Stopwatch s = new Stopwatch();
s.Start();
for(int i = 0; i < 1000000; i++)
{
GetItems(GetRandomData()).ToList();
}
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds);
每个循环都是在不同的进程中运行的,因此垃圾收集或其他任何东西都不会对性能产生影响。
这几次看到可靠的性能差异......
答案 0 :(得分:15)
使用两个功能。由客户端调用的外部函数执行您不想延迟的所有非惰性位(如参数验证)。私人工作者做了懒惰的事情:
public IEnumerable<TItem> GetItems(int data) {
if (this.isSingleSet) {
return this.singleSet; // is IEnumerable per-se
} else {
return DoGetItems(data);
}
}
private IEnumerable<TItem> DoGetItems(int data) {
int index = this.GetSet(data);
foreach(TKey key in this.keySets[index]) {
yield return this.items[key];
}
}
答案 1 :(得分:5)
Select的实现是(删除了错误检查):
public static IEnumerable<R> Select<A, R>(
this IEnumerable<A> sequence,
Func<A, R> projection)
{
foreach(A item in sequence)
yield return projection(item);
}
所以我很难相信你使用Select 比你已经拥有的几乎相同的foreach循环慢得多。它将通过执行错误检查(一次)和创建委托(一次)以及通过委托间接的轻微开销来减慢速度。但循环机械应该是相同的。
但是,如果我在绩效分析中学到了一点,那就是我的期望经常出错。您的分析运行表明您的应用程序的瓶颈是什么?让我们从事实出发,而不是从这里猜测。什么是热点?