我从EF内核的SQLite中读取了40,000个小对象/行,耗时18秒,这对于我的UWP应用程序来说太长了。当发生这种情况时,单核上的CPU使用率达到100%,但磁盘读取速度约为1%。
var dataPoints = _db.DataPoints.AsNoTracking().ToArray();
如果没有AsNoTracking()
,所需的时间会更长。
DataPoint
是一个具有一些基本属性的小POCO。我加载的数据总量是4.5MB。
public class DataPointDto
{
[Key]
public ulong Id { get; set; }
[Required]
public DateTimeOffset TimeStamp { get; set; }
[Required]
public bool trueTime { get; set; }
[Required]
public double Value { get; set; }
}
问:有没有更好的方法来加载这么多的对象,或者我被困在这个级别的性能上了?
有趣的事实: x86需要11秒,x64需要18秒。“优化代码”减少了一秒钟。使用Async
将执行时间推到30秒。
发布于 2016-03-28 02:07:39
大多数答案遵循加载较少数据的常识,但在某些情况下,例如在这里,您绝对肯定地必须加载大量实体。那么我们如何做到这一点呢?
性能不佳的原因
这个操作花这么长时间是不可避免的吗?嗯,不是的。我们只从磁盘加载了一兆字节的数据,性能低下的原因是数据被拆分到40,000个微小实体上。数据库可以处理这一点,但是实体框架似乎很难设置所有这些实体,更改跟踪等。如果我们不打算修改数据,我们可以做很多事情。
我试了三件事
原语
只加载一个属性,然后你就会得到一个原语列表。
List<double> dataPoints = _db.DataPoints.Select(dp => dp.Value).ToList();
这绕过了通常由实体框架执行的所有实体创建。此查询耗时0.4秒,而原始查询为18秒。我们讨论的是45 (!)倍的改进。
匿名类型
当然,大多数时候我们需要的不仅仅是一个基元数组,我们可以在LINQ查询中创建新的对象。实体框架不会创建它通常会创建的实体,并且操作运行得更快。为了方便起见,我们可以使用匿名对象。
var query = db.DataPoints.Select(dp => new {Guid ID = dp.sensorID, DateTimeOffset Timestamp = dp.TimeStamp, double Value = dp.Value});
此操作需要1.2秒,而通常检索相同数量的数据需要18秒。
元组
我发现在我的例子中,使用元组而不是匿名类型略微提高了性能,以下查询的执行速度大约快了30%:
var query = db.DataPoints.Select(dp => Tuple.Create(dp.sensorID, dp.TimeStamp, dp.Value));
其他方式
发布于 2016-03-09 00:02:36
您可以使用不同的技术来加载您的所有项目。
您可以创建自己的逻辑,以便在用户滚动ListView时加载部分数据(我猜您正在使用它)。
幸运的是,UWP有一种简单的方法来完成这项技术。增量加载请参阅文档和示例
发布于 2020-06-29 19:30:08
对2600万条记录(1个datetime,1个double,1个int)进行性能测试,EF Core 3.1.5:
接受答案中建议的
https://stackoverflow.com/questions/35854862
复制相似问题