Медленные коллекции при использовании List в CodeFluent
У меня были проблемы утечки памяти в CodeFluent, потому что обработчики событий ListCollections объектов сохраняли ссылки на объекты, которые мне больше не нужны. Решением было изменить тип коллекции объекта на List вместо ListCollection. Это решило проблемы утечки памяти.
Однако теперь я заметил, что List НАМНОГО медленнее, чем ListCollection. Каждый раз, когда Codefluent добавляет объект в список, проверяет, есть ли объект уже в списке. Это запустит метод BaseContains в объекте. Здесь тратится 91% ЦП (профилирование с использованием ANTS). Я отметил Горячий путь с процентом процессора.
Функция 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);
}
Все методы кажутся легкими. Я думаю, что проблема кроется в HitCount. Если у меня есть список из 100 000 объектов, и Codefluent добавляет число 100,001, он проверит все 100 000 других, чтобы найти совпадение. Растущая коллекция будет экспоненциально замедлять метод. Add.
Кажется немного странным проверять, находится ли объект уже в коллекции в ходе обычной операции загрузки Codefluent. Есть ли обходной путь, или я должен просто жить с тем фактом, что большие списки действительно медленно работают в Codefluent?
1 ответ
SoftFluent создал аспект, который улучшает производительность сгенерированного кода путем удаления некоторых проверок и универсальности: запись в блоге, код на GitHub. В тесте, предоставленном Франсом Боума, с использованием этого аспекта, CodeFluent Entities занимает второе место сразу после запроса, закодированного вручную.
Если вы определили лишь небольшое количество методов загрузки, которые медленны в контексте вашего приложения, вы можете создать собственные методы C#, которые используют сгенерированный код для возврата пользовательской коллекции. Например, вы можете создать метод, который возвращает 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);
}
}
Обновить
fastReader
атрибут должен быть виден в сетке свойств при выборе свойства на поверхности. Вы должны выбрать вкладку "Свойства аспекта и производителя":
Свойство добавляется аспектом:
<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
) и ожидаем, что хранимые процедуры вернут все столбцы. В некоторых случаях это может не сработать.