首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在“loop”循环中缓存变量

在“loop”循环中缓存变量
EN

Stack Overflow用户
提问于 2013-05-19 07:29:43
回答 4查看 5.5K关注 0票数 8

我一直在阅读有关Javascript性能提升的技巧,还有一条技巧据说可以缓存循环comparer语句中的所有变量(不改变),我想知道这是否也适用于.NET。

假设我有一个简单的for循环,下面哪一个会更快,还是它们是相同的?

无缓存:

代码语言:javascript
运行
复制
for (int i = 0; i < someArray.Length; i++)
{

}

加缓存:

代码语言:javascript
运行
复制
for (int i = 0, count = someArray.Length; i < count; i++)
{

}

根据文章“缓存”,Length的值减少了循环中的一个操作,因为与访问对象的成员相比,访问局部变量的速度更快。与简单地访问成员相比,声明局部变量实际上更快吗?编译器是否会从中获取并自动缓存该值?声明局部变量对访问成员有什么负面影响吗?

虽然速度可能是这里的一个关键因素,但这并不是唯一的因素。我的下一个问题可能是哪一个更有效率。哪种方法使用较少的内存分配?哪个堆栈操作执行得更少?等等。

从注释来看,访问数组的长度似乎相当快。假设我使用的是IList<>。缓存Count的值比每次迭代都要快吗?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-05-19 07:44:15

在编译语言中,您所做的只是过早优化。我认为解释语言可能会节省一点,但是即使在那里,对于(在我的经验中)编写for循环的一种不寻常的方式来说,您的回报也是非常小的。

为了直接回答C#的问题,不是,编译器不会通过缓存来优化任何东西。在循环期间,我可以很容易地创建一个具有新长度的新数组。因此,每次评估停止条件时,它都会加载数组长度。或者更糟的是,我可能没有使用“传统”风格的停止条件,可能需要评估一个函数才能知道停止。

尽管如此,这里有一个简单的程序:

代码语言:javascript
运行
复制
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 ):

代码语言:javascript
运行
复制
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指令应该是快速的,因为获取数组的长度不会浪费太多时间。

编辑:

与列表的主要区别将是以下指令:

代码语言:javascript
运行
复制
IL_002b:  callvirt   instance int32 class [mscorlib]System.Collections.Generic.List`1<int32>::get_Count()

callvirt将占用更多的时间,但实际上这个函数所做的只是返回一个私有变量。

您最好是担心花费毫秒时间的事情,比如分块数据库调用,或者优化SQL查询,使它们更快,等等,而不是试图减少单个IL操作。

票数 0
EN

Stack Overflow用户

发布于 2017-12-04 18:42:15

只是为了提供一个不同的答案。在某些特殊情况下,缓存长度值可能对您有所帮助。

在我的例子中,我们使用带有的示例代码进行ui测试。代码如下所示:

代码语言:javascript
运行
复制
for (int i = 0; i < Grid.VisibleRows.Length; i++) {
    // do something
}

我们的网格只是一个表示html表的类。VisibleRows得到一个IWebElement[],然后使用Length属性。每一次迭代都意味着去UI并得到所有的行。

当然,另一个实现可能是只访问UI一次并获得长度(而不是计算内存中的行数),但是仍然将代码移出for循环意味着性能上的改进--我将返回UI的往返次数从多次减少到了一次。

所以代码现在看起来如下:

代码语言:javascript
运行
复制
var rowsLength = Grid.VisibleRows.Length;
 for (int i = 0; i < rowsLength ; i++) {
        // do something
    }

对我们来说,手动缓存起作用了。使用秒表,我们检查了是否能够将代码的该部分的时间从32993 ms缩短到12020 MS。

票数 2
EN

Stack Overflow用户

发布于 2013-05-19 07:51:18

我曾经尝试过缓存和array.Length。Array.Length更快。我认为,这是因为虚拟机的内部结构和“安全”。当您使用.Length表示法时,它认为它不会溢出数组。对于变量,它是未知的,并进行额外的测试。汇编代码看上去是一种方式,但虚拟机的内部行为则是另一回事。

但另一方面,您正在进行过早的优化。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16632362

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档