[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖
  caruko很有研究和较真的精神,先在这里赞一个!的确,我们在平常一直要求新手写代码的执行效率要高。

这只是为了大家养成一个好的习惯而已。而其实我们在实际中处理问题都是现写现用,有时根本没有去考虑代码

执行效率的问题,只求能把问题解决了就好。毕竟程序再怎么慢也要比人手动快n倍,说句俏皮话:让代码飞一会

儿。当然,在学习研究的时候我们还是要注重这个代码执行效率的问题。

  当代码运行时产生大量的变量肯定是对运行时间有影响的,因为变量是储存在内存中的,也就是说变量越多

程序消耗的内存就越大,大家都知道当内存占用率接近100%时,用电脑做任何事情都是很慢很慢的,在这里

是一个道理,当程序运行时产生足够多的变量时,它就会占去绝大部分的内存空间而导致电脑变慢甚至死机(

不信的话就用批去定义一亿个变量)。大家注意在这里变量所影响的并不是程序的执行效率而是电脑的运算处

理速度,所以个人认为只要电脑的内存足够大,大量的变量的存在是不会对程序的执行效率带来多大的影响的。
***共同提高***

TOP

大家是不是对代码执行效率有了误解?如对一个变量操作的时间为一个单位,同时对10000个变量操作的时间小于或等于一万个单位,这样的执行效率则是正常。但如对一个变量操作的时间为一个单位,而对10000个变量操作的时间远远大于一万个单位,这样的执行效率就不是正常的,也才能说因为大量的变量的存在让代码执行效率有了改变。
    至于caruko所提出的读取_1和_9999的速度不一样,我认为存在误区,以下面的代码来证明:
  1. @echo off
  2. for /l %%a in (1,1,10000) do set "_%%a=a"
  3. echo %time%
  4. echo %_1%
  5. echo %time%
  6. echo %_9999%
  7. echo %time%
  8. pause>nul
复制代码
我本机上一次运行结果如下(几乎都是不需要时间的,何来的差距很大):
18:59:59.82
a
18:59:59.82
a
18:59:59.82
***共同提高***

TOP

13# caruko
代码:
  1. @echo off
  2. echo %time%
  3. for /l %%a in (1,1,10000) do set /a _9+=1
  4. echo %time%
  5. for /l %%a in (1,1,10000) do set /a _10000+=1
  6. echo %time%
  7. pause>nul
复制代码
运行结果:
19:12:49.35
19:12:49.57
19:12:49.79
哪来的巨大的相差?
***共同提高***

TOP

16# caruko
看我13楼是不是10000次?
***共同提高***

TOP

真的是没有调查就没有发言权,我之前有点想当然了。。。

但是按理来说,变量在内存中的存储应该是堆栈,先入的后出,也就是说在定义_1-_10000的变量时,_1变量

在堆栈中应该是放在堆栈的最下面的,而_10000变量是在堆栈的顶部,如果遵循先入后出的原则,则读取_10

000变量的时间是最短的,而读取_1变量的时间是最长的。

但是从caruko的测试中我们可以看出读取_1和_10000变量的时间是差不多的,而读取_9和_9999变量的时

间也是差不多的,但是读取_1和_9以及读取_9999和_10000的时间却出现了很大的相差。这不得不让我们

从cmd默认排序机制上猜想,在内存中变量_1至_10000的排列是_9999置于最底部,接着是_9998.......

_9990,_999,_9989....._9980,_998,_9979......._10000,_1000,_100,_10,_1,_1是置于最顶部。

这一猜测几乎可以推翻开始堆栈的设想,除非在堆栈中变量名也存在排序的机制。真的希望有牛人能找到变量

在内存的存放地址,这样真相才能大白于天下。
***共同提高***

TOP

可能以下代码能说明一点问题:
  1. @echo off&setlocal enabledelayedexpansion
  2. for /l %%a in (1,1,1000) do set /a "_%%a=1"
  3. for /l %%a in (1,1,100) do (
  4.     set "t=!time!"
  5.     for /l %%b in (1,1,10000) do set /a _%%a+=1
  6.     set "t1=!time!"&set "a="
  7.     if "!t:~,2!" lss "!t1:~,2!" set "a=+24"
  8.     set /a ".%%a=(!t1:~,2!-!t:~,2!!a!)*360000+(1!t1:~3,2!%%100-1!t:~3,2!%%100)*6000+(1!t1:~6,2!%%100-1!t:~6,2!%%100)*100+(1!t1:~-2!%%100-1!t:~-2!%%100)"
  9. )
  10. set .
  11. pause>nul
复制代码
这是将_1到_100变量计算10000次的时间(ms)用.1到.100变量储存然后列出,本机上运行结果如下:
  1. .1=50
  2. .10=54
  3. .100=69
  4. .11=54
  5. .12=57
  6. .13=57
  7. .14=59
  8. .15=63
  9. .16=62
  10. .17=66
  11. .18=67
  12. .19=70
  13. .2=62
  14. .20=68
  15. .21=70
  16. .22=72
  17. .23=73
  18. .24=75
  19. .25=78
  20. .26=78
  21. .27=80
  22. .28=85
  23. .29=82
  24. .3=78
  25. .30=83
  26. .31=85
  27. .32=87
  28. .33=88
  29. .34=89
  30. .35=90
  31. .36=96
  32. .37=93
  33. .38=97
  34. .39=100
  35. .4=91
  36. .40=97
  37. .41=100
  38. .42=101
  39. .43=103
  40. .44=105
  41. .45=108
  42. .46=109
  43. .47=111
  44. .48=114
  45. .49=116
  46. .5=106
  47. .50=112
  48. .51=114
  49. .52=118
  50. .53=118
  51. .54=119
  52. .55=123
  53. .56=124
  54. .57=126
  55. .58=127
  56. .59=128
  57. .6=119
  58. .60=130
  59. .61=128
  60. .62=133
  61. .63=133
  62. .64=134
  63. .65=137
  64. .66=138
  65. .67=140
  66. .68=141
  67. .69=144
  68. .7=131
  69. .70=143
  70. .71=146
  71. .72=146
  72. .73=147
  73. .74=152
  74. .75=151
  75. .76=154
  76. .77=154
  77. .78=156
  78. .79=159
  79. .8=145
  80. .80=158
  81. .81=160
  82. .82=161
  83. .83=166
  84. .84=175
  85. .85=194
  86. .86=178
  87. .87=174
  88. .88=173
  89. .89=177
  90. .9=159
  91. .90=173
  92. .91=175
  93. .92=176
  94. .93=180
  95. .94=180
  96. .95=182
  97. .96=185
  98. .97=184
  99. .98=191
  100. .99=187
复制代码
从这个运行结果可以看出确实变量名的存储和读取是按照cmd默认序列进行的。
***共同提高***

TOP

再测试(这下等得我。。。。)
代码:
  1. @echo off&setlocal enabledelayedexpansion
  2. for /l %%a in (1,1,10000) do set /a "_%%a=1"
  3. for /l %%a in (1,1,100) do (
  4.     set "t=!time!"
  5.     for /l %%b in (1,1,10000) do set /a _%%a+=1
  6.     set "t1=!time!"&set "a="
  7.     if "!t:~,2!" lss "!t1:~,2!" set "a=+24"
  8.     set /a ".%%a=(!t1:~,2!-!t:~,2!!a!)*360000+(1!t1:~3,2!%%100-1!t:~3,2!%%100)*6000+(1!t1:~6,2!%%100-1!t:~6,2!%%100)*100+(1!t1:~-2!%%100-1!t:~-2!%%100)"
  9. )
  10. set .
  11. pause>nul
复制代码
运行结果:
  1. .1=389
  2. .10=390
  3. .100=413
  4. .11=406
  5. .12=426
  6. .13=439
  7. .14=466
  8. .15=473
  9. .16=502
  10. .17=514
  11. .18=520
  12. .19=546
  13. .2=528
  14. .20=520
  15. .21=537
  16. .22=555
  17. .23=577
  18. .24=590
  19. .25=606
  20. .26=625
  21. .27=647
  22. .28=652
  23. .29=677
  24. .3=654
  25. .30=657
  26. .31=678
  27. .32=697
  28. .33=717
  29. .34=736
  30. .35=738
  31. .36=758
  32. .37=786
  33. .38=799
  34. .39=810
  35. .4=782
  36. .40=791
  37. .41=804
  38. .42=823
  39. .43=841
  40. .44=855
  41. .45=872
  42. .46=887
  43. .47=906
  44. .48=926
  45. .49=943
  46. .5=918
  47. .50=918
  48. .51=961
  49. .52=969
  50. .53=978
  51. .54=992
  52. .55=1011
  53. .56=1018
  54. .57=1033
  55. .58=1059
  56. .59=1067
  57. .6=1050
  58. .60=1052
  59. .61=1071
  60. .62=1085
  61. .63=1107
  62. .64=1118
  63. .65=1136
  64. .66=1154
  65. .67=1176
  66. .68=1200
  67. .69=1216
  68. .7=1179
  69. .70=1192
  70. .71=1206
  71. .72=1224
  72. .73=1242
  73. .74=1254
  74. .75=1280
  75. .76=1302
  76. .77=1322
  77. .78=1324
  78. .79=1345
  79. .8=1330
  80. .80=1356
  81. .81=1372
  82. .82=1366
  83. .83=1387
  84. .84=1395
  85. .85=1425
  86. .86=1444
  87. .87=1452
  88. .88=1461
  89. .89=1483
  90. .9=1455
  91. .90=1473
  92. .91=1486
  93. .92=1520
  94. .93=1522
  95. .94=1536
  96. .95=1549
  97. .96=1566
  98. .97=1586
  99. .98=1644
  100. .99=1646
复制代码
***共同提高***

TOP

本帖最后由 batman 于 2011-4-23 01:38 编辑

接着测试(又是一次头大的等待)
代码:
  1. @echo off&setlocal enabledelayedexpansion
  2. for /l %%a in (1,1,10000) do set /a "_%%a=1"
  3. for /l %%a in (1,1,9) do (
  4.     set "num="
  5.     for /l %%b in (1,1,4) do (
  6.         set "num=!num!%%a"
  7.         for %%a in (!num!) do (
  8.             set "t=!time!"
  9.             for /l %%b in (1,1,10000) do set /a _%%a+=1
  10.             set "t1=!time!"&set "a="
  11.             if "!t:~,2!" lss "!t1:~,2!" set "a=+24"
  12.             set /a ".%%a=(!t1:~,2!-!t:~,2!!a!)*360000+(1!t1:~3,2!%%100-1!t:~3,2!%%100)*6000+(1!t1:~6,2!%%100-1!t:~6,2!%%100)*100+(1!t1:~-2!%%100-1!t:~-2!%%100)"
  13.        )
  14.     )
  15. )
  16. set .
  17. pause>nul
复制代码
运行结果:
  1. .1=393
  2. .11=440
  3. .111=409
  4. .1111=411
  5. .2=525
  6. .22=559
  7. .222=566
  8. .2222=562
  9. .3=657
  10. .33=711
  11. .333=717
  12. .3333=722
  13. .4=796
  14. .44=866
  15. .444=877
  16. .4444=876
  17. .5=928
  18. .55=1019
  19. .555=1036
  20. .5555=1028
  21. .6=1065
  22. .66=1166
  23. .666=1181
  24. .6666=1183
  25. .7=1191
  26. .77=1317
  27. .777=1336
  28. .7777=1342
  29. .8=1353
  30. .88=1483
  31. .888=1490
  32. .8888=1507
  33. .9=1476
  34. .99=1633
  35. .999=1655
  36. .9999=1656
复制代码
***共同提高***

TOP

本帖最后由 batman 于 2011-4-23 02:06 编辑

上面的三次数据测试基本可以证明:

    cmd对变量的储存和读取是遵循其默认的序列机制的,即cmd会将当前设置的变量+=号+值的所有字符放在

一起进行正序排列(ansi编码序列),排前的字符放入内存中靠前的地址,而将排后的字符放入内存中靠后的地

址。然后在后面的读取寻址中则是一个遍历的过程,即首先查找比对存入最前地址中的字符,如果相符则取出

,如不相符则对下一个地址进行比对,一直到找到完全的匹配。也就是说当内存中储存的变量越多时,而要取

出后面地址中储存的变量所要消耗的时间越长(如在前面地址中则无明显时间的变化),也就是说当出现大量

变量时,对于排序在前的变量在程序中的运算影响并不是很大,而对排序在后的变量在程序中的运算这个影响

将是巨大的。由此可见cmd无论从哪个方面都逃不开其根本的序列机制(字符的ansi编码序列)。
***共同提高***

TOP

hanyeguxing的debug变量储存展示已经力证了变量存储是按照变量名+=号+值全字符的ansi序列先后进行的,为什么还要强调变量的读取又不是按照这一基本序列机制呢?
***共同提高***

TOP

hanyeguxing的debug变量储存展示已经力证了变量存储是按照变量名+=号+值全字符的ansi序列先后进行的,为什么还要强调变量的读取又不是按照这一基本序列机制呢?
***共同提高***

TOP

本帖最后由 batman 于 2011-4-23 11:19 编辑

加入debug的测试
代码:
  1. @echo off&setlocal enabledelayedexpansion
  2. call :lp 1 10 random
  3. call :lp 11 100 random
  4. del /q db
  5. goto :eof
  6. :lp
  7. for /l %%a in (%1,1,%2) do set "_%%a=!%3!"
  8. (echo d2cl2
  9. echo q
  10. echo,
  11. )>db
  12. (for /f "skip=1 tokens=2,3" %%a in ('debug^<db') do (
  13.      echo d%%b%%a:0380&echo q&echo,
  14. ))>tem
  15. del /q db&ren tem db
  16. debug<db>%1.txt
  17. start %1.txt
复制代码
1.txt
  1. -d0D96:0380
  2. 0D96:0380  00 5F 31 3D 36 31 33 35-00 5F 31 30 3D 32 38 36   ._1=6135._10=286
  3. 0D96:0390  31 32 00 5F 32 3D 32 39-36 38 30 00 5F 33 3D 32   12._2=29680._3=2
  4. 0D96:03A0  33 30 36 39 00 5F 34 3D-33 31 32 32 37 00 5F 35   3069._4=31227._5
  5. 0D96:03B0  3D 38 34 37 37 00 5F 36-3D 32 33 39 38 38 00 5F   =8477._6=23988._
  6. 0D96:03C0  37 3D 33 32 36 31 36 00-5F 38 3D 32 39 35 38 00   7=32616._8=2958.
  7. 0D96:03D0  5F 39 3D 32 37 37 32 31-00 42 4C 41 53 54 45 52   _9=27721.BLASTER
  8. 0D96:03E0  3D 41 32 32 30 20 49 35-20 44 31 20 50 33 33 30   =A220 I5 D1 P330
  9. 0D96:03F0  20 54 33 00 00 01 00 43-3A 5C 57 49 4E 44 4F 57    T3....C:\WINDOW
  10. -q
复制代码
11.txt
  1. -d0E0D:0380
  2. 0E0D:0380  00 5F 31 3D 36 31 33 35-00 5F 31 30 3D 32 38 36   ._1=6135._10=286
  3. 0E0D:0390  31 32 00 5F 31 30 30 3D-31 34 36 31 34 00 5F 31   12._100=14614._1
  4. 0E0D:03A0  31 3D 35 33 30 37 00 5F-31 32 3D 33 30 32 38 37   1=5307._12=30287
  5. 0E0D:03B0  00 5F 31 33 3D 31 30 35-31 00 5F 31 34 3D 35 39   ._13=1051._14=59
  6. 0E0D:03C0  36 37 00 5F 31 35 3D 32-37 34 37 38 00 5F 31 36   67._15=27478._16
  7. 0E0D:03D0  3D 31 35 33 37 33 00 5F-31 37 3D 31 37 30 31 33   =15373._17=17013
  8. 0E0D:03E0  00 5F 31 38 3D 32 36 38-31 37 00 5F 31 39 3D 38   ._18=26817._19=8
  9. 0E0D:03F0  31 33 31 00 5F 32 3D 32-39 36 38 30 00 5F 32 30   131._2=29680._20
  10. -q
复制代码
由此可见cmd在变量有变化的情况下会重新分配内存的储存地址,1.txt和11.txt中开始的地址分别为0D96:0380和0E0D:0380,而这一存储过程是按变量名+=号+值的全字符(实际上就是变量名,因为cmd是不会允许有同名变量的存在的)的ansi序列先后进行的。
***共同提高***

TOP

本帖最后由 batman 于 2011-4-23 11:21 编辑

再次测试(测试前猜想变量减少时,变量的内存存储开始地址不会改变)
代码:
  1. @echo off&setlocal enabledelayedexpansion
  2. del /q db-*.txt
  3. call :lp 1 100 random
  4. call :lp 10 20
  5. del /q db
  6. goto :eof
  7. :lp
  8. for /l %%a in (%1,1,%2) do set "_%%a=!%3!"
  9. (echo d2cl2
  10. echo q
  11. echo,
  12. )>db
  13. (for /f "skip=1 tokens=2,3" %%a in ('debug^<db') do (
  14.      echo d%%b%%a:0380&echo q&echo,
  15. ))>tem
  16. del /q db&ren tem db
  17. debug<db>db-%1.txt
  18. start db-%1.txt
复制代码
db-1.txt
  1. -d0DCC:0380
  2. 0DCC:0380  00 5F 31 3D 31 34 37 36-32 00 5F 31 30 3D 35 35   ._1=14762._10=55
  3. 0DCC:0390  37 37 00 5F 31 30 30 3D-32 31 31 35 33 00 5F 31   77._100=21153._1
  4. 0DCC:03A0  31 3D 37 37 35 35 00 5F-31 32 3D 31 35 35 38 36   1=7755._12=15586
  5. 0DCC:03B0  00 5F 31 33 3D 31 30 38-31 00 5F 31 34 3D 38 38   ._13=1081._14=88
  6. 0DCC:03C0  34 39 00 5F 31 35 3D 34-34 35 31 00 5F 31 36 3D   49._15=4451._16=
  7. 0DCC:03D0  31 32 39 34 35 00 5F 31-37 3D 32 35 37 33 35 00   12945._17=25735.
  8. 0DCC:03E0  5F 31 38 3D 31 33 30 31-31 00 5F 31 39 3D 32 37   _18=13011._19=27
  9. 0DCC:03F0  39 39 35 00 5F 32 3D 31-37 31 31 33 00 5F 32 30   995._2=17113._20
  10. -q
复制代码
db-10.txt
  1. -d0DCC:0380
  2. 0DCC:0380  00 5F 31 3D 31 34 37 36-32 00 5F 31 30 30 3D 32   ._1=14762._100=2
  3. 0DCC:0390  31 31 35 33 00 5F 32 3D-31 37 31 31 33 00 5F 32   1153._2=17113._2
  4. 0DCC:03A0  31 3D 32 37 37 36 30 00-5F 32 32 3D 33 37 36 35   1=27760._22=3765
  5. 0DCC:03B0  00 5F 32 33 3D 31 33 30-37 35 00 5F 32 34 3D 37   ._23=13075._24=7
  6. 0DCC:03C0  35 38 38 00 5F 32 35 3D-31 31 31 30 36 00 5F 32   588._25=11106._2
  7. 0DCC:03D0  36 3D 32 33 36 38 35 00-5F 32 37 3D 31 32 37 38   6=23685._27=1278
  8. 0DCC:03E0  39 00 5F 32 38 3D 32 30-33 30 31 00 5F 32 39 3D   9._28=20301._29=
  9. 0DCC:03F0  32 31 39 35 31 00 5F 33-3D 31 35 32 32 00 5F 33   21951._3=1522._3
  10. -q
复制代码
可见当变量减少时(部分变量被清空),其实就是对已有变量进行读取操作时,内存确实不会重新洗牌。
***共同提高***

TOP

本帖最后由 batman 于 2011-4-23 13:44 编辑

还是测试说明问题,但也带来了新问题。。。。
代码:
  1. @echo off&setlocal enabledelayedexpansion
  2. for /l %%a in (1,1,100) do set "d=!d!&echo d"
  3. call :lp _str a
  4. call :lp _str b setlocal endlocal
  5. call :lp _str s
  6. del /q db&pause
  7. goto :eof
  8. :lp
  9. %3
  10. if "%2" neq "s" set "%1=%2"
  11. (echo d2cl2
  12. echo q
  13. echo,
  14. )>db
  15. (for /f "skip=1 tokens=2,3" %%a in ('debug^<db') do (
  16.      echo d%%b%%a:0000%d%&echo q&echo,
  17. ))>tem
  18. del /q db&ren tem db
  19. for /f "delims=" %%a in ('debug^<db^|findstr /i "%1="') do echo %%a
  20. echo,
  21. %4
复制代码
结果:
  1. 0DBD:0640  5F 53 54 52 3D 61 00 42-4C 41 53 54 45 52 3D 41   _STR=a.BLASTER=A
  2. 0DBD:0640  5F 53 54 52 3D 62 00 42-4C 41 53 54 45 52 3D 41   _STR=b.BLASTER=A
  3. 0DBD:0640  5F 53 54 52 3D 61 00 42-4C 41 53 54 45 52 3D 41   _STR=a.BLASTER=A
  4. Press any key to continue . . .
复制代码
上面的测试表明,setlocal前以及setlcoal-endlocal中以及endlocal后_str变量在内存中储存的地址没有改变(至少是开始值没有改变)。同时说明了cmd中为什么变量名不分大小写,因为不管变量中的字符是不是大小写,内存中统一是以大写的字符储存的变量。但这里又有一个问题了,既然内存中只会储存一个名为_str的变量,那么endlocal后这个变量的值又是怎么恢复为前面的值的,难道还有一个临时储存变量的地方?
***共同提高***

TOP

测试系统变量:
  1. @echo off&setlocal enabledelayedexpansion
  2. for /l %%a in (1,1,100) do set "d=!d!&echo d"
  3. call :lp os
  4. call :lp os a setlocal endlocal
  5. call :lp os
  6. del /q db&pause
  7. goto :eof
  8. :lp
  9. %3
  10. if "%2" neq "" set "%1=%2"
  11. (echo d2cl2
  12. echo q
  13. echo,
  14. )>db
  15. (for /f "skip=1 tokens=2,3" %%a in ('debug^<db') do (
  16.      echo d%%b%%a:0000%d%&echo q&echo,
  17. ))>tem
  18. del /q db&ren tem db
  19. for /f "delims=" %%a in ('debug^<db^|findstr /i "%1="') do echo %%a
  20. echo,
  21. %4
复制代码
  1. 0DBD:03F0  52 4F 43 45 53 53 4F 52-53 3D 32 00 4F 53 3D 57   ROCESSORS=2.OS=W
  2. 0DBD:03F0  52 4F 43 45 53 53 4F 52-53 3D 32 00 4F 53 3D 61   ROCESSORS=2.OS=a
  3. 0DBD:03F0  52 4F 43 45 53 53 4F 52-53 3D 32 00 4F 53 3D 57   ROCESSORS=2.OS=W
  4. Press any key to continue . . .
复制代码
系统变量os在setlcoal-enlocal中去哪里了?,假如将os改成path,运行结果如下:
  1. 0DBD:0400  69 6E 64 6F 77 73 5F 4E-54 00 50 41 54 48 3D 43   indows_NT.PATH=C
  2. 'debug' is not recognized as an internal or external command,
  3. operable program or batch file.
  4. 'debug' is not recognized as an internal or external command,
  5. operable program or batch file.
  6. 0DBD:0400  69 6E 64 6F 77 73 5F 4E-54 00 50 41 54 48 3D 43   indows_NT.PATH=C
  7. Press any key to continue . . .
复制代码
在重新设定path后,debug命令变得无效。
***共同提高***

TOP

返回列表