c# - Linq 在大数据集上的查询性能
问题描述
我正在运行一种方法来对存储在ConcurrentQueue<T>
. 在 CPU 性能分析中,主要的影响似乎是:
foreach (Item inSequence in items.Where(w => w.SequenceNumber == i.SequenceNumber && w.Device == i.Device)) {}
对于 1,000 和 10,000,它实际上非常快。在 100,000 个项目时,性能变得至关重要 - 特定Linq
查询从占用总运行时 CPU 的 4.5% 到超过总运行时 CPU 的 58% 以上。我假设性能下降是专门由于 的大小造成的ConcurrentQueue
,但我不知道该怎么做。如果避免 Linq 查询解决了这个问题,那很好。我只是不知道该怎么做。是否有其他一些性能更高的并发类型?
这是一个 CQ,因为数据是异步构建和读取的。然而,在这个特定的方法中,发生在数据构建之后和数据被读回之前,它在单个线程上运行。
非常松散的样本在这里:https ://dotnetfiddle.net/hjDOva
using System;
using System.Diagnostics;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
public class Program
{
static int count = 100000;
public static void Main()
{
var items = new ConcurrentQueue<Item>();
var r = new Random();
for (int i = 0; i < count; i++)
{
items.Enqueue(new Item());
}
var sw = Stopwatch.StartNew();
foreach (Item i in items.DistinctBy(d => new { d.SequenceNumber, d.Device }))
foreach (Item inSequence in items.Where(w => w.Device == i.Device && w.SequenceNumber == i.SequenceNumber))
{
}
Console.WriteLine(sw.Elapsed);
}
}
public static class Extensions
{
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
}
public class Item
{
#region Fields
protected bool fixDates;
protected string randomSerial;
protected decimal amount;
protected string device;
protected DateTime depositTime;
public int SequenceNumber = -1;
[NonSerialized()]
protected System.Random rnd = new Random(Int32.Parse(Guid.NewGuid().ToString().Substring(0, 8), System.Globalization.NumberStyles.HexNumber));
#endregion
#region Properties
public bool FixDates
{
get
{
return this.fixDates;
}
set
{
this.fixDates = value;
}
}
public string Amount
{
get
{
return this.amount.ToString();
}
set
{
this.amount = Convert.ToDecimal(value);
}
}
public string RandomSerial
{
get { return randomSerial; }
set { randomSerial = value; }
}
public string Device
{
get { return this.device; }
set { this.device = value; }
}
public DateTime DepositTime
{
get { return this.depositTime; }
set { this.depositTime = value; }
}
#endregion
#region Constructors
public Item()
{
fixDates = false;
RandomSerial = Guid.NewGuid().ToString().Substring(0, 8);
this.amount = 5.00m;
this.device = "IC" + rnd.Next(6).ToString();
this.depositTime = DateTime.Now;
this.SequenceNumber = rnd.Next(10);
}
#endregion
}
但是,它不提供 100,000 个项目所需的内存。
关于使用 CQ 的问题,是的,我知道队列不适合这个。该工具生成数据以测试各种产品类型的进口情况。只有一个产品需要这种方法,Transactionalize()
. 大多数时候不使用此代码。
这是一个 CQ,因为系统并行创建对象(当它发生时,这是一个显着的性能改进),并且在大多数情况下,它们也是并行出列的。
解决方案
假设下面代码的目的是分组处理项目,每个组具有相同的SequenceNumber
和Device
,
foreach (Item i in items.DistinctBy(d => new { d.SequenceNumber, d.Device }))
foreach (Item inSequence in items
.Where(w => w.Device == i.Device && w.SequenceNumber == i.SequenceNumber))
{
}
...您可以通过使用这样的 Linq 方法更有效地实现相同的GroupBy
目标:
var groups = items.GroupBy(i => (i.SequenceNumber, i.Device));
foreach (IGrouping<(string, string), Item> group in groups)
foreach (Item inSequence in group)
{
}
请注意,我没有使用匿名类型,而是使用了更轻量级ValueTuple
的 s 作为键,它们不需要垃圾收集。
如果您还希望以后能够非常有效地搜索特定组,而不是GroupBy
使用类似的ToLookup
.
推荐阅读
- compiler-errors - Gnuplot - 误差线
- java - 引起:java.lang.NumberFormatException:对于输入字符串:“?” 对于 cron 工作
- django - 使用 pk 以外的属性识别 Django Rest Framework ModelSerializer ForeignKey
- c# - Visual Studio - 调试时以黄色突出显示的行
- python - WSAECONNREFUSED:通过 PyRFC 建立连接时连接被拒绝
- go - 如何处理响应 JSON 具有没有键的自定义字段?
- android - java.lang.IllegalStateException:全局数据库持有者未初始化。确保在访问数据库之前调用 FlowManager.init()?
- kubernetes - Kubernetes 在不同网络中的设置
- .net - Vb.net在面板内的自定义文本上添加占位符
- vue.js - 在 vuetify 中添加 v-tooltip 到 v-treeview