您的位置:首页 > 其它

深入了解 foreach 和 for 循环到底有哪些不同 (二)

2008-08-21 22:30 691 查看
说明一下,本人都是利用工作后的业余时间无条件的为cnblogs写些技术心得.不管写的好还是不好,多还是少都只是希望更多的人能够收益,同时通过分析来提高自己的理解.希望大家能够相互尊重彼此的劳动.

另外我也理解汇编代码可能对大部分人来说太复杂也觉得不实用. 确实,如果只是停留在基础编程的话,可能这是多余的,但是如果希望自己能够深入理解clr的话,这可能又是必须的.

闲话少说,来看看ForeachTest是怎么工作的. (注意黄色标识出来的代码)

首先通过!name2ee test Test.Program.ForeachTest找到编译后的地址,然后!u反编译.具体过程可以参考上一篇.

具体代码和注释如下:

>>> 00e30138 55 push ebp

00e30139 8bec mov ebp,esp

00e3013b 57 push edi

00e3013c 56 push esi

00e3013d 53 push ebx

00e3013e 83ec18 sub esp,18h

00e30141 33c0 xor eax,eax //eax 清零

00e30143 8945e0 mov dword ptr [ebp-20h],eax //置零

00e30146 8945e4 mov dword ptr [ebp-1Ch],eax //置零

00e30149 8945e8 mov dword ptr [ebp-18h],eax //置零

00e3014c 8945ec mov dword ptr [ebp-14h],eax //置零

00e3014f 33c0 xor eax,eax //eax 清零

00e30151 8945e8 mov dword ptr [ebp-18h],eax //置零

00e30154 8bf1 mov esi,ecx //Samples集合对象地址放入esi

00e30156 3906 cmp dword ptr [esi],eax

00e30158 33d2 xor edx,edx //edx置零

00e3015a 8b4610 mov eax,dword ptr [esi+10h] //保存Samples集合对象的_version进eax,这里_version是3

00e3015d 8945dc mov dword ptr [ebp-24h],eax //再把_version存入指定内存地址

00e30160 8bda mov ebx,edx //ebx置零

00e30162 eb14 jmp 00e30178 //无条件跳转到 00e30178

00e30164 8b7a04 mov edi,dword ptr [edx+4] //取出Sample对象的Value并存入edit,这里也可以说是循环开始处

*** WARNING: Unable to verify checksum for C:"Windows"assembly"NativeImages_v2.0.50727_32"mscorlib"9adb89fa22fd5b4ce433b5aca7fb1b07"mscorlib.ni.dll

00e30167 e834d1b966 call mscorlib_ni+0x22d2a0 (679cd2a0) (System.Console.get_Out(), mdToken: 06000772) //获取Console.Out

00e3016c 8bc8 mov ecx,eax //保存Console.Out到ecx

00e3016e 8bd7 mov edx,edi //保存edi到edx

00e30170 8b01 mov eax,dword ptr [ecx] //保存Console.Out的method table到eax

00e30172 ff90bc000000 call dword ptr [eax+0BCh] //打印Value值到屏幕

00e30178 8b45dc mov eax,dword ptr [ebp-24h] //读出_version到eax

00e3017b 3b4610 cmp eax,dword ptr [esi+10h] //比较_version与指定内存地址

00e3017e 7519 jne 00e30199 //不相等就跳转到00e30199

00e30180 3b5e0c cmp ebx,dword ptr [esi+0Ch] //比较ebx与Samples集合的_size

00e30183 7314 jae 00e30199 //大于等于就跳转到 00e30199

00e30185 8b4604 mov eax,dword ptr [esi+4] //保存Sample集合对象中的_items对象地址到eax(_items就是一个object数组内存用来存储List集合中的数据)

00e30188 3b5804 cmp ebx,dword ptr [eax+4] //从内存中获取_items的元素个数并与ebx比较

00e3018b 733f jae 00e301cc //大于等于就跳转到JIT_RngChkFail

00e3018d 8b54980c mov edx,dword ptr [eax+ebx*4+0Ch] //以ebx中的值为索引获取一个Sample对象并存入edx

00e30191 43 inc ebx //ebx加1

00e30192 b801000000 mov eax,1 //eax置1

00e30197 eb1a jmp 00e301b3 //无条件跳转到00e301b3,就是下面test指令处

00e30199 8b45dc mov eax,dword ptr [ebp-24h] //读出_version到eax

00e3019c 3b4610 cmp eax,dword ptr [esi+10h] //比较_version与指定内存地址

00e3019f 740a je 00e301ab //相等就跳转到00e301ab

00e301a1 b920000000 mov ecx,20h

00e301a6 e8e1abff66 call mscorlib_ni+0x68ad8c (67e2ad8c) (System.ThrowHelper.ThrowInvalidOperationException(System.ExceptionResource), mdToken: 060000e2)

00e301ab 8b5e0c mov ebx,dword ptr [esi+0Ch] //读出Sample对象的_size,应该为3

00e301ae 43 inc ebx //ebx加1

00e301af 33d2 xor edx,edx //edx置零

00e301b1 33c0 xor eax,eax //eax置零

00e301b3 85c0 test eax,eax //循环没有结束的时候eax总是1,如果循环要结束了eax是0

00e301b5 75ad jne 00e30164 //eax是1时跳到循环开始处

00e301b7 c745e400000000 mov dword ptr [ebp-1Ch],0

00e301be c745e8fc000000 mov dword ptr [ebp-18h],0FCh

00e301c5 68dc01e300 push 0E301DCh

00e301ca eb05 jmp 00e301d1

00e301cc e8ebbe1c68 call mscorwks!JIT_RngChkFail (68ffc0bc)

00e301d1 58 pop eax

00e301d2 ffe0 jmp eax

00e301d4 8d65f4 lea esp,[ebp-0Ch]

00e301d7 5b pop ebx

00e301d8 5e pop esi

00e301d9 5f pop edi

00e301da 5d pop ebp

00e301db c3 ret

仔细比较Fortest和ForeachTest,你会发现区别所在.

1. ForTest使用edi 做为累加器和List的_size比较, 比较结果做为是循环是否继续的决定条件. ForeachTest在进入循环以前会先保存List的_version并在循环中比较. _version和_size两个是决定是否继续循环的条件.

2. List中的_version和_size 的值是一样的. 如果在循环过程中List中的值添加或减少, 新的_version就会和老的(循环进入前保存)的_version不一样,从而导致InvalidOperationException抛出.

3. 因为ForeachTest比ForTest多了一个判断条件,所以理论上ForeachTest的性能应该低于ForTest. (注意这里是说低,但低多少要根据实际情况做测试)

经过笔者的实际测试,分别对两个循环连续运行1,000,000次在一台奔4有3g内存的机器上, ForTest比ForeachTest快100多毫秒.

到底是for 还是 foreach呢?

笔者认为虽然for可以比foreach带来微小的性能提升,但是foreach的代码更容易理解.对于大部分程序来说,这点性能提升是十分微不足道的. 另外要意识到foreach操作的集合类型中,循环中添加,删除都是不允许的, 但是for在这方面更容易控制.

最后送给大家一段话来自微软的Chief Architect of Visual Studio, Rico Mariani, 说的基本规则关于performance.

Rule #1: Measure

Rule #2: Do Your Homework
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: