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

[原创] 探讨批处理代码效率

为了处理一些windows不方便做的事情时,我们不得不选择cmd批处理来解决问题,
以逸待劳,来为我们服务。稍有一点了解批处理的都知道,cmd批处理是功能“强大”,但代码运行速度太慢。
    于是问题出来了,本来我们选择用cmd批处理的目的是为了更方便和更快速的,可是
cmd批处理的速度和我们期望目标成了矛与盾的较量。
    同一个问题批处理的解决办法,往往有多种,不同算法之间的速度相差非常之大,
为了提高cmd批处理程序,算法很关键,于是,都费尽脑汁,想方设法找一些巧妙的
方法。在注重算法的同时,往往忽略了或误解了最基本的问题:“批处理命令的效率”
    其实了解批处理命令的效率,有一个良好的BAT编写习惯,可以在为了好的算法
伤脑筋时,少走弯路。我根据在cn-dos所学到的简单总结了一下,欢迎各位指出错误。

声明:
以下仅为个人观点,有兴趣的就看看,并不代表权威数据,
特此声明,以免对大家有误导!

**********
因为不同的电脑速度相差太大,为了能有个统一标准,以@echo off 执行间为0.20毫秒为标准
**********
1. 预处理与运行速度
批处理的预处理已经有多人讨论过了,可以在论坛搜索“预处理”查看。这里就不详述,
只讨论预处理同效率的问题。
其实每行代码真正执行的时间不长(0.01~0.02毫秒),可是单单运行一行代码时,
最少也要0.19毫秒(如:注释行rem),为什么呢?我认为:这应该是批处理的运行的
“预处理”造成的,批处理最好的地方在这里却成了拌脚石,有没有办法解决这个问题呢?
有,把多行命令合成组执行,批处理对组()内的多行代码一次性预完成的。
()分组包括If () for () 和直接(),和用&并行,都具有同样的效果,能让系统一次性对多
条指令进行同时预处理,一组命令的用时为:一个预处理时长(0.19毫秒)+每条命令的执行时
长(0.01~0.02毫秒),也就是越多条命令合成一组,效率越高。for 的效率高的主要原因应该就在这里。
2. 管道操作与运行速度
管道操作对象有:程序(外部命令,三方工具等),内部命令(其实是命令解释器),
设备(空设备nul,标准输入输出设备con,打印设备prn,存储设备即常用的文件)
标准设备输入输出间的转换并不会占用很多时间,只有管道操作的对象为程序和内部命令时,
才会突然加大时间开销,如:
echo a                        0.41毫秒
echo a>>b.txt,       0.30毫秒
echo. a>>b.txt      1,30毫秒  (不知为什么这一点会多用那么多时间)
findstr “aa” a.txt    44.90毫秒
type a.txt |findstr “aa”   99.12毫秒
for /f %%a in (‘set #’) do (rem),  42.70毫秒
echo a|echo b     90.04毫秒
echo aa|findstr .*    88.84毫秒
echo a|sort      89.12毫秒
sort a.txt      45.45毫秒
abcd.com      12.50毫秒 (一个最直接退出的程序,非管道)
可见,程序和命令进行管道操作是需要很大资源的,为什么内部命令进行管道操作也要那么多的时间呢?
如:echo a|echo b
这可能是当前内部命令是不能接收管道数据的,而是调用了另一个命令解释器,再执行其中一个命令,
所以像调用了一个外部程序一样。for /f %%a in (‘command’) do 也应视为一次管道操作,
而使用“|”是进行了双管道操作所以用时多了一半。
3. 子程序调用时,选择文件还是函数
在cmd下,可以使用call :标号 来实现子过程调用,这种方法可以把多个子功能集中到一个BAT文件内,
进行内部调用。这种方法可以减少BAT文件的个数,可是要是考虑到效率问题时,
与调用外部BAT文件相比会是怎么样呢?测试结果:
Call :sub   3.51 毫秒
Call abc.bat  1.28 毫秒
出人意料!怎么外部调用会比内部调用更高效呢???
4. Call 各种方法中的效率问题
Call 的用法有多种,效率如下:
Call echo %%!n!b%%    1.64毫秒
for %%a in (!n!) do echo !%%ab!  0.63毫秒   看来用用for 来取代上句是可以加速度的
Call set aa=%%bb%%    1.47毫秒
Call :sub      3.51  毫秒  (空操作)
Call abc.bat     1.28 毫秒  (空操作)
附件: 您需要登录才可以下载或查看附件。没有帐号?注册
3

评分人数

    • 基拉freedom: 很不错 这种技术帖子 支持技术 + 1
    • tireless: (预处理一次) 有意思PB + 9
    • 随风: 有价值是分析!PB + 20 技术 + 1

(接一楼)

5. Goto 标号为什么慢呢? 看:
goto :next  0.27毫秒    (并不慢)
:next
    其实,单个goto 的速度并不是很慢,慢的原因是goto :标号和标号间不支持组合,
也就是每行组指令最少也要0.20毫秒以上。所以通常不应该用goto :标号 来构造循环体,
如果一定要这样时,应该把循环体内的多行,用()或用 “&”进行组合来减少行数。
通常只用在分支,或跳出for 循环。如果不得不用到时,最好用for来辅助以减少循环次数。

6. 注释与也与速度有关吗?
   注释方法有两种.
::号
当发现这以用这种方法后,绝大多数人都喜欢上这种方法,因为觉得它基本上不耗时,
其实并非这样,在运行过程中,系统遇到时相当于处理标号一样的时间:0.05毫秒,
其实只要是一行,就算是空行也要0.05毫秒的时间,也就是基本不用时间。
缺点是不能用在命令行后。也不能用在分组()内
Rem
按道理没有进行作何操作,应该不耗时才对,可是并非如此,单独一行rem要用0.19毫秒,
可见它没有 ::注释高效,是否rem该退休了呢?不,实际并非如此,因为它可以用在行后,
和分组()内,这时它的速度到了0.01~0.02毫秒,这是好多人没有想到的。

7. 外部命令和三方软件的效率问题,文件大少对用时在cmd下相差不是很大,主要是路径的问题,
以直接指明路径最快,在当前路为次,最慢是在%path%路径的最后。三方软件的调用速度是很慢的,
最快操作单个也要12.25毫秒,所以要用到三方的话,尽量在1到3次内完成,不要多次调用三方和外部命令,
特别是不能用在循环体内,除非不得已。这也就是为什么我不想用三方来做界面的原因。
8. 行长与速度,每长30个字符要多用0.01毫秒,因为批处理是一边运行一边解释的,
所以字符串的长短与时间也是有关的。最好是变量名的定义只能要方便看懂,越短越好。


附:(本人对一些命令实测效率,运行1000次所用毫秒数,测试工具请在一楼附件下载)

命令         运行次数      用时(毫秒)
cd.
===   1000  161
echo.
===   1000  158
echo.>nul
===   1000  161
echo.aa>nul
===   1000  182
echo dd>nul
===   1000  30
::dir
===   1000  5
rem dir
===   1000  19
set aa=aa
===   1000  21
set /a aa=1
===   1000  25
set aa=^(3+3^)*1
===   1000  24
set /a aa=8^^3
===   1000  25
for /l %%a in (1,1,10) do rem dir
===   1000  30
for /l %%a in (1,1,100) do set aa=aa
===   1000  193
for /l %%a in (1,1,10) do set aa=aa
===   1000  46
if 22==aaa rem dd
===   1000  21
if 22==aaa set aa=aa
〈〈数据未完,详请看附件〉〉

[ 本帖最后由 netbenton 于 2009-5-11 12:32 编辑 ]
1

评分人数

TOP

re 2,3楼:
命令耗时单,在附件里的排是好的,发上来就变了,有兴趣的人可以下载附件来看。


看了一下,plp626 所测试的数据,是以for /l 来实现执行次数,本人认为有误差,
因为for /l 本身也要耗时,另外在for /l 内是进行代码组合了的,并不能了解到因预处理所花的时间

就拿shift 这个命令来说吧,其单条命令应为0.19毫秒,相当于一行rem行的时间,而在组合里面,它用时则仅为0.01毫秒。



而我的方法是,把一千行命令写到一个临时BAT,并在这一千行代码的头尾,加上取数时间命令set aa=%time%
然后调用这个临时BAT来取得运行一千次命令前后的时间。所以误差较小。

[ 本帖最后由 netbenton 于 2009-5-11 00:16 编辑 ]

TOP

原帖由 随风 于 2009-5-11 00:22 发表
又一发现分别对 ehco. 和 echo/ 、echo\ 、echo= 、作了测试,发现除了点以外,其余的耗时都和echo 加 空格一样。看来以后要改掉用 echo点的习惯才行了。
:loop2
echo=a>nul
echo=a>nul
echo=a>nul
echo=a>nul ...

原来只有点号才会这样呀,我还以为是显示一行多用的时间呢,
这点我倒没有发现!


测试了一下,用echo=dd和echo,dd及echo;dd这三个用时最少为:   0.28毫秒

[ 本帖最后由 netbenton 于 2009-5-11 00:39 编辑 ]
1

评分人数

    • tireless: =,;确实好。http://www.bathome.cn/thread- ...PB + 1

TOP

re 楼上

确实写错,已经在一楼更正.
谢谢指正。

TOP

在批处理程序设计中,取字符长度最高效的方法

在批处理程序设计中,取字符长度最高效的方法
  1. @echo off&setlocal enabledelayedexpansion
  2. set aav=abcdefghijklmnopqrstuvwxyz不够的话文字也可以abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
  3. set 最大长度=80
  4. for /l %%a in (0,1,%最大长度%) do (
  5. set len!aav:~%%a,1!=%%a
  6. set vva=!aav:~%%a,1!!vva!
  7. )
  8. ::前面初始定义一次,后面可以任意使用,只要两个set 就可以取得字符串长度。
  9. ::但前提是,你要知道需计算的字符串中,最长字符串的长度。
  10. ::效率可想而知了!
  11. set str1=12345678
  12. set str2=abcdefghijklmn
  13. set var1=!str1!!vva!
  14. set var2=!str2!!vva!
  15. set/a var1=len!var1:~%最大长度%,1!,var2=len!var2:~%最大长度%,1!
  16. echo !str1! 长度为:!var1!
  17. echo !str2! 长度为:!var2!
  18. pause
复制代码
2

评分人数

TOP

re  13L
是的 a v v 变量字符不能有重复。但是全半角字符都可以,只要不重复任意使用。
此法在计算 子串 在 字符串 中的位置很实用。

re 19L
用到临时文件的话这样最简单(按字节计算的):
set str=任意字符串。
set /p=!str!<nul>len.temp
for %%a in (len.temp) do (echo !str! 长度为:%%~za)
del/q len.temp

TOP

返回列表