我一直在阅读有关Javascript性能提升的技巧,还有一条技巧据说可以缓存循环comparer语句中的所有变量(不改变),我想知道这是否也适用于.NET。
假设我有一个简单的for循环,下面哪一个会更快,还是它们是相同的?
无缓存:
for (int i = 0; i < someArray.Length; i++)
{
}加缓存:
for (int i = 0, count = someArray.Length; i < count; i++)
{
}根据文章“缓存”,Length的值减少了循环中的一个操作,因为与访问对象的成员相比,访问局部变量的速度更快。与简单地访问成员相比,声明局部变量实际上更快吗?编译器是否会从中获取并自动缓存该值?声明局部变量对访问成员有什么负面影响吗?
虽然速度可能是这里的一个关键因素,但这并不是唯一的因素。我的下一个问题可能是哪一个更有效率。哪种方法使用较少的内存分配?哪个堆栈操作执行得更少?等等。
从注释来看,访问数组的长度似乎相当快。假设我使用的是IList<>。缓存Count的值比每次迭代都要快吗?
发布于 2013-05-19 07:44:15
在编译语言中,您所做的只是过早优化。我认为解释语言可能会节省一点,但是即使在那里,对于(在我的经验中)编写for循环的一种不寻常的方式来说,您的回报也是非常小的。
为了直接回答C#的问题,不是,编译器不会通过缓存来优化任何东西。在循环期间,我可以很容易地创建一个具有新长度的新数组。因此,每次评估停止条件时,它都会加载数组长度。或者更糟的是,我可能没有使用“传统”风格的停止条件,可能需要评估一个函数才能知道停止。
尽管如此,这里有一个简单的程序:
static void Main( string[] args ) {
int[] integers = new int[] { 1, 2, 3, 4, 5 };
for( int i = 0; i < integers.Length; i++ ) {
Console.WriteLine( i );
}
}下面是IL (删除nops ):
IL_000d: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array,
valuetype [mscorlib]System.RuntimeFieldHandle)
IL_0012: stloc.0
IL_0013: ldc.i4.0
IL_0014: stloc.1
IL_0015: br.s IL_0024
IL_0018: ldloc.1
IL_0019: call void [mscorlib]System.Console::WriteLine(int32)
IL_0020: ldloc.1
IL_0021: ldc.i4.1
IL_0022: add
IL_0023: stloc.1
IL_0024: ldloc.1
IL_0025: ldloc.0
IL_0026: ldlen
IL_0027: conv.i4
IL_0028: clt
IL_002a: stloc.2
IL_002b: ldloc.2
IL_002c: brtrue.s IL_0017这里问题的关键答案是,它将数组推入堆栈中的位置0,然后在IL_0026执行调用以获取数组的长度时,IL_0028执行小于比较的操作,如果计算值为真,则最终返回IL_0017。
通过缓存数组的长度,您要保存的只是一个ldlen和stloc调用。ldlen指令应该是快速的,因为获取数组的长度不会浪费太多时间。
编辑:
与列表的主要区别将是以下指令:
IL_002b: callvirt instance int32 class [mscorlib]System.Collections.Generic.List`1<int32>::get_Count()callvirt将占用更多的时间,但实际上这个函数所做的只是返回一个私有变量。
您最好是担心花费毫秒时间的事情,比如分块数据库调用,或者优化SQL查询,使它们更快,等等,而不是试图减少单个IL操作。
https://stackoverflow.com/questions/16632362
复制相似问题