infinite loop with break condition
Moderator: DosItHelp
Re: infinite loop with break condition
Hi Rileyh.
Read the entire thread to understand how it works.
GOTO is slow and "evil". A lot of programming languages don't support GOTO. The only reason why you need GOTO in batch files is to create loops. If you use it to jump forward to a label in bigger files then you finally create unmaintainable spaghetti code!
The macro stuff is only a goody and uses the EXIT <ReturnValue> method we figured out before.
Regards
aGerman
Read the entire thread to understand how it works.
GOTO is slow and "evil". A lot of programming languages don't support GOTO. The only reason why you need GOTO in batch files is to create loops. If you use it to jump forward to a label in bigger files then you finally create unmaintainable spaghetti code!
The macro stuff is only a goody and uses the EXIT <ReturnValue> method we figured out before.
Regards
aGerman
Re: infinite loop with break condition
'
@aGerman
I come in a little late on this topic, just wanted to say I've been using the while idea for a while.
I do want to point out it is going to be difficult (not impossible) to evaluate very complex conditions if while is in the form of a macro.
Therefor I use a while function, the condition that is tested comes as a macro reference executed by the while, optionally setting a timeout:
Having while as a function allows me to use any macro as a condition.
No need to explain any further, you know where to find the source-code ( I assume )
@aGerman
I come in a little late on this topic, just wanted to say I've been using the while idea for a while.
I do want to point out it is going to be difficult (not impossible) to evaluate very complex conditions if while is in the form of a macro.
Therefor I use a while function, the condition that is tested comes as a macro reference executed by the while, optionally setting a timeout:
Code: Select all
( %While% @condition, $timeout ) &&echo.succes ||echo.timeout !
No need to explain any further, you know where to find the source-code ( I assume )
Re: infinite loop with break condition
Although this topic was solved already, the definition of the While macro is not very practical. I devised another way to made a While easier to write and use.
Antonio
PS: A new way to write a WHILE macro is described here.
Code: Select all
@echo off
rem While dispatcher
if "%1" equ "While" goto While%2
setlocal EnableDelayedExpansion
rem Definition of auxiliary variables
set While=for /L %%a in () do if
set Do=(
set EndW=) else exit
set RunWhile=cmd /V:ON /Q /C "%0" While
echo Example of While
echo/
goto RunWhile1
rem Write the While code here
:While1
set /A i=0, num=0
set /P "num=Enter number: "
%While% !num! gtr 0 %Do%
set /A i+=1
echo !i!- Number processed: !num!
echo/
set num=0
set /P "num=Enter number: "
%EndW% !i!
rem Execute the While here
:RunWhile1
%RunWhile% 1
set i=%errorlevel%
echo/
echo While processed %i% elements
Antonio
PS: A new way to write a WHILE macro is described here.
-
- Expert
- Posts: 960
- Joined: 15 Jun 2012 13:16
- Location: Italy, Rome
Re: infinite loop with break condition
I found myself having to port some code that contained nested while's.
The speed was obviously very slow.
I tried the various solutions proposed to accelerate both that of breaking the infinite loop with an exit inside a separate cmd context and through the trick found by dave benham that uses a hybrid acceleration post #2.
I must say that the most convenient way is the hybrid one because it allows "complex while conditions" and there are no problems for passing parameters (example very complex structures to be passed such as arrays etc.)
To keep the acceleration in the nested while I had to put the nested while in subprocedures to be called via call. I checked the run time and it appears to be not increased.
This is the structure that comes out of it (I added some embellishments to make the code easier to read and develop) in case someone has to use it (Acc stands for accelerate):
The speed was obviously very slow.
I tried the various solutions proposed to accelerate both that of breaking the infinite loop with an exit inside a separate cmd context and through the trick found by dave benham that uses a hybrid acceleration post #2.
I must say that the most convenient way is the hybrid one because it allows "complex while conditions" and there are no problems for passing parameters (example very complex structures to be passed such as arrays etc.)
To keep the acceleration in the nested while I had to put the nested while in subprocedures to be called via call. I checked the run time and it appears to be not increased.
This is the structure that comes out of it (I added some embellishments to make the code easier to read and develop) in case someone has to use it (Acc stands for accelerate):
Code: Select all
@echo off
setlocal enableDelayedExpansion
rem beautification of the code
set "Acc=(for /L %%. in (1,1,250) do ("
set "AccGoto=) ) & goto"
set i=0
:whileLabel_outer_1 %= WHILE LOOP =%
%Acc%
if !i! leq 1000 (
rem WhileBody
set /a i+=1
call :inner_while_1
) else goto :endwhile_outer_1 %= WHILE LOOP EXIT =%
%AccGoto% :whileLabel_outer_1
:endwhile_outer_1
pause
goto :eof
:inner_while_1
set j=0
:whileLabel_inner_1 %= WHILE LOOP =%
%Acc%
if !j! leq 1000 (
rem WhileBody
set /a j+=1
title !i! !j!
) else goto :endwhile_inner_1 %= WHILE LOOP EXIT =%
%AccGoto% :whileLabel_inner_1
:endwhile_inner_1
EDIT: changed the minim loop from 500 to 250 for faster exit. On my sistem the gain for 500 is minimum.
goto :eof
Last edited by einstein1969 on 22 Oct 2022 05:04, edited 1 time in total.
Re: infinite loop with break condition
There are a lot of dependencies and it's not easy to choose the right approach. GOTO, for example, is known to first search downward in your code to find the label. If it didn't find the label, it begins again in the first line searching downward. Thus, the time it takes to find the label is quite related to the number of lines in your code. Adding a few thousand REM FOO lines to your example would make this immediately visible. They are never executed, but they are searched.
So, it depends ...
So, it depends ...
-
- Expert
- Posts: 960
- Joined: 15 Jun 2012 13:16
- Location: Italy, Rome
Re: infinite loop with break condition
sure, but in the previous approach the number of gotos is greatly reduced.
I calculated the minimum value (using my PC, I realize that with a different PC it may not be optimal) to have an increase in performance without affecting too much in case you have to exit the cycle. I figured 250 cycles might be a good compromise.
But your observation forces me to review. If you give me an example I try to better understand how much I can influence the goto distance.
I calculated the minimum value (using my PC, I realize that with a different PC it may not be optimal) to have an increase in performance without affecting too much in case you have to exit the cycle. I figured 250 cycles might be a good compromise.
But your observation forces me to review. If you give me an example I try to better understand how much I can influence the goto distance.
Re: infinite loop with break condition
Well, actually just add 10000 REM FOO lines as I said. You may finally come up with the observation that moving your loops into another script file might be good in terms of performance ¯\_(ツ)_/¯ I don't know. My assumption is that you won't find the one and only solution which performs best in every code. The "it depends" is probably also the result of your investigations.
-
- Expert
- Posts: 960
- Joined: 15 Jun 2012 13:16
- Location: Italy, Rome
Re: infinite loop with break condition
just to better understand, where should the REM be placed?aGerman wrote: ↑22 Oct 2022 05:22Well, actually just add 10000 REM FOO lines as I said. You may finally come up with the observation that moving your loops into another script file might be good in terms of performance ¯\_(ツ)_/¯ I don't know. My assumption is that you won't find the one and only solution which performs best in every code. The "it depends" is probably also the result of your investigations.
give me an example, even theoretical is fine.
we are here to learn and find solutions.
Re: infinite loop with break condition
Currently the last line in your code is GOTO :EOF, right? Now just continue with 10000 more lines. The content of those lines doesn't matter at all because they are never executed. It just increases the number of lines in your file. This is good enough to demonstrate the behavior of GOTO searching each line in your code.
Re: infinite loop with break condition
These 5 lines take ~5 seconds on my machine
The same code but 12 lines take ~11 seconds on my machine
Code: Select all
@echo off &setlocal EnableDelayedExpansion
set "n=0" &echo %time%
:loop
set /a "n+=1" &if !n! leq 10000 goto loop
echo %time% %n% &pause
Code: Select all
@echo off
setlocal EnableDelayedExpansion
set "n=0"
echo %time%
:loop
set /a "n+=1"
if !n! leq 10000 goto loop
echo %time% %n%
pause
Re: infinite loop with break condition
And for not making it too easy, the distance between the label and GOTO seems to matter as well.
Again 5 lines but the SET /A in a separate line makes it take ~8 seconds.
Another example of mine:
viewtopic.php?p=55148#p55148
In this post you find a readable code. The "unreadable" but significantly shorter code is ~25% faster:
viewtopic.php?p=55139#p55139
The reason is the necessity of GOTO statements to perform loops, and CALL statements to perform recursion in the code.
Again 5 lines but the SET /A in a separate line makes it take ~8 seconds.
Code: Select all
@echo off &setlocal EnableDelayedExpansion &set "n=0" &echo %time%
:loop
set /a "n+=1"
if !n! leq 10000 goto loop
echo %time% %n% &pause
viewtopic.php?p=55148#p55148
In this post you find a readable code. The "unreadable" but significantly shorter code is ~25% faster:
viewtopic.php?p=55139#p55139
The reason is the necessity of GOTO statements to perform loops, and CALL statements to perform recursion in the code.
-
- Expert
- Posts: 960
- Joined: 15 Jun 2012 13:16
- Location: Italy, Rome
Re: infinite loop with break condition
I haven't seen all the other codes but have you tried to transform "repeat .. until" into "while .. do"?
if you turn them into while do I think you can take advantage of the "for" acceleration
the first is "while do" and the second is "repeat until" and I think that" while .. do " can be optimized about 100 times in batch.
if you turn them into while do I think you can take advantage of the "for" acceleration
the first is "while do" and the second is "repeat until" and I think that" while .. do " can be optimized about 100 times in batch.
-
- Expert
- Posts: 960
- Joined: 15 Jun 2012 13:16
- Location: Italy, Rome
Re: infinite loop with break condition
aGerman wrote: ↑22 Oct 2022 05:55These 5 lines take ~5 seconds on my machineCode: Select all
@echo off &setlocal EnableDelayedExpansion set "n=0" &echo %time% :loop set /a "n+=1" &if !n! leq 10000 goto loop echo %time% %n% &pause
this is slower but optimizable:
equivalent while..do code
Code: Select all
@echo off &setlocal EnableDelayedExpansion
set "n=0" &echo %time% & set /a "n+=1"
:loop
if !n! leq 10000 ( set /a "n+=1" ) else goto :exit
goto :loop
:exit
echo %time% %n% &pause
-
- Expert
- Posts: 960
- Joined: 15 Jun 2012 13:16
- Location: Italy, Rome
Re: infinite loop with break condition
the long code trasformed in while..do on my machine take 4 secondaGerman wrote: ↑22 Oct 2022 05:55
The same code but 12 lines take ~11 seconds on my machineCode: Select all
@echo off setlocal EnableDelayedExpansion set "n=0" echo %time% :loop set /a "n+=1" if !n! leq 10000 goto loop echo %time% %n% pause
Code: Select all
@echo off
setlocal EnableDelayedExpansion
set "n=0"
echo %time%
set /a "n+=1"
:loop
if !n! leq 10000 (
set /a "n+=1"
) else goto :exit
goto :loop
:exit
echo %time% %n%
pause
-
- Expert
- Posts: 960
- Joined: 15 Jun 2012 13:16
- Location: Italy, Rome
Re: infinite loop with break condition
but while..do code can be accelerated and even if there will be differences between short and long code they will still be 100 times smaller.
In my test I added 10000 lines with "rem foo" and the code counted up to 1000 * 1000 = 1.000.000 in 66 seconds in the version without the 10000 rem foo and 132 seconds in the one with "rem foo"
In my test I added 10000 lines with "rem foo" and the code counted up to 1000 * 1000 = 1.000.000 in 66 seconds in the version without the 10000 rem foo and 132 seconds in the one with "rem foo"