找回密码
 注册
搜索
[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
查看: 345560|回复: 95

[系统相关] 批处理变量表机制的猜测及测试

[复制链接]
发表于 2011-4-22 15:08:35 | 显示全部楼层 |阅读模式
前面ZM兄谈到变量越多,批执行效率越低的问题。
我做了如下代码测试:

一、
环境中只有1个变量时,速度非常快,一万次调用,耗时不到1秒。

二、
生成一万个变量_1到_10000作为测试环境,测试都执行一万次,现象如下:

1:调用"_1",用时5.5秒。
2:调用"_1000",用时5.5秒。
3:调用"_9999",用时25-27秒。
4:调用"_9",用时25-27秒。
5:调用"_1 To _10000"各一次,总共也是一万次,耗时16秒。
6:调用"_5000"以及"_5","_50",三者用时都在15秒左右。
7:调用字符变量"_x",用时25-27秒。

都是从一万个变量中读取其中一个,"_1"与"_9999"的速度差距巨大!!

直观的可以看20楼的图。

测试结论(猜测)::
批处理遍历了一个变量(名)表,排在前列的读取速度快,排在后面的读取速度慢,而且从表开始到结束,读取耗时是匀速增加的。表的内容是经过排序的,与"set _"的输出一致。

测试代码:

  1. @echo off&setlocal enabledelayedexpansion
  2. for /l %%a in (1,1,10000) do set _%%a=1
  3. echo %time%
  4. for /l %%a in (1 1 10000)do set/a _9+=2
  5. echo %time%
  6. for /l %%a in (1 1 10000)do set/a _10000+=2
  7. echo %time%
  8. pause

复制代码
执行结果

  1. 20:58:43.03
  2. 20:59:04.87
  3. 20:59:09.26
  4. 请按任意键继续. . .
复制代码

评分

参与人数 2技术 +2 收起 理由
plp626 + 1 有内涵
Hello123World + 1 很不错的研究

查看全部评分

发表于 2011-4-22 15:55:23 | 显示全部楼层
  caruko很有研究和较真的精神,先在这里赞一个!的确,我们在平常一直要求新手写代码的执行效率要高。

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

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

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

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

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

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

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

理速度,所以个人认为只要电脑的内存足够大,大量的变量的存在是不会对程序的执行效率带来多大的影响的。
发表于 2011-4-22 16:13:53 | 显示全部楼层
本帖最后由 zm900612 于 2011-4-22 16:16 编辑

cmd的cpu占用(低于百分五十)和内存占用(低于XXMB,具体值忘了)都是有上限的,无论用几G的内存都不可能超过那个值,题外话,我以前有过利用双cmd进程并行实现最大程度利用cpu的想法,但是当时背景不对,效果不显著。
 楼主| 发表于 2011-4-22 16:24:20 | 显示全部楼层
本帖最后由 caruko 于 2011-4-22 16:28 编辑

并不只是内存的问题。而是CMD的变量存储读取机制问题。

否者,为什么调用第一个变量的速度比调用最后一个变量的速度快几倍?

而且,一定程度上解释了,set 存放变量的时候是排序好的。
并且 set _ 这类代码的作用,说明set 存放变量很可能是 数据库 的方式。
发表于 2011-4-22 16:51:16 | 显示全部楼层
我上个月做过一些关于效率的测试,原因不解释...部分结果如下:
1、正如楼主的结果证明的那样,存在大量变量时,读取变量的效率会极大地降低,不含变量时,命令效率不变。
2、单纯读取变量快于变量偏移,变量偏移快于变量替换,并且变量偏移的效率与偏移量关系不大

另外赠送两个:
3、外部命令启动速度都差不多,而不仅仅只有findstr慢,另外,文本写入速度与外部命令启动速度相当,读取速度的测试结果忘了
4、预处理!str!比%%a快
 楼主| 发表于 2011-4-22 18:00:13 | 显示全部楼层
其实我不是想只说明变量越多,速度越慢的问题,这个经常写bat的一般都有了解。

我希望大家谈谈对“变量的存储、读取” 方面猜测理解。

环境:
先设置1万个变量,从_1到_10000。

测试1:调用“_9999”这个变量1万次,再 调用“_1”这个变量1万次,耗时比 27:6 。

测试2:调用_1,_1000,_10000这3个变量执行1万次计算,3者耗时相差仿佛。调用 _99,_999,_9999,以及唯一的一个字符变量"_x",4者调用耗时也相差仿佛。

测试3:将_1--_10000这一万个变量都调用一次 ≈ 调用 _5 | _5000 这些变量一万次。


从上面测试结果来看:
1.  "变量增多,耗时越多"的原因,跟内存的关系不大!!因为1万个小变量不会导致内存不够,其它程序语言变量没有多到阀值,多少也不会有明显影响。

2.  变慢的原因可能跟批处理在预处理变量时,进行变量名查找匹配的速度有关。

3.  批处理内应该有一张表,按照排序好的顺序,存储着变量名(也可能含有以字符类型存储的变量值,或者内存指针)。

4.  批处理在预处理变量时,遍历变量表,取出匹配的变量名,再取得该变量的值。

5.  批处理在读取变量表时,很可能有一个复制(或者别的)操作(这可能是变量越多越慢的另一个原因)。 因为从一万个元素的Array(10000)数组中读取Array[0] 的速度,绝对不会比从Array(1)中读取Array[0]的速度慢太多。而这在批处理中的差距是几千倍。

大家有什么看法??
发表于 2011-4-22 18:16:30 | 显示全部楼层
1# caruko


对变量的结论你是怎么得出的? 猜测的话你的控制实验现象是什么?

你看一些介绍中缀表达式计算的C代码,扩展后可以操作变量,可以解释文本里的数学公式,并有简单的编程功能,都是堆栈的方式,每一条命令行语句,对变量的读取,就是读地址,预处理的时候变量的字符串已经成为常量,cmd把这个常量转换为散列地址,你赋值时后它已经放在哪,然后读变量的时候cmd又把这个变量解释为散列地址,就直接取值了,若没定义过它就返回空。只是谁也没见识过cmd的源代码,汇编也是更是门外汉。

cmd里的random,errorlevel,date,time变量又不同于我们定义的变量,对于延迟处理我们还是靠现象猜测,有时候我给cmd发一条好长的复合语句,它会报告存储空间不足,无法处理此命令,我们是在黑暗中摸索,较真的话,很耗费时间精力的。
发表于 2011-4-22 18:18:30 | 显示全部楼层
我们若为提高效率,记住这条实用的:

对数值的单一赋值,set 比set/a 快25%左右。
发表于 2011-4-22 18:26:55 | 显示全部楼层
有个给大伙的建议,我们在做cmd执行效率的时候,能不能把耗费时间的秒换算成是echo off(或其他某条语句,大伙可以指定可统一标准)语句耗费时间的多少倍,这样方便参考。

都说自己运行耗费了多少秒,那只是时间,没要参考价值的。当然你可以给出自己的cpu型号,内存,但即使这样各人的机子也不同。

效率是单位时间所执行的任务才对。
发表于 2011-4-22 18:27:00 | 显示全部楼层
我们若为提高效率,记住这条实用的:

对数值的单一赋值,set 比set/a 快25%左右。
plp626 发表于 2011-4-22 18:18

这个没留意过,学习了
 楼主| 发表于 2011-4-22 18:42:10 | 显示全部楼层
C的变量,内存的读取,我有一定了解。但是C是编译程序,静态变量一般直接翻译成地址。

如果调用一万个变量 比 调用一万个变量中的 一个变量 速度慢,或许可以解释成CPU缓存与寻址之类的说法。


但是,从 读取 _1 跟 _9999 的速度不一样,以及_9,_5000等调用测试,可以看出:
批处理遍历了一个表,排在前列的读取速度快,排在后面的读取速度慢,而且从表开始到结束,读取耗时是匀速增加的。表的内容是经过排序的,与"set _"的输出一致。

这个结论很容易验证,我已给出代码,大家都可以自己试试,调用不同位置的变量,看看速度有那些差距。


验证过上面的代码后:
如果真的有“变量表”存在,那么P跟C的变量存储方式肯定不同。
set x=1 速度快于 set /a x=1 ,则很可能说明:变量是以字符类型存储的。如果是指针,那么速度应该差不多。
发表于 2011-4-22 18:48:21 | 显示全部楼层
11# caruko


你怎么排除预处理的因素?
 楼主| 发表于 2011-4-22 18:53:59 | 显示全部楼层
本帖最后由 caruko 于 2011-4-22 19:00 编辑

12# plp626

for /l %%i in (1,1,10000)  set /a _10000+=2

for /l %%i in (1,1,10000)  set /a _9999+=2

预处理会有很大差距吗?
_9 , _99 , _9999  耗时几乎相同
_1,_100,_10000 耗时也几乎相同

但是 _9 >> _10000,可否说明,跟预处理无关?

1万次 _9 耗时 23.1 秒
1万次 _10000 耗时 5.3 秒

每次的执行时间虽然不固定,但是巨大的差距足以说明问题。
发表于 2011-4-22 19:02:51 | 显示全部楼层
大家是不是对代码执行效率有了误解?如对一个变量操作的时间为一个单位,同时对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
发表于 2011-4-22 19:13:55 | 显示全部楼层
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
哪来的巨大的相差?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|批处理之家 ( 渝ICP备10000708号 )

GMT+8, 2026-3-17 02:34 , Processed in 0.024527 second(s), 9 queries , File On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表