批量处理长 Linq 操作?

2024-03-06

我提出了一个问题并得到了回答here https://stackoverflow.com/a/23606749/859154关于我遇到的性能问题large数据收集。 (使用 linq 创建)

好吧,我们先把它放在一边。

但其中一个有趣的(并且geniusly)优化 - Marc 建议 - 是Batchifylinq 查询。

/*1*/ static IEnumerable<T> Batchify<T>(this IEnumerable<T> source, int count)
/*2*/    {
/*3*/      var list = new List<T>(count);
/*4*/      foreach(var item in source)
/*5*/         {
/*6*/           list.Add(item);
/*7*/           if(list.Count == count)
/*8*/             {
/*9*/               foreach (var x in list) yield return x;
/*10*/              list.Clear();
/*11*/            }
/*12*/        }
/*13*/      foreach (var item in list) yield return item;
/*14*/    }

在这里,Batchify 的目的是确保我们不会帮助 服务器太多,每次操作之间花费相当长的时间 - 数据以1000为批次发明,每批次制作 很快就可以使用。

现在我明白了what它正在做,但我can't说出区别,因为我可能会错过它实际工作的方式。 (有时你认为你知道一些事情......直到......)

好的,回到基础知识:

AFAIK,Linq 的工作方式就像这个链 –:

所以,我们不能开始枚举till的结果select in :

Where-->OrderBy-->Select 

已经完成了。

所以基本上我是waiting for select具有all正确的数据(after where, 后orderby), and 只有那时- 我的代码可以触及这些值。 (产生自select)

但根据我对Marc回答的理解,这些之间似乎存在差距yields它允许其他资源做某事...(?)

如果是这样,那么在每次迭代之间#4, 行后#9,CPU还有时间做别的事吗?

Question

  • 有人可以照亮吗?这是如何运作的 ?

nb

我已经知道了(例如)select无非是:

public static IEnumerable<TResult> Select<TSource,TResult>
 (this IEnumerable<TSource> source, Func<TSource,TResult> selector)
{
   foreach (TSource element in source)
       yield return selector (elem![enter image description here][3]ent);
}

但如果是这样,我的代码就无法触及它,直到所有值(之后where , orderby) 计算...

edit :

对于那些询问是否有区别的人:https://i.stack.imgur.com/19Ojw.jpg https://i.stack.imgur.com/19Ojw.jpg

2秒为1M项目。9秒为5M items.

(ignore the second line of time , (extra console.write line).) enter image description here

这是 5m 列表:https://i.stack.imgur.com/DflGR.jpg https://i.stack.imgur.com/DflGR.jpg(第一个是 withBatchify ,另一个不是)


重要提示:显示的图像包括OrderBy: 你应该注意这个breaks在这里进行批处理,因为OrderBy是一个缓冲运算符。我展示的批处理方法适用于非缓冲假脱机流。

在我使用它的上下文中,起源(在批处理之前)是一个迭代器块,它在每次迭代中执行许多涉及对象创建和伪随机数生成器的操作。因为有问题的代码对时间敏感,所以我做了什么not想要做的是在每次调用存储之间引入一个可靠的暂停(用于创建每个项目的 CPU 工作)。这部分是为了模拟原始代码,它预先创建了所有对象,部分是因为我了解 SE.Redis 如何处理套接字工作。

让我们考虑一下没有的行为Batchify:

  • 创建一个项目(CPU 工作)并产生它
  • 发送到商店(网络IO)
  • 创建一个项目(CPU 工作)并产生它
  • 发送到商店(网络IO)
  • 创建一个项目(CPU 工作)并产生它
  • 发送到商店(网络IO)
  • ...

特别是,这意味着存储请求之间存在可预测的暂停。 SE.Redis 在专用工作线程上处理套接字 IO,上述情况很容易导致大量数据包碎片,特别是因为我使用了“即发即忘”标志。编写器线程需要定期刷新,当either缓冲区达到临界大小,or出站消息队列中没有更多工作。

现在考虑batchify 的作用:

  • 创建一个项目(CPU 工作)并缓冲它
  • 创建一个项目(CPU 工作)并缓冲它
  • ...
  • 创建一个项目(CPU 工作)并缓冲它
  • 产生一个项目
  • 发送到商店(网络IO)
  • 产生一个项目
  • 发送到商店(网络IO)
  • ...
  • 产生一个项目
  • 发送到商店(网络IO)
  • 创建一个项目(CPU 工作)并缓冲它
  • ...

在这里您可以看到存储请求之间的 CPU 工作量是显著地减少。这更正确地模仿了原始代码,其中最初创建了数百万个列表,然后进行迭代。但此外,这意味着创建出站消息的线程很有可能会去至少一样快作为写入线程,这意味着出站队列在任何可观的时间内都不太可能变为零。这允许much减少数据包碎片,因为现在每个请求中很可能包含多个消息,而不是每个请求都有一个数据包。由于开销减少,更少的数据包通常意味着更高的带宽。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

批量处理长 Linq 操作? 的相关文章

随机推荐