在 CodeFluent 中使用 List 时收集速度慢
Slow collections when using List in CodeFluent
我在 CodeFluent 中遇到内存泄漏问题,因为对象的 ListCollections 的事件处理程序维护了对我不再需要的对象的引用。解决方案是将实体的集合类型更改为 List 而不是 ListCollection。这解决了内存泄漏问题。
但是,现在我注意到 List 比 ListCollection 慢得多。每次 Codefluent 将对象添加到列表时,如果检查对象是否已经在列表中。这将触发对象中的 BaseContains 方法。 CPU 的 91% 都花在了这里(使用 ANTS 进行分析)。我用 CPU 百分比标记了热路径。
函数 LoadByMainCwEntity 包含以下代码块:
for (readerRead = reader.Read(); ((readerRead == true)
&& ((count < this.MaxCount)
&& (count < pageSize))); readerRead = reader.Read())
{
readCount = (readCount + 1);
if ((CodeFluent.Runtime.CodeFluentPersistence.CanAddEntity(pageIndex, pageSize, pageOptions, readCount) == true))
{
Runtime.CwObject cwObject = new Runtime.CwObject();
((CodeFluent.Runtime.ICodeFluentEntity)(cwObject)).ReadRecord(reader);
91% CPU >> if ((this.BaseContains(cwObject) == false))
{
this.BaseAdd(cwObject);
count = (count + 1);
}
cwObject.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Unchanged;
}
}
这叫做:
protected virtual bool BaseContains(Runtime.CwObject cwObject)
{
if ((cwObject == null))
{
return false;
}
91% CPU >> bool localContains = this.BaseList.Contains(cwObject);
return localContains;
}
这叫做:
public virtual bool Equals(Runtime.CwObject cwObject)
{
if ((cwObject == null))
{
return false;
}
29% CPU >> if ((this.Guid.Equals(CodeFluentPersistence.DefaultGuidValue) == true))
{
return base.Equals(cwObject);
}
45% CPU >> return (this.Guid.Equals(cwObject.Guid) == true);
}
所有的方法都显得轻巧。我认为问题出在命中数上。如果我有一个包含 100.000 个对象的列表并且 Codefluent 添加数字 100.001,它将检查所有 100.000 个其他对象以找到匹配项。不断增长的集合会以指数方式减慢 .Add 方法的速度。
在 Codefluent 的正常 'load' 操作中检查对象是否已经在集合中似乎有点奇怪。是否有任何解决方法,或者我应该接受大型列表在 Codefluent 中真的很慢的事实?
SoftFluent 创建了一个方面,通过删除一些检查和 genericity 来提高生成代码的性能:blog post, code on GitHub。在 Frans Bouma 提供的基准测试中,使用这个方面,CodeFluent Entities 排名第二,仅次于手工编码查询。
如果您只发现少量加载方法在您的应用程序上下文中速度较慢,您可以创建自定义 C# 方法,使用生成的代码来 return 自定义集合。例如,您可以创建一个方法,其中 return 是一个 IEnumerable<T>
而不是生成的集合:
static IEnumerable<Order> LoadOrders()
{
using (IDataReader reader = OrderCollection.PageDataLoadAll(null))
{
while (reader.Read())
{
Order o = new Order();
o.RaisePropertyChangedEvents = false;
((ICodeFluentEntity)o).ReadRecord(reader);
yield return o;
}
CodeFluentPersistence.CompleteCommand(Constants.NorthwindStoreName);
}
}
更新
当您在表面上 select 一个 属性 时,fastReader
属性应该在 属性 网格中可见。您必须 select "Aspect and Producers Properties" 选项卡:
属性由the aspect添加:
<cf:descriptor name="fastReader" targets="Property" defaultValue="false" displayName="Enable Fast Reader" typeName="boolean" description="Determines if the fast reder is enabled for collection loading." category="Faster Reader Aspect" />
方面删除了自动转换 (CodeFluentPersistence.GetReaderValue
) 并期望存储过程 return 所有列。在某些情况下,它可能不起作用。
我在 CodeFluent 中遇到内存泄漏问题,因为对象的 ListCollections 的事件处理程序维护了对我不再需要的对象的引用。解决方案是将实体的集合类型更改为 List 而不是 ListCollection。这解决了内存泄漏问题。
但是,现在我注意到 List 比 ListCollection 慢得多。每次 Codefluent 将对象添加到列表时,如果检查对象是否已经在列表中。这将触发对象中的 BaseContains 方法。 CPU 的 91% 都花在了这里(使用 ANTS 进行分析)。我用 CPU 百分比标记了热路径。
函数 LoadByMainCwEntity 包含以下代码块:
for (readerRead = reader.Read(); ((readerRead == true)
&& ((count < this.MaxCount)
&& (count < pageSize))); readerRead = reader.Read())
{
readCount = (readCount + 1);
if ((CodeFluent.Runtime.CodeFluentPersistence.CanAddEntity(pageIndex, pageSize, pageOptions, readCount) == true))
{
Runtime.CwObject cwObject = new Runtime.CwObject();
((CodeFluent.Runtime.ICodeFluentEntity)(cwObject)).ReadRecord(reader);
91% CPU >> if ((this.BaseContains(cwObject) == false))
{
this.BaseAdd(cwObject);
count = (count + 1);
}
cwObject.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Unchanged;
}
}
这叫做:
protected virtual bool BaseContains(Runtime.CwObject cwObject)
{
if ((cwObject == null))
{
return false;
}
91% CPU >> bool localContains = this.BaseList.Contains(cwObject);
return localContains;
}
这叫做:
public virtual bool Equals(Runtime.CwObject cwObject)
{
if ((cwObject == null))
{
return false;
}
29% CPU >> if ((this.Guid.Equals(CodeFluentPersistence.DefaultGuidValue) == true))
{
return base.Equals(cwObject);
}
45% CPU >> return (this.Guid.Equals(cwObject.Guid) == true);
}
所有的方法都显得轻巧。我认为问题出在命中数上。如果我有一个包含 100.000 个对象的列表并且 Codefluent 添加数字 100.001,它将检查所有 100.000 个其他对象以找到匹配项。不断增长的集合会以指数方式减慢 .Add 方法的速度。
在 Codefluent 的正常 'load' 操作中检查对象是否已经在集合中似乎有点奇怪。是否有任何解决方法,或者我应该接受大型列表在 Codefluent 中真的很慢的事实?
SoftFluent 创建了一个方面,通过删除一些检查和 genericity 来提高生成代码的性能:blog post, code on GitHub。在 Frans Bouma 提供的基准测试中,使用这个方面,CodeFluent Entities 排名第二,仅次于手工编码查询。
如果您只发现少量加载方法在您的应用程序上下文中速度较慢,您可以创建自定义 C# 方法,使用生成的代码来 return 自定义集合。例如,您可以创建一个方法,其中 return 是一个 IEnumerable<T>
而不是生成的集合:
static IEnumerable<Order> LoadOrders()
{
using (IDataReader reader = OrderCollection.PageDataLoadAll(null))
{
while (reader.Read())
{
Order o = new Order();
o.RaisePropertyChangedEvents = false;
((ICodeFluentEntity)o).ReadRecord(reader);
yield return o;
}
CodeFluentPersistence.CompleteCommand(Constants.NorthwindStoreName);
}
}
更新
当您在表面上 select 一个 属性 时,fastReader
属性应该在 属性 网格中可见。您必须 select "Aspect and Producers Properties" 选项卡:
属性由the aspect添加:
<cf:descriptor name="fastReader" targets="Property" defaultValue="false" displayName="Enable Fast Reader" typeName="boolean" description="Determines if the fast reder is enabled for collection loading." category="Faster Reader Aspect" />
方面删除了自动转换 (CodeFluentPersistence.GetReaderValue
) 并期望存储过程 return 所有列。在某些情况下,它可能不起作用。