IEnumerable<T>.Count() 的性能 - 迭代值类型和引用类型之间的区别
Performance of IEnumerable<T>.Count() - difference between iterating over value types and reference types
我正在尝试优化集合中的计数元素 (IEnumerable<T>
)
由于此数据是从内存缓存加载的,因此没有必要切换到使用 IQueryable<T>.Count()
(为了进行简单的数据库调用 SELECT count(1) FROM TableT
而不是遍历 [=11 的整个列表=])
所以我有一个想法,也许遍历值类型(例如 int)的集合 (IEnumerable) 可能比遍历引用类型的集合更快
myEnumerable.Count();
替换为:
myEnumerable.Select(el => el.Id).Count();
不知道它是否正确,也不确定如何正确测试它,但初步的简单估计表明迭代 IEnumerable<int>
比迭代 [=18 稍微快一点(比如 5-10%) =]
所以,问题是 - 无论集合是值类型还是引用类型,遍历集合有什么不同吗?
myEnumerable.Count();
replaced with this:
myEnumerable.Select(el => el.Id).Count();
那绝对是摔倒的表现!我不知道你如何比较这些得出更好的结论,但你可以按如下方式轻松比较它们:
class TestObj
{
public int Id { get; set; }
}
static void Main(string[] args)
{
var dataRef = Enumerable.Empty<TestObj>();
for (int i = 0; i < 100000; i++)
{
dataRef = dataRef.Append(new TestObj { Id = i });
}
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 100000; i++)
{
dataRef.Count();
}
sw.Stop();
Console.WriteLine($"By Ref: {sw.ElapsedMilliseconds / 1000.0} Sec");
sw.Restart();
var dataVal = dataRef.Select(p => p.Id);
for (int i = 0; i < 100000; i++)
{
dataVal.Count();
}
Console.WriteLine($"By Val: {sw.ElapsedMilliseconds / 1000.0} Sec");
}
结果:
By Ref: 0.002 Sec
By Val: 4.111 Sec
一般来说ValueType
和ReferenceType
如果我们把它们独立的看成是两个可枚举的,在迭代时间和计数上没有什么明显的不同。
但是您上面所做的是在引用类型可枚举上添加附加枚举以将其转换为值类型可枚举,这会导致许多开销和性能下降。
如果您关心性能并且准备好通过内存支付成本,您可以使用 .ToList()
然后 .Count()
将接近于零。
我正在尝试优化集合中的计数元素 (IEnumerable<T>
)
由于此数据是从内存缓存加载的,因此没有必要切换到使用 IQueryable<T>.Count()
(为了进行简单的数据库调用 SELECT count(1) FROM TableT
而不是遍历 [=11 的整个列表=])
所以我有一个想法,也许遍历值类型(例如 int)的集合 (IEnumerable) 可能比遍历引用类型的集合更快
myEnumerable.Count();
替换为:
myEnumerable.Select(el => el.Id).Count();
不知道它是否正确,也不确定如何正确测试它,但初步的简单估计表明迭代 IEnumerable<int>
比迭代 [=18 稍微快一点(比如 5-10%) =]
所以,问题是 - 无论集合是值类型还是引用类型,遍历集合有什么不同吗?
myEnumerable.Count();
replaced with this:
myEnumerable.Select(el => el.Id).Count();
那绝对是摔倒的表现!我不知道你如何比较这些得出更好的结论,但你可以按如下方式轻松比较它们:
class TestObj
{
public int Id { get; set; }
}
static void Main(string[] args)
{
var dataRef = Enumerable.Empty<TestObj>();
for (int i = 0; i < 100000; i++)
{
dataRef = dataRef.Append(new TestObj { Id = i });
}
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 100000; i++)
{
dataRef.Count();
}
sw.Stop();
Console.WriteLine($"By Ref: {sw.ElapsedMilliseconds / 1000.0} Sec");
sw.Restart();
var dataVal = dataRef.Select(p => p.Id);
for (int i = 0; i < 100000; i++)
{
dataVal.Count();
}
Console.WriteLine($"By Val: {sw.ElapsedMilliseconds / 1000.0} Sec");
}
结果:
By Ref: 0.002 Sec
By Val: 4.111 Sec
一般来说ValueType
和ReferenceType
如果我们把它们独立的看成是两个可枚举的,在迭代时间和计数上没有什么明显的不同。
但是您上面所做的是在引用类型可枚举上添加附加枚举以将其转换为值类型可枚举,这会导致许多开销和性能下降。
如果您关心性能并且准备好通过内存支付成本,您可以使用 .ToList()
然后 .Count()
将接近于零。