Редактировать: Мой тест был изменен, так как был обнаружен недостаток в способе проведения теста.
Недавно я столкнулся с некоторыми проблемами производительности с Fluent Nhibernate, и я столкнулся с тем, что мне показалось очень странным. Когда я сделал IEnumerable, производительность List резко возросла. Я пытался понять, почему. Похоже, что это не так, и Google ничего не нашел.
Вот базовый тест, который я провел:
//Class has various built in type fields, but no references to anything
public class Something
{
public int ID;
public decimal Value;
}
var someRepository = new Repository(uow);
//RUN 1
var start = DateTime.Now;
// Returns a IEnumerable from a session.Linq<SomeAgg> based on the passed in parameters, nothing fancy. Has about 1300 rows that get returned.
var somethings = someRepository.GetABunchOfSomething(various, parameters);
var returnValue = SumAllFunction(somethings);
var timeSpent = DateTime.Now - start; //Takes {00:00:00.3580358} on my box
//RUN2
var start2 = DateTime.Now;
var returnValue = someFunction(somethings);
var timeSpent = DateTime.Now - start2; //Takes {00:00:00.0560000} on my box
public decimal SumAllFunction(IEnumerable<Something> somethings)
{
return somethings.Sum(x => x.Value); //Value is a decimal that's part of the Something class
}
Теперь, если я возьму тот же код и просто изменю строку someRepository.GetABunchOfSomethingto и присоединен .ToList ():
//RUN 1
var start = DateTime.Now;
var somethings = someRepository.GetABunchOfSomething(various, parameters).ToList();
var returnValue = SumAllFunction(somethings);
var timeSpent = DateTime.Now - start; //Takes {00:00:00.3580358} on my box
//RUN 2
var start2 = DateTime.Now;
var returnValue = SumAllFunction(somethings);
var timeSpent = DateTime.Now - start2; //Takes {00:00:00.0010000} on my box
Больше ничего не изменилось. Эти результаты очень повторяемы. Так что это не просто вопрос времени.
Версия TLDR:
При запуске одного и того же IEnumerable через цикл дважды, второй запуск занимает в 10-20 раз больше времени, чем если бы я изменил IEnumerable на List с использованием .ToList (), прежде чем запускать его через 2 цикла.
Я проверил SQL, и когда это List, sql запускается только один раз и, кажется, кешируется и используется снова, вместо того, чтобы возвращаться в базу данных для получения результатов.
Если это IEnumerable, то каждый раз, когда он обращается к дочерним элементам IEnumerable, он совершает поездку в базу данных для их регидратации.
Я понимаю, что вы не можете добавлять / удалять из IEnumerable, но, насколько я понимаю, IEnumerable был бы изначально заполнен объектами-посредниками, а затем объекты-посредники впоследствии были бы гидратированы при необходимости. После того, как они были увлажнены, вам больше не нужно возвращаться в БД, но, похоже, это не так. У меня, очевидно, есть обходной путь для этого, но я подумал, что это странно, и мне было любопытно, почему он ведет себя так, как он делает.