[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖
本帖最后由 caruko 于 2011-4-23 00:00 编辑

变量数目越多,操作越慢。 那么基本的设想就是,每次读取一个变量,都对整个“变量组”做了某种操作。

如果假设:这些变量是一个数组,那么以指针的方式读取Array(10000)中的Array[0],与读取Array(1)中的Array[0]并不会有很大的差别,除非在每次取值的时候,都将整个数组复制了一次(或者类似操作才会使得耗时成倍增加)。

还有一种猜想,就是类似数据库的存储方式。这样才会出现在变量大量增加时,耗时成倍增加。同时兼顾排序,输出所有匹配前几个字符的变量(如set _,set _@#等),等功能。


所以,“变量-耗时”的问题估计主要有两个方面。
1:  每次读取一个变量,都操作了整个“变量组”,体现在“总变量多少--耗时”的关系。
2:  读取变量时,会有一个查找操作,查找耗时跟排序位置有关,体现在“变量排序位置--耗时”的关系。

TOP

本帖最后由 terse 于 2011-4-23 00:25 编辑

猜测下   定义变量后 CND是否开始 在变量表查找变量 然后分析是否已经定义  再对变量表重新排列 整个过程的快慢 是否就是取决于变量表复杂度,
貌似以前翻到过类似贴 解释变量的存储

补充:如果变量如不是已定义, 则寻找一个空的位置来存放 所以相对旧的变量反而查找快 反之cmd 寻找先前变量位置 这个寻找过程的快慢和执行效率相关

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

本帖最后由 plp626 于 2011-4-23 09:39 编辑
用1和10000来做对比测试不太严谨吧,因为字符长度会影响预处理耗时,不如改成类似10000和16383的数(十进制和二进制位数均相同)
plp626兄台的测试代码我有点看不懂啊,endlocal之后不是不存在刚刚制造饿垃圾变量了 ...
zm900612 发表于 2011-4-22 22:43


说明,此楼以上到29楼的所有结论皆无效!
=====================================================
我感到自己有罪了,你们俩现在还没看出我那两个代码测试的结论是错误的,哎,苦了看帖的人了;

刚给你回复了帖子,我才想到有已经endlocal了!那就是说测试一代码没有垃圾变量了;

所以测试代码一不能叫垃圾变量在变量空间一内,赋值在二,而是,垃圾变量在空间一内生成,刚生成完又被cmd释放清空了。

所以测试一和二代码等效的来说就是都在同一个变量空间赋值;

测试一是在一个释放了垃圾变量的空间相等于清空的空间赋值,测试二则是在已经充满垃圾变量的空间进行变量赋值;

29楼那个结论“用setlocal可以减少其他变量赋值的耗费时间”真的惹笑话了!!

正确结论是 垃圾变量越多赋值越耗时,你没办法了~~~~除非你endlocal把他释放掉。

搞了这么久,就搞出来这个常识性的结论,是在汗!

TOP

39# plp626


确实没怎么用过...
不过endlocal的基本作用我是知道的,它会清除最近一个setlocal后的所有变量改动,但是我对“栈内数据存储操作和不受栈外垃圾变量影响”这句不太理解,为什么说是栈外变量呢?“栈外”存在变量吗?那些变量不是应该被清空了吗?

TOP

用1和10000来做对比测试不太严谨吧,因为字符长度会影响预处理耗时,不如改成类似10000和16383的数(十进制和二进制位数均相同)
plp626兄台的测试代码我有点看不懂啊,endlocal之后不是不存在刚刚制造饿垃圾变量了 ...
zm900612 发表于 2011-4-22 22:43



记得你说过自己不怎么用call,那可想而知你更是少用endlocal;

endlocal执行后,就把垃圾变量“释放”了。
把这段代码执行下,看看运行结果:
@echo off
setlocal&set plp=1&endlocal&set plp
pause

TOP

36# zm900612

我测试过 set /a _1000+=2 跟 set /a _9999 +=2
排除了字符长度问题,结果仍然。

因为很多人没看懂,所以代码就简单写了2个, _9 耗时比 _10000 长,最能说明问题。

耗时的多少,跟排序后的先后顺序有关。

TOP

题外话,好像本贴标题变了四五次了...

TOP

用1和10000来做对比测试不太严谨吧,因为字符长度会影响预处理耗时,不如改成类似10000和16383的数(十进制和二进制位数均相同)
plp626兄台的测试代码我有点看不懂啊,endlocal之后不是不存在刚刚制造饿垃圾变量了么?

TOP

是啊,没考虑过setlocal对此有没有影响。

ZM谈起大量变量影响效率,自己当初也做过生命游戏,做出过耗时3分钟跟15秒的不同版本,但当时没有深想。

现在回想起来,感觉变量大量存在导致的效率问题,超出了其它程序语言的N倍,而且SET 自带排序输出(耗时时很少) 。就猜想set中的变量,很可能是有序存储的。

然后就做了测试,发现了新的问题。
另外就是 _1 跟 _9 之间的效率差距问题,到底是否可能存在有序的变量表?

TOP

29# plp626


你的代码,好像不是 全局 与 局部 的关系啊。
而是2个相对独立的局部。

这样的意义就相当于:
当我们需要大量变量参与计算(计算后这大量的变量需要废弃),而我们只需要计算后的少数几个结果, ...
caruko 发表于 2011-4-22 21:57


你说的对,是两个相对独立的局部。。

TOP

29# plp626


你的代码,好像不是 全局 与 局部 的关系啊。
而是2个相对独立的局部。

这样的意义就相当于:
当我们需要大量变量参与计算(计算后这大量的变量需要废弃),而我们只需要计算后的少数几个结果,那么可以setlocal,减少之后其它语句的运行耗时。

TOP

本帖最后由 plp626 于 2011-4-23 09:41 编辑

结论就是:                                            【结论就是,只看测试代码运行结果,或者直接跳到40楼】

endlocal(不是setlocal)释放掉大量垃圾变量后赋值操作才会变快

代码比较如下:
  1. @echo off
  2. :: 测试一,垃圾变量在变量空间一内,变量二空间内变量的赋值耗时测试
  3. setlocal enabledelayedexpansion
  4. call:tt
  5. call:etime t1 t2 one
  6. set one
  7. for /l %%a in (1,1,10000) do set _%%a=1
  8. endlocal&Set one=%one%&setlocal enabledelayedexpansion
  9. call:tt
  10. call:etime t1 t2 two
  11. set two
  12. set/a pp=two/one
  13. echo 倍数: !pp!
  14. pause
  15. :tt -------------------------- sub -----------------------------
  16. set t1=%time%
  17. for /l %%a in (1 1 10000)do set/a _9+=2
  18. set t2=%time%
  19. goto:eof
  20. :etime
  21. set/a %3=1!%2:~-5,2!!%2:~-2!-1!%1:~-5,2!!%1:~-2!,%3+=-6000*("%3>>31")
  22. goto:eof
复制代码
  1. @echo off
  2. :: 测试二,垃圾变量在变量空间二而内,变量空间二内变量的赋值耗时测试。
  3. setlocal enabledelayedexpansion
  4. call:tt
  5. call:etime t1 t2 one
  6. set one
  7. endlocal&Set one=%one%&setlocal enabledelayedexpansion
  8. for /l %%a in (1,1,10000) do set _%%a=1
  9. call:tt
  10. call:etime t1 t2 two
  11. set two
  12. set/a pp=two/one
  13. ECHO 倍数:!pp!
  14. pause
  15. :tt -------------------------- sub -----------------------------
  16. set t1=%time%
  17. for /l %%a in (1 1 10000)do set/a _9+=2
  18. set t2=%time%
  19. goto:eof
  20. :etime
  21. set/a %3=1!%2:~-5,2!!%2:~-2!-1!%1:~-5,2!!%1:~-2!,%3+=-6000*("%3>>31")
  22. goto:eof
复制代码

TOP

26# caruko


老了,把函数的et忘了改了成%3了;
  1. :etime
  2. set/a %3=1!%2:~-5,2!!%2:~-2!-1!%1:~-5,2!!%1:~-2!,%3+=-6000*("%3>>31")
复制代码
你的试验数据属实,不过我把你才试验改进下,不是相差4.5倍,是相差70倍,我得到一个很有意义的结论。
这个结论对我们在进行大量变量赋值时具有很现实的指导意义。

待会告诉大家。

caruko为大家做了一个很有意义的试验。暂一个。

TOP

本帖最后由 cjiabing 于 2011-4-22 20:53 编辑

虽然俺不是专家,不过看见你们瞎忙乎,很想和你们谈谈一些个人认识。
      计算机有 0和1 两种基本信号,编码后组成一些最基本的字符列阵,这些字符列阵在系统中被其它程序广泛使用。具体搜索下“计算机编码”、“字符集”、“进制”。字符集应该这就是你们谈到的“字符表”了。这些字符表、字符集也是按照计算机编码顺序组成的,如果记得当年的那些什么码输入法你可能会幡然醒悟。但假如没有也不要紧,系统一般自带了这些字符映射信息。
      通常,XP系统里的字符集位于:【开始菜单——附件——系统工具——字符映射表】,真实路径位于:【  %SystemRoot%\system32\charmap.exe  】,可在运行里输入【 charmap 】打开。这就是字符映射表。将鼠标随便移动到字符表上,稍加停留,或者点击,你就能看到任意字符在字符表中的ID。然后你再看看排在最前面的几个是什么字符,你就知道可能的读取速度了。一般我们认为,可能受到编码进制的影响,排在前面的编码顺序要比后面的快。当然,这些在平时是感觉不到的,只有像你们折磨CPU和内存的时候它才表现出来。——这个可能需要更多的证据。
      再谈谈你们测试方法存在的问题。当你们通过产生大量变量来处理问题的时候,我就想到了洪水对河道的霸占。当暴雨剧降,河水泛滥,河道无法容纳足够的河水,于是溢出。而当暴雨过去,河水的消退也需要一定的时间。从变量的增加到变量的清除,内存或者cpu的处理都需要时间和空间,而这个时间是不能用增加时和清除时的时间简单相加和平均的来算的。
      再者,批处理融入到系统内部,它处理的时候应该是内部处理,只是它有许多外部接口。做个试验,在一个简单循环程序中,使用“@echo off”会明显比不使用该开头的程序运行速度快,因为后者运行时进行了重定向。
寂寞是黑白的,但黑白不是寂寞,是永恒。BAT 需要的不是可能,而是智慧。

TOP

返回列表