更改具有“return”的方法并且“收益回报”

本文关键字:收益 回报 收益回报 方法 return | 更新日期: 2023-09-27 18:19:17

我知道用同样的方法使用returnyield 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];
        }
    }
}

重要提示:我知道这段代码不能编译。这是我要优化的代码。

我知道有两种方法可以使这个方法工作:

  1. 转换yield return部分:

    ...
    else
    {
        int index = this.GetSet(data);
        return this.keySets[index].Select(key => this.items[key]);
    }
    
  2. 转换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);

每个循环都在单独的进程中运行,因此不会受到垃圾收集或其他任何因素的性能影响。

  1. 我用一个方法版本运行程序,然后
  2. 关闭它
  3. 重写方法并重新运行。

更改具有“return”的方法并且“收益回报”

使用两个函数。外部函数由客户端调用,执行所有不需要延迟的非延迟操作(如参数验证)。private worker执行惰性部分:

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];
  }
}

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循环要慢得多。通过执行错误检查(一次)和创建委托(一次),以及通过委托间接的轻微开销,它将减慢速度。但是循环机制应该是相同的。

然而,如果说我在性能分析中学到了什么,那就是我的期望经常是完全错误的。您的分析运行表明应用程序中的瓶颈是什么?让我们从事实出发,而不是从猜测出发。热点在哪里?