infinite loop with break condition

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: infinite loop with break condition

#31 Post by aGerman » 01 Jan 2012 07:58

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

Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: infinite loop with break condition

#32 Post by Ed Dyreen » 15 Jan 2012 20:58

'
@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 !
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 ) :wink:

Aacini
Expert
Posts: 1913
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: infinite loop with break condition

#33 Post by Aacini » 23 May 2012 22:44

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.

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.

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: infinite loop with break condition

#34 Post by einstein1969 » 22 Oct 2022 04:05

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):

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.

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: infinite loop with break condition

#35 Post by aGerman » 22 Oct 2022 05:02

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 ...

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: infinite loop with break condition

#36 Post by einstein1969 » 22 Oct 2022 05:11

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.

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: infinite loop with break condition

#37 Post by aGerman » 22 Oct 2022 05:22

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.

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: infinite loop with break condition

#38 Post by einstein1969 » 22 Oct 2022 05:33

aGerman wrote:
22 Oct 2022 05:22
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.
just to better understand, where should the REM be placed?
give me an example, even theoretical is fine.
we are here to learn and find solutions.

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: infinite loop with break condition

#39 Post by aGerman » 22 Oct 2022 05:42

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.

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: infinite loop with break condition

#40 Post by aGerman » 22 Oct 2022 05:55

These 5 lines take ~5 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
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

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: infinite loop with break condition

#41 Post by aGerman » 22 Oct 2022 06:11

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.

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
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.

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: infinite loop with break condition

#42 Post by einstein1969 » 22 Oct 2022 06:42

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.

Image

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: infinite loop with break condition

#43 Post by einstein1969 » 22 Oct 2022 06:51

aGerman wrote:
22 Oct 2022 05:55
These 5 lines take ~5 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



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
this take 3 second on my pc

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: infinite loop with break condition

#44 Post by einstein1969 » 22 Oct 2022 06:58

aGerman wrote:
22 Oct 2022 05:55

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
the long code trasformed in while..do on my machine take 4 second

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

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: infinite loop with break condition

#45 Post by einstein1969 » 22 Oct 2022 07:03

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"

Post Reply