Why is a GOTO loop much slower than a FOR loop and depends additionally on power supply?












3















I wrote a small batch file to convert a file containing eight 16 bit hexadecimal values per line into a CSV file with the eight values in decimal.



The input data file was the captured output of ADC values of an embedded device sent in packets of eight hexadecimal values in ASCII with carriage return plus line-feed via RS-232 to PC and simply captured on PC into a file. One line in input data file was something like:



000A002D0044008B0125018C01F40237


The CSV file for this line was:



10,45,68,139,293,396,500,567


The batch file worked, but it took several minutes to complete the conversion which shocked me. I expected that Windows command processor takes some seconds for this task which a console application written in C or C++ could to in some milliseconds. But an execution time of several minutes for a data file with less than 512 KiB was definitely not expected by me.



So I looked further on this issue with creating a batch file using four different methods to create a CSV file with decimal values from a data file with hexadecimal values.



The complete batch file for testing the four methods is appended below as well as my test results.



I understand that the first two methods using a subroutine are much slower than the last two methods doing the conversion in one loop with output each CSV line into file on each loop iteration respectively once at end of FOR loop because of calling a subroutine causes several additional steps done by cmd.exe which altogether take a lot of time on doing subroutine calling thousands of times.



But I do not really understand why first method using a GOTO loop is about six times slower than FOR loop with nearly the same two command lines.



Batch file code of method 1:



@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "DelDataFile="
set "DataFile=%TEMP%HexValues.dat"
if not exist "%DataFile%" set "DelDataFile=1" & echo 000A002D0044008B0125018C01F40237>"%DataFile%"

for /F "usebackq delims=" %%I in ("%DataFile%") do call :ConvertLine "%%I"

if defined DelDataFile del "%DataFile%"
endlocal
goto :EOF

:ConvertLine
set "DataLine=%~1"
set "AllValues="
set "StartColumn=0"
:NextValue
set /A Value=0x!DataLine:~%StartColumn%,4!
set "AllValues=%AllValues%,%Value%"
set /A StartColumn+=4
if not %StartColumn% == 32 goto NextValue
echo %AllValues:~1%
goto :EOF


Batch file code of method 2:



@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "DelDataFile="
set "DataFile=%TEMP%HexValues.dat"
if not exist "%DataFile%" set "DelDataFile=1" & echo 000A002D0044008B0125018C01F40237>"%DataFile%"

for /F "usebackq delims=" %%I in ("%DataFile%") do call :LineConvert "%%I"

if defined DelDataFile del "%DataFile%"
endlocal
goto :EOF

:LineConvert
set "DataLine=%~1"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!
goto :EOF


And I discovered additionally on running tests to find out the reason by myself that method 1 takes 5 to 10 seconds longer on PC running on battery than on power supply being plugged in.



The question:



What is the reason for much slower execution of the GOTO loop used by method 1 in comparison to the FOR loop used by method 2 and why does method 1 depend on kind of power supply of PC?





Here is the entire batch file being used to compare the different methods:



@echo off
setlocal EnableExtensions EnableDelayedExpansion
cls
set "TestRuns=5"
set "DelDataFile="
set "DataFile=%TEMP%HexValues.dat"
if exist "%DataFile%" goto InitMethod1

set "DelDataFile=1"
echo Creating data file which takes some seconds, please wait ...
setlocal
set "HexDigits=0123456789ABCDEF"
set "DataLine="
(for /L %%I in (0,1,32767) do (
set /A "Digit1=(%%I >> 12) %% 16"
set /A "Digit2=(%%I >> 8) %% 16"
set /A "Digit3=(%%I >> 4) %% 16"
set /A "Digit4=%%I %% 16"
set "HexValue="
for %%J in (!Digit1! !Digit2! !Digit3! !Digit4!) do set "HexValue=!HexValue!!HexDigits:~%%J,1!"
set "DataLine=!DataLine!!HexValue!"
set /A "ValuesPerLine=%%I %% 8"
if !ValuesPerLine! == 7 (
echo !DataLine!
set "DataLine="
)
))>"%DataFile%"
endlocal
echo/


:InitMethod1
call :MethodInit 1
:RunMethod1
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

for /F "usebackq delims=" %%I in ("%DataFile%") do call :ConvertLine "%%I"

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod1
call :MethodResults
goto InitMethod2

:ConvertLine
set "DataLine=%~1"
set "AllValues="
set "StartColumn=0"
:NextValue
set /A Value=0x!DataLine:~%StartColumn%,4!
set "AllValues=%AllValues%,%Value%"
set /A StartColumn+=4
if not %StartColumn% == 32 goto NextValue
>>"%CSV_File%" echo %AllValues:~1%
goto :EOF


:InitMethod2
call :MethodInit 2
:RunMethod2
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

for /F "usebackq delims=" %%I in ("%DataFile%") do call :LineConvert "%%I"

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod2
call :MethodResults
goto InitMethod3

:LineConvert
set "DataLine=%~1"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!>>"%CSV_File%"
goto :EOF


:InitMethod3
call :MethodInit 3
:RunMethod3
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

for /F "usebackq delims=" %%I in ("%DataFile%") do (
set "DataLine=%%I"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!>>"%CSV_File%"
)

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod3
call :MethodResults
goto InitMethod4


:InitMethod4
call :MethodInit 4
:RunMethod4
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

(for /F "usebackq delims=" %%I in ("%DataFile%") do (
set "DataLine=%%I"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!
))>>"%CSV_File%"

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod4
call :MethodResults
goto EndBatch


:GetTime
for /F "tokens=2 delims==." %%I in ('%SystemRoot%System32wbemwmic.exe OS GET LocalDateTime /VALUE') do set "%1=%%I"
goto :EOF

:MethodInit
set "Method=%1"
echo Test runs with method %Method%
echo -----------------------
echo/
set "TestRun=0"
set "TotalTime=0"
goto :EOF

:MethodResults
set /A AverageTime=TotalTime / TestRun
echo Method %Method% total time: %TotalTime% seconds
echo Method %Method% average time: %AverageTime% seconds
echo/
goto :EOF

:OutputTime
call :GetTime EndTime
set /A StartTime=(1%StartTime:~8,2% - 100) * 3600 + (1%StartTime:~10,2% - 100) * 60 + 1%StartTime:~12,2% - 100
set /A EndTime=(1%EndTime:~8,2% - 100) * 3600 + (1%EndTime:~10,2% - 100) * 60 + 1%EndTime:~12,2% - 100
set /A DiffTime=EndTime - StartTime
set /A TotalTime+=DiffTime
echo Method %Method% run %TestRun% time: %DiffTime% seconds
goto :EOF


:EndBatch
if defined DelDataFile del "%DataFile%"
del /Q "%TEMP%Values?_*.csv"
endlocal


It creates first the data file with incrementing hexadecimal values in folder for temporary files which take already some seconds. Please comment the antepenultimate command line of this batch file to keep that file in case of running this batch file multiple times or being interested in this file.



Then it runs five times the four methods for reading the hexadecimal values from data file and writing the values in decimal into a CSV file with printing the test results to console respectively handle STDOUT.



Finally it deletes all CSV files created also in folder for temporary files all having same content. Please comment last but one command line to keep those CSV files on being interested in those files.



This batch file was executed by me four times on two notebooks.



Here are the results of first run on a notebook with an Intel Core Duo P8400 with 2.26 GHz and 2 GiB RAM with a HDD with 7200 rpm running Windows XP x86 with power supply plugged in:



Test runs with method 1
-----------------------

Method 1 run 1 time: 51 seconds
Method 1 run 2 time: 51 seconds
Method 1 run 3 time: 51 seconds
Method 1 run 4 time: 52 seconds
Method 1 run 5 time: 51 seconds
Method 1 total time: 256 seconds
Method 1 average time: 51 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 9 seconds
Method 2 run 2 time: 9 seconds
Method 2 run 3 time: 9 seconds
Method 2 run 4 time: 8 seconds
Method 2 run 5 time: 9 seconds
Method 2 total time: 44 seconds
Method 2 average time: 9 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 3 seconds
Method 3 run 2 time: 3 seconds
Method 3 run 3 time: 4 seconds
Method 3 run 4 time: 3 seconds
Method 3 run 5 time: 3 seconds
Method 3 total time: 16 seconds
Method 3 average time: 3 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 3 seconds
Method 4 run 2 time: 2 seconds
Method 4 run 3 time: 2 seconds
Method 4 run 4 time: 2 seconds
Method 4 run 5 time: 2 seconds
Method 4 total time: 11 seconds
Method 4 average time: 2 seconds


Method 2 is 5.67 times faster than method 1. Method 3 and 4 are even faster than method 2, but that is expected by me. Most of the 2 and 3 seconds needed by methods 3 and 4 are from WMIC command to get local date and time in region independent format.



Here are the results of second run on same computer as first run with the difference on running the PC on full charged battery:



Test runs with method 1
-----------------------

Method 1 run 1 time: 63 seconds
Method 1 run 2 time: 61 seconds
Method 1 run 3 time: 61 seconds
Method 1 run 4 time: 61 seconds
Method 1 run 5 time: 61 seconds
Method 1 total time: 307 seconds
Method 1 average time: 61 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 11 seconds
Method 2 run 2 time: 10 seconds
Method 2 run 3 time: 10 seconds
Method 2 run 4 time: 10 seconds
Method 2 run 5 time: 10 seconds
Method 2 total time: 51 seconds
Method 2 average time: 10 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 3 seconds
Method 3 run 2 time: 4 seconds
Method 3 run 3 time: 3 seconds
Method 3 run 4 time: 4 seconds
Method 3 run 5 time: 3 seconds
Method 3 total time: 17 seconds
Method 3 average time: 3 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 2 seconds
Method 4 run 2 time: 2 seconds
Method 4 run 3 time: 2 seconds
Method 4 run 4 time: 2 seconds
Method 4 run 5 time: 2 seconds
Method 4 total time: 10 seconds
Method 4 average time: 2 seconds


It can be seen that for methods 2 to 4 the processing times increase just a little bit. But processing time of method 1 increases by 10 seconds and so this solution is now about 6.10 times slower than method 2. I have no idea why processing time of method 1 depends on kind of power supply.



Here are the results of first run on a notebook with an Intel Core Duo T9600 with 2.80 GHz and 4 GiB RAM with an SSD running Windows 7 x64 with power supply plugged in:



Test runs with method 1
-----------------------

Method 1 run 1 time: 91 seconds
Method 1 run 2 time: 88 seconds
Method 1 run 3 time: 77 seconds
Method 1 run 4 time: 77 seconds
Method 1 run 5 time: 78 seconds
Method 1 total time: 411 seconds
Method 1 average time: 82 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 11 seconds
Method 2 run 2 time: 16 seconds
Method 2 run 3 time: 16 seconds
Method 2 run 4 time: 14 seconds
Method 2 run 5 time: 16 seconds
Method 2 total time: 73 seconds
Method 2 average time: 14 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 6 seconds
Method 3 run 2 time: 4 seconds
Method 3 run 3 time: 4 seconds
Method 3 run 4 time: 4 seconds
Method 3 run 5 time: 6 seconds
Method 3 total time: 24 seconds
Method 3 average time: 4 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 4 seconds
Method 4 run 2 time: 3 seconds
Method 4 run 3 time: 5 seconds
Method 4 run 4 time: 4 seconds
Method 4 run 5 time: 4 seconds
Method 4 total time: 20 seconds
Method 4 average time: 4 seconds


It was interesting to see that the batch file execution with more powerful hardware takes more time on Windows 7 x64 than on Windows XP x86. But even more interesting for me is again the fact that method 2 is 5.86 times faster than method 1 just because of using a FOR instead of a GOTO loop.



For completeness the results of fourth run on same computer as third run with the difference on running the PC on full charged battery:



Test runs with method 1
-----------------------

Method 1 run 1 time: 97 seconds
Method 1 run 2 time: 91 seconds
Method 1 run 3 time: 90 seconds
Method 1 run 4 time: 81 seconds
Method 1 run 5 time: 77 seconds
Method 1 total time: 436 seconds
Method 1 average time: 87 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 12 seconds
Method 2 run 2 time: 16 seconds
Method 2 run 3 time: 17 seconds
Method 2 run 4 time: 16 seconds
Method 2 run 5 time: 13 seconds
Method 2 total time: 74 seconds
Method 2 average time: 14 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 6 seconds
Method 3 run 2 time: 6 seconds
Method 3 run 3 time: 5 seconds
Method 3 run 4 time: 5 seconds
Method 3 run 5 time: 5 seconds
Method 3 total time: 27 seconds
Method 3 average time: 5 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 4 seconds
Method 4 run 2 time: 4 seconds
Method 4 run 3 time: 5 seconds
Method 4 run 4 time: 4 seconds
Method 4 run 5 time: 4 seconds
Method 4 total time: 21 seconds
Method 4 average time: 4 seconds


There is again not much difference in execution time for the methods 3 to 4 in comparison to third run with power supply plugged in. But the execution time of method 1 increases by approximately 5 seconds and for that reason method 1 is 6.21 times slower than method 2.



I would be really interested in why method 1 is so much slower than method 2 and additionally depends on kind of power supply.



The hard disk activity LED was only very rarely blinking on all test runs as expected by me because of Windows file caching.










share|improve this question




















  • 2





    Because the FOR command and all the lines that comprise it are read from the file just once and then the complete parsed code is executed several times from memory. On the other hand, a GOTO just transfer the execution, so all the lines in the loop are executed in the usual Batch file way: open file, read line, close file, parse line, execute line...

    – Aacini
    Nov 18 '18 at 18:13








  • 1





    With a :top label at top of full script, set a counter variable and exit at 32767. Add a goto :bottom. At bottom, add :bottom and goto :top. It takes 25s just to execute that as it has to read each line of the whole script 32767 times searching down for the :bottom label.

    – michael_heath
    Nov 19 '18 at 8:52











  • @Aacini You are absolutely right on what you wrote above. That can be seen on file system accesses logged by Sysinternals Process Monitor during execution of the batch files. The solutions not using GOTO and CALL on processing the data in HexValues.dat don't make any file access on HexValues.dat and the batch file while processing all lines in HexValues.dat in comparison to the solutions with GOTO and CALL on which the batch file is read line by line extremely often. So a FOR loop with using delayed expansion is definitely better than using GOTO or CALL.

    – Mofi
    Nov 19 '18 at 10:33
















3















I wrote a small batch file to convert a file containing eight 16 bit hexadecimal values per line into a CSV file with the eight values in decimal.



The input data file was the captured output of ADC values of an embedded device sent in packets of eight hexadecimal values in ASCII with carriage return plus line-feed via RS-232 to PC and simply captured on PC into a file. One line in input data file was something like:



000A002D0044008B0125018C01F40237


The CSV file for this line was:



10,45,68,139,293,396,500,567


The batch file worked, but it took several minutes to complete the conversion which shocked me. I expected that Windows command processor takes some seconds for this task which a console application written in C or C++ could to in some milliseconds. But an execution time of several minutes for a data file with less than 512 KiB was definitely not expected by me.



So I looked further on this issue with creating a batch file using four different methods to create a CSV file with decimal values from a data file with hexadecimal values.



The complete batch file for testing the four methods is appended below as well as my test results.



I understand that the first two methods using a subroutine are much slower than the last two methods doing the conversion in one loop with output each CSV line into file on each loop iteration respectively once at end of FOR loop because of calling a subroutine causes several additional steps done by cmd.exe which altogether take a lot of time on doing subroutine calling thousands of times.



But I do not really understand why first method using a GOTO loop is about six times slower than FOR loop with nearly the same two command lines.



Batch file code of method 1:



@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "DelDataFile="
set "DataFile=%TEMP%HexValues.dat"
if not exist "%DataFile%" set "DelDataFile=1" & echo 000A002D0044008B0125018C01F40237>"%DataFile%"

for /F "usebackq delims=" %%I in ("%DataFile%") do call :ConvertLine "%%I"

if defined DelDataFile del "%DataFile%"
endlocal
goto :EOF

:ConvertLine
set "DataLine=%~1"
set "AllValues="
set "StartColumn=0"
:NextValue
set /A Value=0x!DataLine:~%StartColumn%,4!
set "AllValues=%AllValues%,%Value%"
set /A StartColumn+=4
if not %StartColumn% == 32 goto NextValue
echo %AllValues:~1%
goto :EOF


Batch file code of method 2:



@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "DelDataFile="
set "DataFile=%TEMP%HexValues.dat"
if not exist "%DataFile%" set "DelDataFile=1" & echo 000A002D0044008B0125018C01F40237>"%DataFile%"

for /F "usebackq delims=" %%I in ("%DataFile%") do call :LineConvert "%%I"

if defined DelDataFile del "%DataFile%"
endlocal
goto :EOF

:LineConvert
set "DataLine=%~1"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!
goto :EOF


And I discovered additionally on running tests to find out the reason by myself that method 1 takes 5 to 10 seconds longer on PC running on battery than on power supply being plugged in.



The question:



What is the reason for much slower execution of the GOTO loop used by method 1 in comparison to the FOR loop used by method 2 and why does method 1 depend on kind of power supply of PC?





Here is the entire batch file being used to compare the different methods:



@echo off
setlocal EnableExtensions EnableDelayedExpansion
cls
set "TestRuns=5"
set "DelDataFile="
set "DataFile=%TEMP%HexValues.dat"
if exist "%DataFile%" goto InitMethod1

set "DelDataFile=1"
echo Creating data file which takes some seconds, please wait ...
setlocal
set "HexDigits=0123456789ABCDEF"
set "DataLine="
(for /L %%I in (0,1,32767) do (
set /A "Digit1=(%%I >> 12) %% 16"
set /A "Digit2=(%%I >> 8) %% 16"
set /A "Digit3=(%%I >> 4) %% 16"
set /A "Digit4=%%I %% 16"
set "HexValue="
for %%J in (!Digit1! !Digit2! !Digit3! !Digit4!) do set "HexValue=!HexValue!!HexDigits:~%%J,1!"
set "DataLine=!DataLine!!HexValue!"
set /A "ValuesPerLine=%%I %% 8"
if !ValuesPerLine! == 7 (
echo !DataLine!
set "DataLine="
)
))>"%DataFile%"
endlocal
echo/


:InitMethod1
call :MethodInit 1
:RunMethod1
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

for /F "usebackq delims=" %%I in ("%DataFile%") do call :ConvertLine "%%I"

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod1
call :MethodResults
goto InitMethod2

:ConvertLine
set "DataLine=%~1"
set "AllValues="
set "StartColumn=0"
:NextValue
set /A Value=0x!DataLine:~%StartColumn%,4!
set "AllValues=%AllValues%,%Value%"
set /A StartColumn+=4
if not %StartColumn% == 32 goto NextValue
>>"%CSV_File%" echo %AllValues:~1%
goto :EOF


:InitMethod2
call :MethodInit 2
:RunMethod2
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

for /F "usebackq delims=" %%I in ("%DataFile%") do call :LineConvert "%%I"

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod2
call :MethodResults
goto InitMethod3

:LineConvert
set "DataLine=%~1"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!>>"%CSV_File%"
goto :EOF


:InitMethod3
call :MethodInit 3
:RunMethod3
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

for /F "usebackq delims=" %%I in ("%DataFile%") do (
set "DataLine=%%I"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!>>"%CSV_File%"
)

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod3
call :MethodResults
goto InitMethod4


:InitMethod4
call :MethodInit 4
:RunMethod4
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

(for /F "usebackq delims=" %%I in ("%DataFile%") do (
set "DataLine=%%I"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!
))>>"%CSV_File%"

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod4
call :MethodResults
goto EndBatch


:GetTime
for /F "tokens=2 delims==." %%I in ('%SystemRoot%System32wbemwmic.exe OS GET LocalDateTime /VALUE') do set "%1=%%I"
goto :EOF

:MethodInit
set "Method=%1"
echo Test runs with method %Method%
echo -----------------------
echo/
set "TestRun=0"
set "TotalTime=0"
goto :EOF

:MethodResults
set /A AverageTime=TotalTime / TestRun
echo Method %Method% total time: %TotalTime% seconds
echo Method %Method% average time: %AverageTime% seconds
echo/
goto :EOF

:OutputTime
call :GetTime EndTime
set /A StartTime=(1%StartTime:~8,2% - 100) * 3600 + (1%StartTime:~10,2% - 100) * 60 + 1%StartTime:~12,2% - 100
set /A EndTime=(1%EndTime:~8,2% - 100) * 3600 + (1%EndTime:~10,2% - 100) * 60 + 1%EndTime:~12,2% - 100
set /A DiffTime=EndTime - StartTime
set /A TotalTime+=DiffTime
echo Method %Method% run %TestRun% time: %DiffTime% seconds
goto :EOF


:EndBatch
if defined DelDataFile del "%DataFile%"
del /Q "%TEMP%Values?_*.csv"
endlocal


It creates first the data file with incrementing hexadecimal values in folder for temporary files which take already some seconds. Please comment the antepenultimate command line of this batch file to keep that file in case of running this batch file multiple times or being interested in this file.



Then it runs five times the four methods for reading the hexadecimal values from data file and writing the values in decimal into a CSV file with printing the test results to console respectively handle STDOUT.



Finally it deletes all CSV files created also in folder for temporary files all having same content. Please comment last but one command line to keep those CSV files on being interested in those files.



This batch file was executed by me four times on two notebooks.



Here are the results of first run on a notebook with an Intel Core Duo P8400 with 2.26 GHz and 2 GiB RAM with a HDD with 7200 rpm running Windows XP x86 with power supply plugged in:



Test runs with method 1
-----------------------

Method 1 run 1 time: 51 seconds
Method 1 run 2 time: 51 seconds
Method 1 run 3 time: 51 seconds
Method 1 run 4 time: 52 seconds
Method 1 run 5 time: 51 seconds
Method 1 total time: 256 seconds
Method 1 average time: 51 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 9 seconds
Method 2 run 2 time: 9 seconds
Method 2 run 3 time: 9 seconds
Method 2 run 4 time: 8 seconds
Method 2 run 5 time: 9 seconds
Method 2 total time: 44 seconds
Method 2 average time: 9 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 3 seconds
Method 3 run 2 time: 3 seconds
Method 3 run 3 time: 4 seconds
Method 3 run 4 time: 3 seconds
Method 3 run 5 time: 3 seconds
Method 3 total time: 16 seconds
Method 3 average time: 3 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 3 seconds
Method 4 run 2 time: 2 seconds
Method 4 run 3 time: 2 seconds
Method 4 run 4 time: 2 seconds
Method 4 run 5 time: 2 seconds
Method 4 total time: 11 seconds
Method 4 average time: 2 seconds


Method 2 is 5.67 times faster than method 1. Method 3 and 4 are even faster than method 2, but that is expected by me. Most of the 2 and 3 seconds needed by methods 3 and 4 are from WMIC command to get local date and time in region independent format.



Here are the results of second run on same computer as first run with the difference on running the PC on full charged battery:



Test runs with method 1
-----------------------

Method 1 run 1 time: 63 seconds
Method 1 run 2 time: 61 seconds
Method 1 run 3 time: 61 seconds
Method 1 run 4 time: 61 seconds
Method 1 run 5 time: 61 seconds
Method 1 total time: 307 seconds
Method 1 average time: 61 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 11 seconds
Method 2 run 2 time: 10 seconds
Method 2 run 3 time: 10 seconds
Method 2 run 4 time: 10 seconds
Method 2 run 5 time: 10 seconds
Method 2 total time: 51 seconds
Method 2 average time: 10 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 3 seconds
Method 3 run 2 time: 4 seconds
Method 3 run 3 time: 3 seconds
Method 3 run 4 time: 4 seconds
Method 3 run 5 time: 3 seconds
Method 3 total time: 17 seconds
Method 3 average time: 3 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 2 seconds
Method 4 run 2 time: 2 seconds
Method 4 run 3 time: 2 seconds
Method 4 run 4 time: 2 seconds
Method 4 run 5 time: 2 seconds
Method 4 total time: 10 seconds
Method 4 average time: 2 seconds


It can be seen that for methods 2 to 4 the processing times increase just a little bit. But processing time of method 1 increases by 10 seconds and so this solution is now about 6.10 times slower than method 2. I have no idea why processing time of method 1 depends on kind of power supply.



Here are the results of first run on a notebook with an Intel Core Duo T9600 with 2.80 GHz and 4 GiB RAM with an SSD running Windows 7 x64 with power supply plugged in:



Test runs with method 1
-----------------------

Method 1 run 1 time: 91 seconds
Method 1 run 2 time: 88 seconds
Method 1 run 3 time: 77 seconds
Method 1 run 4 time: 77 seconds
Method 1 run 5 time: 78 seconds
Method 1 total time: 411 seconds
Method 1 average time: 82 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 11 seconds
Method 2 run 2 time: 16 seconds
Method 2 run 3 time: 16 seconds
Method 2 run 4 time: 14 seconds
Method 2 run 5 time: 16 seconds
Method 2 total time: 73 seconds
Method 2 average time: 14 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 6 seconds
Method 3 run 2 time: 4 seconds
Method 3 run 3 time: 4 seconds
Method 3 run 4 time: 4 seconds
Method 3 run 5 time: 6 seconds
Method 3 total time: 24 seconds
Method 3 average time: 4 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 4 seconds
Method 4 run 2 time: 3 seconds
Method 4 run 3 time: 5 seconds
Method 4 run 4 time: 4 seconds
Method 4 run 5 time: 4 seconds
Method 4 total time: 20 seconds
Method 4 average time: 4 seconds


It was interesting to see that the batch file execution with more powerful hardware takes more time on Windows 7 x64 than on Windows XP x86. But even more interesting for me is again the fact that method 2 is 5.86 times faster than method 1 just because of using a FOR instead of a GOTO loop.



For completeness the results of fourth run on same computer as third run with the difference on running the PC on full charged battery:



Test runs with method 1
-----------------------

Method 1 run 1 time: 97 seconds
Method 1 run 2 time: 91 seconds
Method 1 run 3 time: 90 seconds
Method 1 run 4 time: 81 seconds
Method 1 run 5 time: 77 seconds
Method 1 total time: 436 seconds
Method 1 average time: 87 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 12 seconds
Method 2 run 2 time: 16 seconds
Method 2 run 3 time: 17 seconds
Method 2 run 4 time: 16 seconds
Method 2 run 5 time: 13 seconds
Method 2 total time: 74 seconds
Method 2 average time: 14 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 6 seconds
Method 3 run 2 time: 6 seconds
Method 3 run 3 time: 5 seconds
Method 3 run 4 time: 5 seconds
Method 3 run 5 time: 5 seconds
Method 3 total time: 27 seconds
Method 3 average time: 5 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 4 seconds
Method 4 run 2 time: 4 seconds
Method 4 run 3 time: 5 seconds
Method 4 run 4 time: 4 seconds
Method 4 run 5 time: 4 seconds
Method 4 total time: 21 seconds
Method 4 average time: 4 seconds


There is again not much difference in execution time for the methods 3 to 4 in comparison to third run with power supply plugged in. But the execution time of method 1 increases by approximately 5 seconds and for that reason method 1 is 6.21 times slower than method 2.



I would be really interested in why method 1 is so much slower than method 2 and additionally depends on kind of power supply.



The hard disk activity LED was only very rarely blinking on all test runs as expected by me because of Windows file caching.










share|improve this question




















  • 2





    Because the FOR command and all the lines that comprise it are read from the file just once and then the complete parsed code is executed several times from memory. On the other hand, a GOTO just transfer the execution, so all the lines in the loop are executed in the usual Batch file way: open file, read line, close file, parse line, execute line...

    – Aacini
    Nov 18 '18 at 18:13








  • 1





    With a :top label at top of full script, set a counter variable and exit at 32767. Add a goto :bottom. At bottom, add :bottom and goto :top. It takes 25s just to execute that as it has to read each line of the whole script 32767 times searching down for the :bottom label.

    – michael_heath
    Nov 19 '18 at 8:52











  • @Aacini You are absolutely right on what you wrote above. That can be seen on file system accesses logged by Sysinternals Process Monitor during execution of the batch files. The solutions not using GOTO and CALL on processing the data in HexValues.dat don't make any file access on HexValues.dat and the batch file while processing all lines in HexValues.dat in comparison to the solutions with GOTO and CALL on which the batch file is read line by line extremely often. So a FOR loop with using delayed expansion is definitely better than using GOTO or CALL.

    – Mofi
    Nov 19 '18 at 10:33














3












3








3








I wrote a small batch file to convert a file containing eight 16 bit hexadecimal values per line into a CSV file with the eight values in decimal.



The input data file was the captured output of ADC values of an embedded device sent in packets of eight hexadecimal values in ASCII with carriage return plus line-feed via RS-232 to PC and simply captured on PC into a file. One line in input data file was something like:



000A002D0044008B0125018C01F40237


The CSV file for this line was:



10,45,68,139,293,396,500,567


The batch file worked, but it took several minutes to complete the conversion which shocked me. I expected that Windows command processor takes some seconds for this task which a console application written in C or C++ could to in some milliseconds. But an execution time of several minutes for a data file with less than 512 KiB was definitely not expected by me.



So I looked further on this issue with creating a batch file using four different methods to create a CSV file with decimal values from a data file with hexadecimal values.



The complete batch file for testing the four methods is appended below as well as my test results.



I understand that the first two methods using a subroutine are much slower than the last two methods doing the conversion in one loop with output each CSV line into file on each loop iteration respectively once at end of FOR loop because of calling a subroutine causes several additional steps done by cmd.exe which altogether take a lot of time on doing subroutine calling thousands of times.



But I do not really understand why first method using a GOTO loop is about six times slower than FOR loop with nearly the same two command lines.



Batch file code of method 1:



@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "DelDataFile="
set "DataFile=%TEMP%HexValues.dat"
if not exist "%DataFile%" set "DelDataFile=1" & echo 000A002D0044008B0125018C01F40237>"%DataFile%"

for /F "usebackq delims=" %%I in ("%DataFile%") do call :ConvertLine "%%I"

if defined DelDataFile del "%DataFile%"
endlocal
goto :EOF

:ConvertLine
set "DataLine=%~1"
set "AllValues="
set "StartColumn=0"
:NextValue
set /A Value=0x!DataLine:~%StartColumn%,4!
set "AllValues=%AllValues%,%Value%"
set /A StartColumn+=4
if not %StartColumn% == 32 goto NextValue
echo %AllValues:~1%
goto :EOF


Batch file code of method 2:



@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "DelDataFile="
set "DataFile=%TEMP%HexValues.dat"
if not exist "%DataFile%" set "DelDataFile=1" & echo 000A002D0044008B0125018C01F40237>"%DataFile%"

for /F "usebackq delims=" %%I in ("%DataFile%") do call :LineConvert "%%I"

if defined DelDataFile del "%DataFile%"
endlocal
goto :EOF

:LineConvert
set "DataLine=%~1"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!
goto :EOF


And I discovered additionally on running tests to find out the reason by myself that method 1 takes 5 to 10 seconds longer on PC running on battery than on power supply being plugged in.



The question:



What is the reason for much slower execution of the GOTO loop used by method 1 in comparison to the FOR loop used by method 2 and why does method 1 depend on kind of power supply of PC?





Here is the entire batch file being used to compare the different methods:



@echo off
setlocal EnableExtensions EnableDelayedExpansion
cls
set "TestRuns=5"
set "DelDataFile="
set "DataFile=%TEMP%HexValues.dat"
if exist "%DataFile%" goto InitMethod1

set "DelDataFile=1"
echo Creating data file which takes some seconds, please wait ...
setlocal
set "HexDigits=0123456789ABCDEF"
set "DataLine="
(for /L %%I in (0,1,32767) do (
set /A "Digit1=(%%I >> 12) %% 16"
set /A "Digit2=(%%I >> 8) %% 16"
set /A "Digit3=(%%I >> 4) %% 16"
set /A "Digit4=%%I %% 16"
set "HexValue="
for %%J in (!Digit1! !Digit2! !Digit3! !Digit4!) do set "HexValue=!HexValue!!HexDigits:~%%J,1!"
set "DataLine=!DataLine!!HexValue!"
set /A "ValuesPerLine=%%I %% 8"
if !ValuesPerLine! == 7 (
echo !DataLine!
set "DataLine="
)
))>"%DataFile%"
endlocal
echo/


:InitMethod1
call :MethodInit 1
:RunMethod1
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

for /F "usebackq delims=" %%I in ("%DataFile%") do call :ConvertLine "%%I"

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod1
call :MethodResults
goto InitMethod2

:ConvertLine
set "DataLine=%~1"
set "AllValues="
set "StartColumn=0"
:NextValue
set /A Value=0x!DataLine:~%StartColumn%,4!
set "AllValues=%AllValues%,%Value%"
set /A StartColumn+=4
if not %StartColumn% == 32 goto NextValue
>>"%CSV_File%" echo %AllValues:~1%
goto :EOF


:InitMethod2
call :MethodInit 2
:RunMethod2
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

for /F "usebackq delims=" %%I in ("%DataFile%") do call :LineConvert "%%I"

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod2
call :MethodResults
goto InitMethod3

:LineConvert
set "DataLine=%~1"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!>>"%CSV_File%"
goto :EOF


:InitMethod3
call :MethodInit 3
:RunMethod3
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

for /F "usebackq delims=" %%I in ("%DataFile%") do (
set "DataLine=%%I"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!>>"%CSV_File%"
)

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod3
call :MethodResults
goto InitMethod4


:InitMethod4
call :MethodInit 4
:RunMethod4
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

(for /F "usebackq delims=" %%I in ("%DataFile%") do (
set "DataLine=%%I"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!
))>>"%CSV_File%"

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod4
call :MethodResults
goto EndBatch


:GetTime
for /F "tokens=2 delims==." %%I in ('%SystemRoot%System32wbemwmic.exe OS GET LocalDateTime /VALUE') do set "%1=%%I"
goto :EOF

:MethodInit
set "Method=%1"
echo Test runs with method %Method%
echo -----------------------
echo/
set "TestRun=0"
set "TotalTime=0"
goto :EOF

:MethodResults
set /A AverageTime=TotalTime / TestRun
echo Method %Method% total time: %TotalTime% seconds
echo Method %Method% average time: %AverageTime% seconds
echo/
goto :EOF

:OutputTime
call :GetTime EndTime
set /A StartTime=(1%StartTime:~8,2% - 100) * 3600 + (1%StartTime:~10,2% - 100) * 60 + 1%StartTime:~12,2% - 100
set /A EndTime=(1%EndTime:~8,2% - 100) * 3600 + (1%EndTime:~10,2% - 100) * 60 + 1%EndTime:~12,2% - 100
set /A DiffTime=EndTime - StartTime
set /A TotalTime+=DiffTime
echo Method %Method% run %TestRun% time: %DiffTime% seconds
goto :EOF


:EndBatch
if defined DelDataFile del "%DataFile%"
del /Q "%TEMP%Values?_*.csv"
endlocal


It creates first the data file with incrementing hexadecimal values in folder for temporary files which take already some seconds. Please comment the antepenultimate command line of this batch file to keep that file in case of running this batch file multiple times or being interested in this file.



Then it runs five times the four methods for reading the hexadecimal values from data file and writing the values in decimal into a CSV file with printing the test results to console respectively handle STDOUT.



Finally it deletes all CSV files created also in folder for temporary files all having same content. Please comment last but one command line to keep those CSV files on being interested in those files.



This batch file was executed by me four times on two notebooks.



Here are the results of first run on a notebook with an Intel Core Duo P8400 with 2.26 GHz and 2 GiB RAM with a HDD with 7200 rpm running Windows XP x86 with power supply plugged in:



Test runs with method 1
-----------------------

Method 1 run 1 time: 51 seconds
Method 1 run 2 time: 51 seconds
Method 1 run 3 time: 51 seconds
Method 1 run 4 time: 52 seconds
Method 1 run 5 time: 51 seconds
Method 1 total time: 256 seconds
Method 1 average time: 51 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 9 seconds
Method 2 run 2 time: 9 seconds
Method 2 run 3 time: 9 seconds
Method 2 run 4 time: 8 seconds
Method 2 run 5 time: 9 seconds
Method 2 total time: 44 seconds
Method 2 average time: 9 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 3 seconds
Method 3 run 2 time: 3 seconds
Method 3 run 3 time: 4 seconds
Method 3 run 4 time: 3 seconds
Method 3 run 5 time: 3 seconds
Method 3 total time: 16 seconds
Method 3 average time: 3 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 3 seconds
Method 4 run 2 time: 2 seconds
Method 4 run 3 time: 2 seconds
Method 4 run 4 time: 2 seconds
Method 4 run 5 time: 2 seconds
Method 4 total time: 11 seconds
Method 4 average time: 2 seconds


Method 2 is 5.67 times faster than method 1. Method 3 and 4 are even faster than method 2, but that is expected by me. Most of the 2 and 3 seconds needed by methods 3 and 4 are from WMIC command to get local date and time in region independent format.



Here are the results of second run on same computer as first run with the difference on running the PC on full charged battery:



Test runs with method 1
-----------------------

Method 1 run 1 time: 63 seconds
Method 1 run 2 time: 61 seconds
Method 1 run 3 time: 61 seconds
Method 1 run 4 time: 61 seconds
Method 1 run 5 time: 61 seconds
Method 1 total time: 307 seconds
Method 1 average time: 61 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 11 seconds
Method 2 run 2 time: 10 seconds
Method 2 run 3 time: 10 seconds
Method 2 run 4 time: 10 seconds
Method 2 run 5 time: 10 seconds
Method 2 total time: 51 seconds
Method 2 average time: 10 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 3 seconds
Method 3 run 2 time: 4 seconds
Method 3 run 3 time: 3 seconds
Method 3 run 4 time: 4 seconds
Method 3 run 5 time: 3 seconds
Method 3 total time: 17 seconds
Method 3 average time: 3 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 2 seconds
Method 4 run 2 time: 2 seconds
Method 4 run 3 time: 2 seconds
Method 4 run 4 time: 2 seconds
Method 4 run 5 time: 2 seconds
Method 4 total time: 10 seconds
Method 4 average time: 2 seconds


It can be seen that for methods 2 to 4 the processing times increase just a little bit. But processing time of method 1 increases by 10 seconds and so this solution is now about 6.10 times slower than method 2. I have no idea why processing time of method 1 depends on kind of power supply.



Here are the results of first run on a notebook with an Intel Core Duo T9600 with 2.80 GHz and 4 GiB RAM with an SSD running Windows 7 x64 with power supply plugged in:



Test runs with method 1
-----------------------

Method 1 run 1 time: 91 seconds
Method 1 run 2 time: 88 seconds
Method 1 run 3 time: 77 seconds
Method 1 run 4 time: 77 seconds
Method 1 run 5 time: 78 seconds
Method 1 total time: 411 seconds
Method 1 average time: 82 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 11 seconds
Method 2 run 2 time: 16 seconds
Method 2 run 3 time: 16 seconds
Method 2 run 4 time: 14 seconds
Method 2 run 5 time: 16 seconds
Method 2 total time: 73 seconds
Method 2 average time: 14 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 6 seconds
Method 3 run 2 time: 4 seconds
Method 3 run 3 time: 4 seconds
Method 3 run 4 time: 4 seconds
Method 3 run 5 time: 6 seconds
Method 3 total time: 24 seconds
Method 3 average time: 4 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 4 seconds
Method 4 run 2 time: 3 seconds
Method 4 run 3 time: 5 seconds
Method 4 run 4 time: 4 seconds
Method 4 run 5 time: 4 seconds
Method 4 total time: 20 seconds
Method 4 average time: 4 seconds


It was interesting to see that the batch file execution with more powerful hardware takes more time on Windows 7 x64 than on Windows XP x86. But even more interesting for me is again the fact that method 2 is 5.86 times faster than method 1 just because of using a FOR instead of a GOTO loop.



For completeness the results of fourth run on same computer as third run with the difference on running the PC on full charged battery:



Test runs with method 1
-----------------------

Method 1 run 1 time: 97 seconds
Method 1 run 2 time: 91 seconds
Method 1 run 3 time: 90 seconds
Method 1 run 4 time: 81 seconds
Method 1 run 5 time: 77 seconds
Method 1 total time: 436 seconds
Method 1 average time: 87 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 12 seconds
Method 2 run 2 time: 16 seconds
Method 2 run 3 time: 17 seconds
Method 2 run 4 time: 16 seconds
Method 2 run 5 time: 13 seconds
Method 2 total time: 74 seconds
Method 2 average time: 14 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 6 seconds
Method 3 run 2 time: 6 seconds
Method 3 run 3 time: 5 seconds
Method 3 run 4 time: 5 seconds
Method 3 run 5 time: 5 seconds
Method 3 total time: 27 seconds
Method 3 average time: 5 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 4 seconds
Method 4 run 2 time: 4 seconds
Method 4 run 3 time: 5 seconds
Method 4 run 4 time: 4 seconds
Method 4 run 5 time: 4 seconds
Method 4 total time: 21 seconds
Method 4 average time: 4 seconds


There is again not much difference in execution time for the methods 3 to 4 in comparison to third run with power supply plugged in. But the execution time of method 1 increases by approximately 5 seconds and for that reason method 1 is 6.21 times slower than method 2.



I would be really interested in why method 1 is so much slower than method 2 and additionally depends on kind of power supply.



The hard disk activity LED was only very rarely blinking on all test runs as expected by me because of Windows file caching.










share|improve this question
















I wrote a small batch file to convert a file containing eight 16 bit hexadecimal values per line into a CSV file with the eight values in decimal.



The input data file was the captured output of ADC values of an embedded device sent in packets of eight hexadecimal values in ASCII with carriage return plus line-feed via RS-232 to PC and simply captured on PC into a file. One line in input data file was something like:



000A002D0044008B0125018C01F40237


The CSV file for this line was:



10,45,68,139,293,396,500,567


The batch file worked, but it took several minutes to complete the conversion which shocked me. I expected that Windows command processor takes some seconds for this task which a console application written in C or C++ could to in some milliseconds. But an execution time of several minutes for a data file with less than 512 KiB was definitely not expected by me.



So I looked further on this issue with creating a batch file using four different methods to create a CSV file with decimal values from a data file with hexadecimal values.



The complete batch file for testing the four methods is appended below as well as my test results.



I understand that the first two methods using a subroutine are much slower than the last two methods doing the conversion in one loop with output each CSV line into file on each loop iteration respectively once at end of FOR loop because of calling a subroutine causes several additional steps done by cmd.exe which altogether take a lot of time on doing subroutine calling thousands of times.



But I do not really understand why first method using a GOTO loop is about six times slower than FOR loop with nearly the same two command lines.



Batch file code of method 1:



@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "DelDataFile="
set "DataFile=%TEMP%HexValues.dat"
if not exist "%DataFile%" set "DelDataFile=1" & echo 000A002D0044008B0125018C01F40237>"%DataFile%"

for /F "usebackq delims=" %%I in ("%DataFile%") do call :ConvertLine "%%I"

if defined DelDataFile del "%DataFile%"
endlocal
goto :EOF

:ConvertLine
set "DataLine=%~1"
set "AllValues="
set "StartColumn=0"
:NextValue
set /A Value=0x!DataLine:~%StartColumn%,4!
set "AllValues=%AllValues%,%Value%"
set /A StartColumn+=4
if not %StartColumn% == 32 goto NextValue
echo %AllValues:~1%
goto :EOF


Batch file code of method 2:



@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "DelDataFile="
set "DataFile=%TEMP%HexValues.dat"
if not exist "%DataFile%" set "DelDataFile=1" & echo 000A002D0044008B0125018C01F40237>"%DataFile%"

for /F "usebackq delims=" %%I in ("%DataFile%") do call :LineConvert "%%I"

if defined DelDataFile del "%DataFile%"
endlocal
goto :EOF

:LineConvert
set "DataLine=%~1"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!
goto :EOF


And I discovered additionally on running tests to find out the reason by myself that method 1 takes 5 to 10 seconds longer on PC running on battery than on power supply being plugged in.



The question:



What is the reason for much slower execution of the GOTO loop used by method 1 in comparison to the FOR loop used by method 2 and why does method 1 depend on kind of power supply of PC?





Here is the entire batch file being used to compare the different methods:



@echo off
setlocal EnableExtensions EnableDelayedExpansion
cls
set "TestRuns=5"
set "DelDataFile="
set "DataFile=%TEMP%HexValues.dat"
if exist "%DataFile%" goto InitMethod1

set "DelDataFile=1"
echo Creating data file which takes some seconds, please wait ...
setlocal
set "HexDigits=0123456789ABCDEF"
set "DataLine="
(for /L %%I in (0,1,32767) do (
set /A "Digit1=(%%I >> 12) %% 16"
set /A "Digit2=(%%I >> 8) %% 16"
set /A "Digit3=(%%I >> 4) %% 16"
set /A "Digit4=%%I %% 16"
set "HexValue="
for %%J in (!Digit1! !Digit2! !Digit3! !Digit4!) do set "HexValue=!HexValue!!HexDigits:~%%J,1!"
set "DataLine=!DataLine!!HexValue!"
set /A "ValuesPerLine=%%I %% 8"
if !ValuesPerLine! == 7 (
echo !DataLine!
set "DataLine="
)
))>"%DataFile%"
endlocal
echo/


:InitMethod1
call :MethodInit 1
:RunMethod1
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

for /F "usebackq delims=" %%I in ("%DataFile%") do call :ConvertLine "%%I"

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod1
call :MethodResults
goto InitMethod2

:ConvertLine
set "DataLine=%~1"
set "AllValues="
set "StartColumn=0"
:NextValue
set /A Value=0x!DataLine:~%StartColumn%,4!
set "AllValues=%AllValues%,%Value%"
set /A StartColumn+=4
if not %StartColumn% == 32 goto NextValue
>>"%CSV_File%" echo %AllValues:~1%
goto :EOF


:InitMethod2
call :MethodInit 2
:RunMethod2
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

for /F "usebackq delims=" %%I in ("%DataFile%") do call :LineConvert "%%I"

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod2
call :MethodResults
goto InitMethod3

:LineConvert
set "DataLine=%~1"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!>>"%CSV_File%"
goto :EOF


:InitMethod3
call :MethodInit 3
:RunMethod3
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

for /F "usebackq delims=" %%I in ("%DataFile%") do (
set "DataLine=%%I"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!>>"%CSV_File%"
)

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod3
call :MethodResults
goto InitMethod4


:InitMethod4
call :MethodInit 4
:RunMethod4
set /A TestRun+=1
set "CSV_File=%TEMP%Values%Method%_%TestRun%.csv"
del "%CSV_File%" 2>nul
call :GetTime StartTime

(for /F "usebackq delims=" %%I in ("%DataFile%") do (
set "DataLine=%%I"
set "AllValues="
for /L %%J in (0,4,28) do (
set /A Value=0x!DataLine:~%%J,4!
set "AllValues=!AllValues!,!Value!"
)
echo !AllValues:~1!
))>>"%CSV_File%"

call :OutputTime
if %TestRun% LSS %TestRuns% goto RunMethod4
call :MethodResults
goto EndBatch


:GetTime
for /F "tokens=2 delims==." %%I in ('%SystemRoot%System32wbemwmic.exe OS GET LocalDateTime /VALUE') do set "%1=%%I"
goto :EOF

:MethodInit
set "Method=%1"
echo Test runs with method %Method%
echo -----------------------
echo/
set "TestRun=0"
set "TotalTime=0"
goto :EOF

:MethodResults
set /A AverageTime=TotalTime / TestRun
echo Method %Method% total time: %TotalTime% seconds
echo Method %Method% average time: %AverageTime% seconds
echo/
goto :EOF

:OutputTime
call :GetTime EndTime
set /A StartTime=(1%StartTime:~8,2% - 100) * 3600 + (1%StartTime:~10,2% - 100) * 60 + 1%StartTime:~12,2% - 100
set /A EndTime=(1%EndTime:~8,2% - 100) * 3600 + (1%EndTime:~10,2% - 100) * 60 + 1%EndTime:~12,2% - 100
set /A DiffTime=EndTime - StartTime
set /A TotalTime+=DiffTime
echo Method %Method% run %TestRun% time: %DiffTime% seconds
goto :EOF


:EndBatch
if defined DelDataFile del "%DataFile%"
del /Q "%TEMP%Values?_*.csv"
endlocal


It creates first the data file with incrementing hexadecimal values in folder for temporary files which take already some seconds. Please comment the antepenultimate command line of this batch file to keep that file in case of running this batch file multiple times or being interested in this file.



Then it runs five times the four methods for reading the hexadecimal values from data file and writing the values in decimal into a CSV file with printing the test results to console respectively handle STDOUT.



Finally it deletes all CSV files created also in folder for temporary files all having same content. Please comment last but one command line to keep those CSV files on being interested in those files.



This batch file was executed by me four times on two notebooks.



Here are the results of first run on a notebook with an Intel Core Duo P8400 with 2.26 GHz and 2 GiB RAM with a HDD with 7200 rpm running Windows XP x86 with power supply plugged in:



Test runs with method 1
-----------------------

Method 1 run 1 time: 51 seconds
Method 1 run 2 time: 51 seconds
Method 1 run 3 time: 51 seconds
Method 1 run 4 time: 52 seconds
Method 1 run 5 time: 51 seconds
Method 1 total time: 256 seconds
Method 1 average time: 51 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 9 seconds
Method 2 run 2 time: 9 seconds
Method 2 run 3 time: 9 seconds
Method 2 run 4 time: 8 seconds
Method 2 run 5 time: 9 seconds
Method 2 total time: 44 seconds
Method 2 average time: 9 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 3 seconds
Method 3 run 2 time: 3 seconds
Method 3 run 3 time: 4 seconds
Method 3 run 4 time: 3 seconds
Method 3 run 5 time: 3 seconds
Method 3 total time: 16 seconds
Method 3 average time: 3 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 3 seconds
Method 4 run 2 time: 2 seconds
Method 4 run 3 time: 2 seconds
Method 4 run 4 time: 2 seconds
Method 4 run 5 time: 2 seconds
Method 4 total time: 11 seconds
Method 4 average time: 2 seconds


Method 2 is 5.67 times faster than method 1. Method 3 and 4 are even faster than method 2, but that is expected by me. Most of the 2 and 3 seconds needed by methods 3 and 4 are from WMIC command to get local date and time in region independent format.



Here are the results of second run on same computer as first run with the difference on running the PC on full charged battery:



Test runs with method 1
-----------------------

Method 1 run 1 time: 63 seconds
Method 1 run 2 time: 61 seconds
Method 1 run 3 time: 61 seconds
Method 1 run 4 time: 61 seconds
Method 1 run 5 time: 61 seconds
Method 1 total time: 307 seconds
Method 1 average time: 61 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 11 seconds
Method 2 run 2 time: 10 seconds
Method 2 run 3 time: 10 seconds
Method 2 run 4 time: 10 seconds
Method 2 run 5 time: 10 seconds
Method 2 total time: 51 seconds
Method 2 average time: 10 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 3 seconds
Method 3 run 2 time: 4 seconds
Method 3 run 3 time: 3 seconds
Method 3 run 4 time: 4 seconds
Method 3 run 5 time: 3 seconds
Method 3 total time: 17 seconds
Method 3 average time: 3 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 2 seconds
Method 4 run 2 time: 2 seconds
Method 4 run 3 time: 2 seconds
Method 4 run 4 time: 2 seconds
Method 4 run 5 time: 2 seconds
Method 4 total time: 10 seconds
Method 4 average time: 2 seconds


It can be seen that for methods 2 to 4 the processing times increase just a little bit. But processing time of method 1 increases by 10 seconds and so this solution is now about 6.10 times slower than method 2. I have no idea why processing time of method 1 depends on kind of power supply.



Here are the results of first run on a notebook with an Intel Core Duo T9600 with 2.80 GHz and 4 GiB RAM with an SSD running Windows 7 x64 with power supply plugged in:



Test runs with method 1
-----------------------

Method 1 run 1 time: 91 seconds
Method 1 run 2 time: 88 seconds
Method 1 run 3 time: 77 seconds
Method 1 run 4 time: 77 seconds
Method 1 run 5 time: 78 seconds
Method 1 total time: 411 seconds
Method 1 average time: 82 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 11 seconds
Method 2 run 2 time: 16 seconds
Method 2 run 3 time: 16 seconds
Method 2 run 4 time: 14 seconds
Method 2 run 5 time: 16 seconds
Method 2 total time: 73 seconds
Method 2 average time: 14 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 6 seconds
Method 3 run 2 time: 4 seconds
Method 3 run 3 time: 4 seconds
Method 3 run 4 time: 4 seconds
Method 3 run 5 time: 6 seconds
Method 3 total time: 24 seconds
Method 3 average time: 4 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 4 seconds
Method 4 run 2 time: 3 seconds
Method 4 run 3 time: 5 seconds
Method 4 run 4 time: 4 seconds
Method 4 run 5 time: 4 seconds
Method 4 total time: 20 seconds
Method 4 average time: 4 seconds


It was interesting to see that the batch file execution with more powerful hardware takes more time on Windows 7 x64 than on Windows XP x86. But even more interesting for me is again the fact that method 2 is 5.86 times faster than method 1 just because of using a FOR instead of a GOTO loop.



For completeness the results of fourth run on same computer as third run with the difference on running the PC on full charged battery:



Test runs with method 1
-----------------------

Method 1 run 1 time: 97 seconds
Method 1 run 2 time: 91 seconds
Method 1 run 3 time: 90 seconds
Method 1 run 4 time: 81 seconds
Method 1 run 5 time: 77 seconds
Method 1 total time: 436 seconds
Method 1 average time: 87 seconds

Test runs with method 2
-----------------------

Method 2 run 1 time: 12 seconds
Method 2 run 2 time: 16 seconds
Method 2 run 3 time: 17 seconds
Method 2 run 4 time: 16 seconds
Method 2 run 5 time: 13 seconds
Method 2 total time: 74 seconds
Method 2 average time: 14 seconds

Test runs with method 3
-----------------------

Method 3 run 1 time: 6 seconds
Method 3 run 2 time: 6 seconds
Method 3 run 3 time: 5 seconds
Method 3 run 4 time: 5 seconds
Method 3 run 5 time: 5 seconds
Method 3 total time: 27 seconds
Method 3 average time: 5 seconds

Test runs with method 4
-----------------------

Method 4 run 1 time: 4 seconds
Method 4 run 2 time: 4 seconds
Method 4 run 3 time: 5 seconds
Method 4 run 4 time: 4 seconds
Method 4 run 5 time: 4 seconds
Method 4 total time: 21 seconds
Method 4 average time: 4 seconds


There is again not much difference in execution time for the methods 3 to 4 in comparison to third run with power supply plugged in. But the execution time of method 1 increases by approximately 5 seconds and for that reason method 1 is 6.21 times slower than method 2.



I would be really interested in why method 1 is so much slower than method 2 and additionally depends on kind of power supply.



The hard disk activity LED was only very rarely blinking on all test runs as expected by me because of Windows file caching.







windows batch-file cmd






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 19 '18 at 12:44







Mofi

















asked Nov 18 '18 at 16:19









MofiMofi

28.1k83777




28.1k83777








  • 2





    Because the FOR command and all the lines that comprise it are read from the file just once and then the complete parsed code is executed several times from memory. On the other hand, a GOTO just transfer the execution, so all the lines in the loop are executed in the usual Batch file way: open file, read line, close file, parse line, execute line...

    – Aacini
    Nov 18 '18 at 18:13








  • 1





    With a :top label at top of full script, set a counter variable and exit at 32767. Add a goto :bottom. At bottom, add :bottom and goto :top. It takes 25s just to execute that as it has to read each line of the whole script 32767 times searching down for the :bottom label.

    – michael_heath
    Nov 19 '18 at 8:52











  • @Aacini You are absolutely right on what you wrote above. That can be seen on file system accesses logged by Sysinternals Process Monitor during execution of the batch files. The solutions not using GOTO and CALL on processing the data in HexValues.dat don't make any file access on HexValues.dat and the batch file while processing all lines in HexValues.dat in comparison to the solutions with GOTO and CALL on which the batch file is read line by line extremely often. So a FOR loop with using delayed expansion is definitely better than using GOTO or CALL.

    – Mofi
    Nov 19 '18 at 10:33














  • 2





    Because the FOR command and all the lines that comprise it are read from the file just once and then the complete parsed code is executed several times from memory. On the other hand, a GOTO just transfer the execution, so all the lines in the loop are executed in the usual Batch file way: open file, read line, close file, parse line, execute line...

    – Aacini
    Nov 18 '18 at 18:13








  • 1





    With a :top label at top of full script, set a counter variable and exit at 32767. Add a goto :bottom. At bottom, add :bottom and goto :top. It takes 25s just to execute that as it has to read each line of the whole script 32767 times searching down for the :bottom label.

    – michael_heath
    Nov 19 '18 at 8:52











  • @Aacini You are absolutely right on what you wrote above. That can be seen on file system accesses logged by Sysinternals Process Monitor during execution of the batch files. The solutions not using GOTO and CALL on processing the data in HexValues.dat don't make any file access on HexValues.dat and the batch file while processing all lines in HexValues.dat in comparison to the solutions with GOTO and CALL on which the batch file is read line by line extremely often. So a FOR loop with using delayed expansion is definitely better than using GOTO or CALL.

    – Mofi
    Nov 19 '18 at 10:33








2




2





Because the FOR command and all the lines that comprise it are read from the file just once and then the complete parsed code is executed several times from memory. On the other hand, a GOTO just transfer the execution, so all the lines in the loop are executed in the usual Batch file way: open file, read line, close file, parse line, execute line...

– Aacini
Nov 18 '18 at 18:13







Because the FOR command and all the lines that comprise it are read from the file just once and then the complete parsed code is executed several times from memory. On the other hand, a GOTO just transfer the execution, so all the lines in the loop are executed in the usual Batch file way: open file, read line, close file, parse line, execute line...

– Aacini
Nov 18 '18 at 18:13






1




1





With a :top label at top of full script, set a counter variable and exit at 32767. Add a goto :bottom. At bottom, add :bottom and goto :top. It takes 25s just to execute that as it has to read each line of the whole script 32767 times searching down for the :bottom label.

– michael_heath
Nov 19 '18 at 8:52





With a :top label at top of full script, set a counter variable and exit at 32767. Add a goto :bottom. At bottom, add :bottom and goto :top. It takes 25s just to execute that as it has to read each line of the whole script 32767 times searching down for the :bottom label.

– michael_heath
Nov 19 '18 at 8:52













@Aacini You are absolutely right on what you wrote above. That can be seen on file system accesses logged by Sysinternals Process Monitor during execution of the batch files. The solutions not using GOTO and CALL on processing the data in HexValues.dat don't make any file access on HexValues.dat and the batch file while processing all lines in HexValues.dat in comparison to the solutions with GOTO and CALL on which the batch file is read line by line extremely often. So a FOR loop with using delayed expansion is definitely better than using GOTO or CALL.

– Mofi
Nov 19 '18 at 10:33





@Aacini You are absolutely right on what you wrote above. That can be seen on file system accesses logged by Sysinternals Process Monitor during execution of the batch files. The solutions not using GOTO and CALL on processing the data in HexValues.dat don't make any file access on HexValues.dat and the batch file while processing all lines in HexValues.dat in comparison to the solutions with GOTO and CALL on which the batch file is read line by line extremely often. So a FOR loop with using delayed expansion is definitely better than using GOTO or CALL.

– Mofi
Nov 19 '18 at 10:33












5 Answers
5






active

oldest

votes


















3














According to this analysis of the interpreter, the FOR variable will be expanded in phase 4, so the interpreter will know how many times to execute the command and on what values immediately. In contrast, each GOTO is interpreted in phase 7, and it requires scanning the file anew looking for the label each time, which accounts for the perceived time difference.






share|improve this answer

































    4














    If I recall correctly, the GOTO LABEL command in a batch file actually scans the remainder of the text looking for the label, and if it doesn't find it then it restarts at the top.



    That's a pretty expensive operation, and I think CALL does it too.



    So if you care about performance then you should try to avoid these constructs. Or, even better, don't be doing this kind of work in batch files.






    share|improve this answer































      3














      It's because of the way goto works. Unlike a compiled programming language, where a goto is translated into a fixed address during the compilation, batch has to search through the file for the label for every goto.



      It searches the file downwards from the goto line, and if it doesn't find the label, continues the search from the start of the file.






      share|improve this answer































        1














        As already said, GOTO and CALL searches for next matching the label from the current file position to the end of file, then it searches from the file begin up to the current file position.

        This behaviour has some useful effects, as you don't have to bother about different label names in functions.



        :myFunc1
        <some code>
        goto :someLabel -- this goto's to the next :someLabel

        :someLabel

        :myFunc2
        <some code>
        goto :someLabel -- this goto's to the next :someLabel

        :someLabel


        But when you build loops, there is a big speed advantage, because the complete FOR parenthesis block will be read only once and parsed up to phase 2.

        The parsed block is in a cmd cache, therefore there aren't any further disk reads necessary and the tokenization is already done.



        Related topics
        Rules for label names vs GOTO and CALL
        CALL me, or better avoid call






        share|improve this answer





















        • 2





          I suggested somewhere to use :@F label to define a repeated label that will only be used to jump a few lines forward via GOTO @F. This is with the purpose of be similar to the anonymous label feature of MASM (that is used in the same way), and to give an indication (by convention) that such a label can appear several times and it is not an error...

          – Aacini
          Nov 20 '18 at 14:01



















        0














        Thanks for the answers. It looks like you are all right.



        The reason is searching for label referenced by GOTO first downwards in batch file and next from top on not finding it to bottom of file.



        I verified that with using first a batch file with following lines and a file size of 941 bytes:



        @echo off
        setlocal EnableExtensions EnableDelayedExpansion
        for /F "tokens=2 delims==." %%I in ('%SystemRoot%System32wbemwmic.exe OS GET LocalDateTime /VALUE') do set "StartTime=%%I"
        for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do call :ConvertLine "%%I"
        for /F "tokens=2 delims==." %%I in ('%SystemRoot%System32wbemwmic.exe OS GET LocalDateTime /VALUE') do set "EndTime=%%I"
        set /A StartTime=(1%StartTime:~8,2% - 100) * 3600 + (1%StartTime:~10,2% - 100) * 60 + 1%StartTime:~12,2% - 100
        set /A EndTime=(1%EndTime:~8,2% - 100) * 3600 + (1%EndTime:~10,2% - 100) * 60 + 1%EndTime:~12,2% - 100
        set /A DiffTime=EndTime - StartTime
        echo Time: %DiffTime% seconds
        endlocal
        goto :EOF

        :ConvertLine
        set "DataLine=%~1"
        set "AllValues="
        set "StartColumn=0"
        :NextValue
        set /A Value=0x!DataLine:~%StartColumn%,4!
        set "AllValues=%AllValues%,%Value%"
        set /A StartColumn+=4
        if not %StartColumn% == 32 goto NextValue
        goto :EOF


        It took 34 seconds to complete the task which was done with larger test batch file in 51 seconds on notebook with Windows XP.



        Then I created a copy of this batch file and inserted between goto :EOF and :ConvertLine a block with 250 lines all with same string:



        rem comment line of no interest


        This batch file with 9193 bytes required 64 seconds to do exactly the same task.



        So searching for a label which is just four lines upwards is definitely the reason for the much longer time of method 1 in comparison to method 2. And method 2 is slower than method 3 and 4 mainly because of the same reason.



        But I have still not found out why the second batch file with 9193 bytes needs 72 seconds instead of 64 seconds on notebook running on battery instead of power supply plugged in. The batch file and the data file are loaded in cache. There is no output on running the batch file. And I have configured in the power options to use maximum performance also on running on battery. Scanning for the label in batch file is obviously slower on running on battery than on power supply plugged in although hard disk is not really accessed during batch file execution, just CPU core, CPU cache and RAM.



        I tried also this batch code with using region dependent TIME environment variable instead of using WMIC command to get a region independent date/time. %TIME% expands on my PCs to German time format HH::MM:SS.ms.



        @echo off
        setlocal EnableExtensions EnableDelayedExpansion
        set "StartTime=%TIME%"
        for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do call :ConvertLine "%%I"
        set "EndTime=%TIME%"
        set /A StartTime=(1%StartTime:~0,2% - 100) * 3600 + (1%StartTime:~3,2% - 100) * 60 + 1%StartTime:~6,2% - 100
        set /A EndTime=(1%EndTime:~0,2% - 100) * 3600 + (1%EndTime:~3,2% - 100) * 60 + 1%EndTime:~6,2% - 100
        set /A DiffTime=EndTime - StartTime
        echo Time: %DiffTime%
        endlocal
        goto :EOF

        :ConvertLine
        set "DataLine=%~1"
        set "AllValues="
        set "StartColumn=0"
        :NextValue
        set /A Value=0x!DataLine:~%StartColumn%,4!
        set "AllValues=%AllValues%,%Value%"
        set /A StartColumn+=4
        if not %StartColumn% == 32 goto NextValue
        goto :EOF


        This batch file finished in 30 seconds on running on power supply plugged in on Windows XP x86 with HDD ST980411ASG with 7200 rpm (magnetic hard disk). The same batch file running next on battery took 37 seconds to finish on same PC.



        It took 72 seconds on PC with Windows 7 x64 with a Samsung SSD 850 EVO (solid state disk) with power supply plugged in and 77 seconds on running on battery. I have just plugged out the power supply between the test runs, nothing else changed. There was no connection to any network, WLAN switched off per hardware switch, bluetooth is disabled in BIOS, anti-virus application disabled during execution (with exception of Windows Defender on Windows 7x 64).



        I run this batch file again and again on PC with Windows 7 x64 and finally with fan spinning on quite often during execution, the execution time became constant with 72 seconds independent on power supply plugged in or not.



        For comparison I executed also this batch file:



        @echo off
        setlocal EnableExtensions EnableDelayedExpansion
        set "StartTime=%TIME%"
        for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do (
        set "DataLine=%%I"
        set "AllValues="
        for /L %%J in (0,4,28) do (
        set /A Value=0x!DataLine:~%%J,4!
        set "AllValues=!AllValues!,!Value!"
        )
        )
        set "EndTime=%TIME%"
        set /A StartTime=(1%StartTime:~0,2% - 100) * 3600 + (1%StartTime:~3,2% - 100) * 60 + 1%StartTime:~6,2% - 100
        set /A EndTime=(1%EndTime:~0,2% - 100) * 3600 + (1%EndTime:~3,2% - 100) * 60 + 1%EndTime:~6,2% - 100
        set /A DiffTime=EndTime - StartTime
        echo Time: %DiffTime%
        endlocal


        It took 1 or 2 seconds on PC running Windows XP to finish whereby on power supply plugged in the finishing time was most often 1 second while on running on battery the finishing time was most often 2 seconds. I would need to take the milliseconds also into account to be more precise on this fast finishing solution. The execution time was between 4 and 5 seconds on PC Windows 7 x64 same trend to lower time on power supply plugged in like on other notebook with Windows XP.



        The hard disk activity light emitting diodes on both computers don't blink different in comparison to not running the batch file. I don't hear any different sound from the HDD on Windows XP while running the batch file taking about 30 seconds to finish and even more on running on battery.



        But I can see with using Process Monitor on both PCs that the batch file itself is permanently opened, read, closed on running the batch file with using GOTO loop while there is nearly no batch file access on using the most optimized version with FOR loop.



        And cmd.exe really reads the batch file line by line again and again with GOTO method as it can be seen also with Process Monitor on large amount of ReadFile accesses with increasing Offset with the offsets being identical to the offsets of beginning of each line in batch file. The execution time increases dramatically on Process Monitor recording file system accesses because of more than two millions of recorded events and more than 500,000 events displayed.



        Further, it can be seen with Process Monitor on using just the optimized FOR loop that cmd.exe reads the lines up to end of FOR loop, then reads once entire HexValues.dat with just one ReadFile access, takes 5 seconds (on Windows 7 x64) to finish the conversion from hexadecimal to decimal without any file system access and next reads the remaining lines of the batch file to finish its execution. Process Monitor records just about 50,000 events with less than 100 events displayed.



        I suppose Intel SpeedStep technology being enabled in BIOS is the reason for increased time of batch file execution with command GOTO with label above current command line on running on battery. That would also explain in my point of view the effect that running the second posted batch in this answer again and again on Windows 7 x64 results finally in a constant execution time independent on power supply plugged in or not because of Intel SpeedStep finally increases the performance of the CPU to maximum even on running on battery because of one core runs permanently on 100%.



        Conclusion:



        The GOTO solution of method 1 is so much slower than all other methods because of cmd.exe does following on reaching goto NextValue according to information given by Aacini and the others analyzed and verified with Process Monitor:




        1. Open the batch file for reading and query standard information about file to check if batch file changed since last access. Querying standard information is not done on the other steps below.

        2. Read one line after the other from batch file with processing each line to find the line with :NextValue with no success to end of file.

        3. Rewind to top of file on reaching end of file.

        4. Read one line after the other from batch file now from top with processing each line to find the line with :NextValue.

        5. Close the batch file on having found the line :NextValue and so knowing offset for next command line to process.

        6. Open the batch file again.

        7. Read the line set /A Value=0x!DataLine:~%StartColumn%,4!.

        8. Close the batch file again.

        9. Process the read command line.

        10. Open the batch file once more.

        11. Read next line set "AllValues=%AllValues%,%Value%" from batch file.

        12. Close the batch file.

        13. Process the read command line.

        14. Open the batch file once more.

        15. Read next line set /A StartColumn+=4 from batch file.

        16. Close the batch file.

        17. Process the read command line.

        18. Open the batch file once more.

        19. Read next line if not %StartColumn% == 32 goto NextValue from batch file.

        20. Close the batch file.

        21. Process the read command line.

        22. Continue on first step if the condition is true, i.e. StartColumn is not 32.


        And all those batch file open / read / close operations take some milliseconds even on not accessing storage media, but most likely (my assumption) on accessing DRAM on motherboard in which the file is loaded because of entire batch file content not loaded into internal cache of CPU.



        So with using a FOR loop in the subroutine as done by method 2 the number of batch file accessing actions is already reduced dramatically because of none of the 21 steps with much more file accessing actions to read the batch file line by line and not explicitly listed all in single steps needs to be done on processing the hexadecimal values in current line read from HexValues.dat.



        And all lines in HexValues.dat can be processed by cmd.exe without any access of the batch file on doing the entire conversion in one FOR loop as done by the methods 3 and 4. And finally some more file system accesses can be saved with outputting all lines of CSV to STDOUT (buffer in memory) and writing them just once into CSV file as done by method 4 reducing once more the total time required for this value conversion task in comparison to method 3.



        It should be avoided having a command line with goto LABEL or call :LABEL with LABEL being above the current line or many lines below on a larger batch file doing something in a loop with hundreds or thousands of iterations. A complex respectively not easy to understand FOR loop for non-experts in batch file coding is in such cases better than a nice readable solution using GOTO or CALL. Or in other words it is always advisable to use just a FOR loop on doing something in a loop really often.






        share|improve this answer





















        • 1





          It is really interesting that the power supply affects the performance even with same settings for battery mode; unfortunately my notebook broke down, so I cannot do any experiments at the moment, but I guess Windows is still behaving differently on battery than on mains power, even if all settings are equal...

          – aschipfl
          Nov 18 '18 at 18:26











        • I think you are wrong with these phrases: "The batch file and the data file are loaded in cache" and "although hard disk is not really accessed during batch file execution". Where did you get these points from? As far as I know, the Batch file execution repeatedly open/read/close the Batch file from disk for each executed line... I also don't agree that a solution using GOTO or CALL be "nice readable solution" when compared vs a complex FOR construct... ;)

          – Aacini
          Nov 18 '18 at 18:36













        • Most disks have an internal cache. So I think it's safe to assume, the batch file (if not too big) is effectively running from either Windows cache or at least Disk cache (as long as there is no other heavy IO).

          – Stephan
          Nov 19 '18 at 8:55











        • @Aacini I am really quite sure that hard disk itself is not accessed during batch file execution. I extended my answer after running more tests and using also Sysinternals Process Monitor. The file accesses are all done on cached file in memory. But it was interesting to see that on running batch file solution with GOTO again and again that after more than 10 executions it did not make anymore a difference on running on power supply or on battery on Windows 7 x64. However, the main batch file is a good performance testing script. I plan to run it also on other PCs with Windows 10 x64.

          – Mofi
          Nov 19 '18 at 10:27











        • IMHO the central point that causes this problem is the open/read/close file method used to run Batch files that is done just once with FOR, but repeated many times with GOTO as I first mentioned in my comment. The fact that a GOTO command searches for the label in downwards order just causes to execute more lines in large files. However, in your extensive explanation of the problem you not even mentioned my comment about this point...

          – Aacini
          Nov 19 '18 at 17:37











        Your Answer






        StackExchange.ifUsing("editor", function () {
        StackExchange.using("externalEditor", function () {
        StackExchange.using("snippets", function () {
        StackExchange.snippets.init();
        });
        });
        }, "code-snippets");

        StackExchange.ready(function() {
        var channelOptions = {
        tags: "".split(" "),
        id: "1"
        };
        initTagRenderer("".split(" "), "".split(" "), channelOptions);

        StackExchange.using("externalEditor", function() {
        // Have to fire editor after snippets, if snippets enabled
        if (StackExchange.settings.snippets.snippetsEnabled) {
        StackExchange.using("snippets", function() {
        createEditor();
        });
        }
        else {
        createEditor();
        }
        });

        function createEditor() {
        StackExchange.prepareEditor({
        heartbeatType: 'answer',
        autoActivateHeartbeat: false,
        convertImagesToLinks: true,
        noModals: true,
        showLowRepImageUploadWarning: true,
        reputationToPostImages: 10,
        bindNavPrevention: true,
        postfix: "",
        imageUploader: {
        brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
        contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
        allowUrls: true
        },
        onDemand: true,
        discardSelector: ".discard-answer"
        ,immediatelyShowMarkdownHelp:true
        });


        }
        });














        draft saved

        draft discarded


















        StackExchange.ready(
        function () {
        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53362979%2fwhy-is-a-goto-loop-much-slower-than-a-for-loop-and-depends-additionally-on-power%23new-answer', 'question_page');
        }
        );

        Post as a guest















        Required, but never shown

























        5 Answers
        5






        active

        oldest

        votes








        5 Answers
        5






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes









        3














        According to this analysis of the interpreter, the FOR variable will be expanded in phase 4, so the interpreter will know how many times to execute the command and on what values immediately. In contrast, each GOTO is interpreted in phase 7, and it requires scanning the file anew looking for the label each time, which accounts for the perceived time difference.






        share|improve this answer






























          3














          According to this analysis of the interpreter, the FOR variable will be expanded in phase 4, so the interpreter will know how many times to execute the command and on what values immediately. In contrast, each GOTO is interpreted in phase 7, and it requires scanning the file anew looking for the label each time, which accounts for the perceived time difference.






          share|improve this answer




























            3












            3








            3







            According to this analysis of the interpreter, the FOR variable will be expanded in phase 4, so the interpreter will know how many times to execute the command and on what values immediately. In contrast, each GOTO is interpreted in phase 7, and it requires scanning the file anew looking for the label each time, which accounts for the perceived time difference.






            share|improve this answer















            According to this analysis of the interpreter, the FOR variable will be expanded in phase 4, so the interpreter will know how many times to execute the command and on what values immediately. In contrast, each GOTO is interpreted in phase 7, and it requires scanning the file anew looking for the label each time, which accounts for the perceived time difference.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 18 '18 at 17:47

























            answered Nov 18 '18 at 16:49









            mnisticmnistic

            7,1731923




            7,1731923

























                4














                If I recall correctly, the GOTO LABEL command in a batch file actually scans the remainder of the text looking for the label, and if it doesn't find it then it restarts at the top.



                That's a pretty expensive operation, and I think CALL does it too.



                So if you care about performance then you should try to avoid these constructs. Or, even better, don't be doing this kind of work in batch files.






                share|improve this answer




























                  4














                  If I recall correctly, the GOTO LABEL command in a batch file actually scans the remainder of the text looking for the label, and if it doesn't find it then it restarts at the top.



                  That's a pretty expensive operation, and I think CALL does it too.



                  So if you care about performance then you should try to avoid these constructs. Or, even better, don't be doing this kind of work in batch files.






                  share|improve this answer


























                    4












                    4








                    4







                    If I recall correctly, the GOTO LABEL command in a batch file actually scans the remainder of the text looking for the label, and if it doesn't find it then it restarts at the top.



                    That's a pretty expensive operation, and I think CALL does it too.



                    So if you care about performance then you should try to avoid these constructs. Or, even better, don't be doing this kind of work in batch files.






                    share|improve this answer













                    If I recall correctly, the GOTO LABEL command in a batch file actually scans the remainder of the text looking for the label, and if it doesn't find it then it restarts at the top.



                    That's a pretty expensive operation, and I think CALL does it too.



                    So if you care about performance then you should try to avoid these constructs. Or, even better, don't be doing this kind of work in batch files.







                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered Nov 18 '18 at 16:40









                    Matt TimmermansMatt Timmermans

                    19.2k11532




                    19.2k11532























                        3














                        It's because of the way goto works. Unlike a compiled programming language, where a goto is translated into a fixed address during the compilation, batch has to search through the file for the label for every goto.



                        It searches the file downwards from the goto line, and if it doesn't find the label, continues the search from the start of the file.






                        share|improve this answer




























                          3














                          It's because of the way goto works. Unlike a compiled programming language, where a goto is translated into a fixed address during the compilation, batch has to search through the file for the label for every goto.



                          It searches the file downwards from the goto line, and if it doesn't find the label, continues the search from the start of the file.






                          share|improve this answer


























                            3












                            3








                            3







                            It's because of the way goto works. Unlike a compiled programming language, where a goto is translated into a fixed address during the compilation, batch has to search through the file for the label for every goto.



                            It searches the file downwards from the goto line, and if it doesn't find the label, continues the search from the start of the file.






                            share|improve this answer













                            It's because of the way goto works. Unlike a compiled programming language, where a goto is translated into a fixed address during the compilation, batch has to search through the file for the label for every goto.



                            It searches the file downwards from the goto line, and if it doesn't find the label, continues the search from the start of the file.







                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered Nov 18 '18 at 16:41









                            StephanStephan

                            35k43255




                            35k43255























                                1














                                As already said, GOTO and CALL searches for next matching the label from the current file position to the end of file, then it searches from the file begin up to the current file position.

                                This behaviour has some useful effects, as you don't have to bother about different label names in functions.



                                :myFunc1
                                <some code>
                                goto :someLabel -- this goto's to the next :someLabel

                                :someLabel

                                :myFunc2
                                <some code>
                                goto :someLabel -- this goto's to the next :someLabel

                                :someLabel


                                But when you build loops, there is a big speed advantage, because the complete FOR parenthesis block will be read only once and parsed up to phase 2.

                                The parsed block is in a cmd cache, therefore there aren't any further disk reads necessary and the tokenization is already done.



                                Related topics
                                Rules for label names vs GOTO and CALL
                                CALL me, or better avoid call






                                share|improve this answer





















                                • 2





                                  I suggested somewhere to use :@F label to define a repeated label that will only be used to jump a few lines forward via GOTO @F. This is with the purpose of be similar to the anonymous label feature of MASM (that is used in the same way), and to give an indication (by convention) that such a label can appear several times and it is not an error...

                                  – Aacini
                                  Nov 20 '18 at 14:01
















                                1














                                As already said, GOTO and CALL searches for next matching the label from the current file position to the end of file, then it searches from the file begin up to the current file position.

                                This behaviour has some useful effects, as you don't have to bother about different label names in functions.



                                :myFunc1
                                <some code>
                                goto :someLabel -- this goto's to the next :someLabel

                                :someLabel

                                :myFunc2
                                <some code>
                                goto :someLabel -- this goto's to the next :someLabel

                                :someLabel


                                But when you build loops, there is a big speed advantage, because the complete FOR parenthesis block will be read only once and parsed up to phase 2.

                                The parsed block is in a cmd cache, therefore there aren't any further disk reads necessary and the tokenization is already done.



                                Related topics
                                Rules for label names vs GOTO and CALL
                                CALL me, or better avoid call






                                share|improve this answer





















                                • 2





                                  I suggested somewhere to use :@F label to define a repeated label that will only be used to jump a few lines forward via GOTO @F. This is with the purpose of be similar to the anonymous label feature of MASM (that is used in the same way), and to give an indication (by convention) that such a label can appear several times and it is not an error...

                                  – Aacini
                                  Nov 20 '18 at 14:01














                                1












                                1








                                1







                                As already said, GOTO and CALL searches for next matching the label from the current file position to the end of file, then it searches from the file begin up to the current file position.

                                This behaviour has some useful effects, as you don't have to bother about different label names in functions.



                                :myFunc1
                                <some code>
                                goto :someLabel -- this goto's to the next :someLabel

                                :someLabel

                                :myFunc2
                                <some code>
                                goto :someLabel -- this goto's to the next :someLabel

                                :someLabel


                                But when you build loops, there is a big speed advantage, because the complete FOR parenthesis block will be read only once and parsed up to phase 2.

                                The parsed block is in a cmd cache, therefore there aren't any further disk reads necessary and the tokenization is already done.



                                Related topics
                                Rules for label names vs GOTO and CALL
                                CALL me, or better avoid call






                                share|improve this answer















                                As already said, GOTO and CALL searches for next matching the label from the current file position to the end of file, then it searches from the file begin up to the current file position.

                                This behaviour has some useful effects, as you don't have to bother about different label names in functions.



                                :myFunc1
                                <some code>
                                goto :someLabel -- this goto's to the next :someLabel

                                :someLabel

                                :myFunc2
                                <some code>
                                goto :someLabel -- this goto's to the next :someLabel

                                :someLabel


                                But when you build loops, there is a big speed advantage, because the complete FOR parenthesis block will be read only once and parsed up to phase 2.

                                The parsed block is in a cmd cache, therefore there aren't any further disk reads necessary and the tokenization is already done.



                                Related topics
                                Rules for label names vs GOTO and CALL
                                CALL me, or better avoid call







                                share|improve this answer














                                share|improve this answer



                                share|improve this answer








                                edited Nov 19 '18 at 7:54

























                                answered Nov 19 '18 at 7:45









                                jebjeb

                                57.7k13129167




                                57.7k13129167








                                • 2





                                  I suggested somewhere to use :@F label to define a repeated label that will only be used to jump a few lines forward via GOTO @F. This is with the purpose of be similar to the anonymous label feature of MASM (that is used in the same way), and to give an indication (by convention) that such a label can appear several times and it is not an error...

                                  – Aacini
                                  Nov 20 '18 at 14:01














                                • 2





                                  I suggested somewhere to use :@F label to define a repeated label that will only be used to jump a few lines forward via GOTO @F. This is with the purpose of be similar to the anonymous label feature of MASM (that is used in the same way), and to give an indication (by convention) that such a label can appear several times and it is not an error...

                                  – Aacini
                                  Nov 20 '18 at 14:01








                                2




                                2





                                I suggested somewhere to use :@F label to define a repeated label that will only be used to jump a few lines forward via GOTO @F. This is with the purpose of be similar to the anonymous label feature of MASM (that is used in the same way), and to give an indication (by convention) that such a label can appear several times and it is not an error...

                                – Aacini
                                Nov 20 '18 at 14:01





                                I suggested somewhere to use :@F label to define a repeated label that will only be used to jump a few lines forward via GOTO @F. This is with the purpose of be similar to the anonymous label feature of MASM (that is used in the same way), and to give an indication (by convention) that such a label can appear several times and it is not an error...

                                – Aacini
                                Nov 20 '18 at 14:01











                                0














                                Thanks for the answers. It looks like you are all right.



                                The reason is searching for label referenced by GOTO first downwards in batch file and next from top on not finding it to bottom of file.



                                I verified that with using first a batch file with following lines and a file size of 941 bytes:



                                @echo off
                                setlocal EnableExtensions EnableDelayedExpansion
                                for /F "tokens=2 delims==." %%I in ('%SystemRoot%System32wbemwmic.exe OS GET LocalDateTime /VALUE') do set "StartTime=%%I"
                                for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do call :ConvertLine "%%I"
                                for /F "tokens=2 delims==." %%I in ('%SystemRoot%System32wbemwmic.exe OS GET LocalDateTime /VALUE') do set "EndTime=%%I"
                                set /A StartTime=(1%StartTime:~8,2% - 100) * 3600 + (1%StartTime:~10,2% - 100) * 60 + 1%StartTime:~12,2% - 100
                                set /A EndTime=(1%EndTime:~8,2% - 100) * 3600 + (1%EndTime:~10,2% - 100) * 60 + 1%EndTime:~12,2% - 100
                                set /A DiffTime=EndTime - StartTime
                                echo Time: %DiffTime% seconds
                                endlocal
                                goto :EOF

                                :ConvertLine
                                set "DataLine=%~1"
                                set "AllValues="
                                set "StartColumn=0"
                                :NextValue
                                set /A Value=0x!DataLine:~%StartColumn%,4!
                                set "AllValues=%AllValues%,%Value%"
                                set /A StartColumn+=4
                                if not %StartColumn% == 32 goto NextValue
                                goto :EOF


                                It took 34 seconds to complete the task which was done with larger test batch file in 51 seconds on notebook with Windows XP.



                                Then I created a copy of this batch file and inserted between goto :EOF and :ConvertLine a block with 250 lines all with same string:



                                rem comment line of no interest


                                This batch file with 9193 bytes required 64 seconds to do exactly the same task.



                                So searching for a label which is just four lines upwards is definitely the reason for the much longer time of method 1 in comparison to method 2. And method 2 is slower than method 3 and 4 mainly because of the same reason.



                                But I have still not found out why the second batch file with 9193 bytes needs 72 seconds instead of 64 seconds on notebook running on battery instead of power supply plugged in. The batch file and the data file are loaded in cache. There is no output on running the batch file. And I have configured in the power options to use maximum performance also on running on battery. Scanning for the label in batch file is obviously slower on running on battery than on power supply plugged in although hard disk is not really accessed during batch file execution, just CPU core, CPU cache and RAM.



                                I tried also this batch code with using region dependent TIME environment variable instead of using WMIC command to get a region independent date/time. %TIME% expands on my PCs to German time format HH::MM:SS.ms.



                                @echo off
                                setlocal EnableExtensions EnableDelayedExpansion
                                set "StartTime=%TIME%"
                                for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do call :ConvertLine "%%I"
                                set "EndTime=%TIME%"
                                set /A StartTime=(1%StartTime:~0,2% - 100) * 3600 + (1%StartTime:~3,2% - 100) * 60 + 1%StartTime:~6,2% - 100
                                set /A EndTime=(1%EndTime:~0,2% - 100) * 3600 + (1%EndTime:~3,2% - 100) * 60 + 1%EndTime:~6,2% - 100
                                set /A DiffTime=EndTime - StartTime
                                echo Time: %DiffTime%
                                endlocal
                                goto :EOF

                                :ConvertLine
                                set "DataLine=%~1"
                                set "AllValues="
                                set "StartColumn=0"
                                :NextValue
                                set /A Value=0x!DataLine:~%StartColumn%,4!
                                set "AllValues=%AllValues%,%Value%"
                                set /A StartColumn+=4
                                if not %StartColumn% == 32 goto NextValue
                                goto :EOF


                                This batch file finished in 30 seconds on running on power supply plugged in on Windows XP x86 with HDD ST980411ASG with 7200 rpm (magnetic hard disk). The same batch file running next on battery took 37 seconds to finish on same PC.



                                It took 72 seconds on PC with Windows 7 x64 with a Samsung SSD 850 EVO (solid state disk) with power supply plugged in and 77 seconds on running on battery. I have just plugged out the power supply between the test runs, nothing else changed. There was no connection to any network, WLAN switched off per hardware switch, bluetooth is disabled in BIOS, anti-virus application disabled during execution (with exception of Windows Defender on Windows 7x 64).



                                I run this batch file again and again on PC with Windows 7 x64 and finally with fan spinning on quite often during execution, the execution time became constant with 72 seconds independent on power supply plugged in or not.



                                For comparison I executed also this batch file:



                                @echo off
                                setlocal EnableExtensions EnableDelayedExpansion
                                set "StartTime=%TIME%"
                                for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do (
                                set "DataLine=%%I"
                                set "AllValues="
                                for /L %%J in (0,4,28) do (
                                set /A Value=0x!DataLine:~%%J,4!
                                set "AllValues=!AllValues!,!Value!"
                                )
                                )
                                set "EndTime=%TIME%"
                                set /A StartTime=(1%StartTime:~0,2% - 100) * 3600 + (1%StartTime:~3,2% - 100) * 60 + 1%StartTime:~6,2% - 100
                                set /A EndTime=(1%EndTime:~0,2% - 100) * 3600 + (1%EndTime:~3,2% - 100) * 60 + 1%EndTime:~6,2% - 100
                                set /A DiffTime=EndTime - StartTime
                                echo Time: %DiffTime%
                                endlocal


                                It took 1 or 2 seconds on PC running Windows XP to finish whereby on power supply plugged in the finishing time was most often 1 second while on running on battery the finishing time was most often 2 seconds. I would need to take the milliseconds also into account to be more precise on this fast finishing solution. The execution time was between 4 and 5 seconds on PC Windows 7 x64 same trend to lower time on power supply plugged in like on other notebook with Windows XP.



                                The hard disk activity light emitting diodes on both computers don't blink different in comparison to not running the batch file. I don't hear any different sound from the HDD on Windows XP while running the batch file taking about 30 seconds to finish and even more on running on battery.



                                But I can see with using Process Monitor on both PCs that the batch file itself is permanently opened, read, closed on running the batch file with using GOTO loop while there is nearly no batch file access on using the most optimized version with FOR loop.



                                And cmd.exe really reads the batch file line by line again and again with GOTO method as it can be seen also with Process Monitor on large amount of ReadFile accesses with increasing Offset with the offsets being identical to the offsets of beginning of each line in batch file. The execution time increases dramatically on Process Monitor recording file system accesses because of more than two millions of recorded events and more than 500,000 events displayed.



                                Further, it can be seen with Process Monitor on using just the optimized FOR loop that cmd.exe reads the lines up to end of FOR loop, then reads once entire HexValues.dat with just one ReadFile access, takes 5 seconds (on Windows 7 x64) to finish the conversion from hexadecimal to decimal without any file system access and next reads the remaining lines of the batch file to finish its execution. Process Monitor records just about 50,000 events with less than 100 events displayed.



                                I suppose Intel SpeedStep technology being enabled in BIOS is the reason for increased time of batch file execution with command GOTO with label above current command line on running on battery. That would also explain in my point of view the effect that running the second posted batch in this answer again and again on Windows 7 x64 results finally in a constant execution time independent on power supply plugged in or not because of Intel SpeedStep finally increases the performance of the CPU to maximum even on running on battery because of one core runs permanently on 100%.



                                Conclusion:



                                The GOTO solution of method 1 is so much slower than all other methods because of cmd.exe does following on reaching goto NextValue according to information given by Aacini and the others analyzed and verified with Process Monitor:




                                1. Open the batch file for reading and query standard information about file to check if batch file changed since last access. Querying standard information is not done on the other steps below.

                                2. Read one line after the other from batch file with processing each line to find the line with :NextValue with no success to end of file.

                                3. Rewind to top of file on reaching end of file.

                                4. Read one line after the other from batch file now from top with processing each line to find the line with :NextValue.

                                5. Close the batch file on having found the line :NextValue and so knowing offset for next command line to process.

                                6. Open the batch file again.

                                7. Read the line set /A Value=0x!DataLine:~%StartColumn%,4!.

                                8. Close the batch file again.

                                9. Process the read command line.

                                10. Open the batch file once more.

                                11. Read next line set "AllValues=%AllValues%,%Value%" from batch file.

                                12. Close the batch file.

                                13. Process the read command line.

                                14. Open the batch file once more.

                                15. Read next line set /A StartColumn+=4 from batch file.

                                16. Close the batch file.

                                17. Process the read command line.

                                18. Open the batch file once more.

                                19. Read next line if not %StartColumn% == 32 goto NextValue from batch file.

                                20. Close the batch file.

                                21. Process the read command line.

                                22. Continue on first step if the condition is true, i.e. StartColumn is not 32.


                                And all those batch file open / read / close operations take some milliseconds even on not accessing storage media, but most likely (my assumption) on accessing DRAM on motherboard in which the file is loaded because of entire batch file content not loaded into internal cache of CPU.



                                So with using a FOR loop in the subroutine as done by method 2 the number of batch file accessing actions is already reduced dramatically because of none of the 21 steps with much more file accessing actions to read the batch file line by line and not explicitly listed all in single steps needs to be done on processing the hexadecimal values in current line read from HexValues.dat.



                                And all lines in HexValues.dat can be processed by cmd.exe without any access of the batch file on doing the entire conversion in one FOR loop as done by the methods 3 and 4. And finally some more file system accesses can be saved with outputting all lines of CSV to STDOUT (buffer in memory) and writing them just once into CSV file as done by method 4 reducing once more the total time required for this value conversion task in comparison to method 3.



                                It should be avoided having a command line with goto LABEL or call :LABEL with LABEL being above the current line or many lines below on a larger batch file doing something in a loop with hundreds or thousands of iterations. A complex respectively not easy to understand FOR loop for non-experts in batch file coding is in such cases better than a nice readable solution using GOTO or CALL. Or in other words it is always advisable to use just a FOR loop on doing something in a loop really often.






                                share|improve this answer





















                                • 1





                                  It is really interesting that the power supply affects the performance even with same settings for battery mode; unfortunately my notebook broke down, so I cannot do any experiments at the moment, but I guess Windows is still behaving differently on battery than on mains power, even if all settings are equal...

                                  – aschipfl
                                  Nov 18 '18 at 18:26











                                • I think you are wrong with these phrases: "The batch file and the data file are loaded in cache" and "although hard disk is not really accessed during batch file execution". Where did you get these points from? As far as I know, the Batch file execution repeatedly open/read/close the Batch file from disk for each executed line... I also don't agree that a solution using GOTO or CALL be "nice readable solution" when compared vs a complex FOR construct... ;)

                                  – Aacini
                                  Nov 18 '18 at 18:36













                                • Most disks have an internal cache. So I think it's safe to assume, the batch file (if not too big) is effectively running from either Windows cache or at least Disk cache (as long as there is no other heavy IO).

                                  – Stephan
                                  Nov 19 '18 at 8:55











                                • @Aacini I am really quite sure that hard disk itself is not accessed during batch file execution. I extended my answer after running more tests and using also Sysinternals Process Monitor. The file accesses are all done on cached file in memory. But it was interesting to see that on running batch file solution with GOTO again and again that after more than 10 executions it did not make anymore a difference on running on power supply or on battery on Windows 7 x64. However, the main batch file is a good performance testing script. I plan to run it also on other PCs with Windows 10 x64.

                                  – Mofi
                                  Nov 19 '18 at 10:27











                                • IMHO the central point that causes this problem is the open/read/close file method used to run Batch files that is done just once with FOR, but repeated many times with GOTO as I first mentioned in my comment. The fact that a GOTO command searches for the label in downwards order just causes to execute more lines in large files. However, in your extensive explanation of the problem you not even mentioned my comment about this point...

                                  – Aacini
                                  Nov 19 '18 at 17:37
















                                0














                                Thanks for the answers. It looks like you are all right.



                                The reason is searching for label referenced by GOTO first downwards in batch file and next from top on not finding it to bottom of file.



                                I verified that with using first a batch file with following lines and a file size of 941 bytes:



                                @echo off
                                setlocal EnableExtensions EnableDelayedExpansion
                                for /F "tokens=2 delims==." %%I in ('%SystemRoot%System32wbemwmic.exe OS GET LocalDateTime /VALUE') do set "StartTime=%%I"
                                for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do call :ConvertLine "%%I"
                                for /F "tokens=2 delims==." %%I in ('%SystemRoot%System32wbemwmic.exe OS GET LocalDateTime /VALUE') do set "EndTime=%%I"
                                set /A StartTime=(1%StartTime:~8,2% - 100) * 3600 + (1%StartTime:~10,2% - 100) * 60 + 1%StartTime:~12,2% - 100
                                set /A EndTime=(1%EndTime:~8,2% - 100) * 3600 + (1%EndTime:~10,2% - 100) * 60 + 1%EndTime:~12,2% - 100
                                set /A DiffTime=EndTime - StartTime
                                echo Time: %DiffTime% seconds
                                endlocal
                                goto :EOF

                                :ConvertLine
                                set "DataLine=%~1"
                                set "AllValues="
                                set "StartColumn=0"
                                :NextValue
                                set /A Value=0x!DataLine:~%StartColumn%,4!
                                set "AllValues=%AllValues%,%Value%"
                                set /A StartColumn+=4
                                if not %StartColumn% == 32 goto NextValue
                                goto :EOF


                                It took 34 seconds to complete the task which was done with larger test batch file in 51 seconds on notebook with Windows XP.



                                Then I created a copy of this batch file and inserted between goto :EOF and :ConvertLine a block with 250 lines all with same string:



                                rem comment line of no interest


                                This batch file with 9193 bytes required 64 seconds to do exactly the same task.



                                So searching for a label which is just four lines upwards is definitely the reason for the much longer time of method 1 in comparison to method 2. And method 2 is slower than method 3 and 4 mainly because of the same reason.



                                But I have still not found out why the second batch file with 9193 bytes needs 72 seconds instead of 64 seconds on notebook running on battery instead of power supply plugged in. The batch file and the data file are loaded in cache. There is no output on running the batch file. And I have configured in the power options to use maximum performance also on running on battery. Scanning for the label in batch file is obviously slower on running on battery than on power supply plugged in although hard disk is not really accessed during batch file execution, just CPU core, CPU cache and RAM.



                                I tried also this batch code with using region dependent TIME environment variable instead of using WMIC command to get a region independent date/time. %TIME% expands on my PCs to German time format HH::MM:SS.ms.



                                @echo off
                                setlocal EnableExtensions EnableDelayedExpansion
                                set "StartTime=%TIME%"
                                for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do call :ConvertLine "%%I"
                                set "EndTime=%TIME%"
                                set /A StartTime=(1%StartTime:~0,2% - 100) * 3600 + (1%StartTime:~3,2% - 100) * 60 + 1%StartTime:~6,2% - 100
                                set /A EndTime=(1%EndTime:~0,2% - 100) * 3600 + (1%EndTime:~3,2% - 100) * 60 + 1%EndTime:~6,2% - 100
                                set /A DiffTime=EndTime - StartTime
                                echo Time: %DiffTime%
                                endlocal
                                goto :EOF

                                :ConvertLine
                                set "DataLine=%~1"
                                set "AllValues="
                                set "StartColumn=0"
                                :NextValue
                                set /A Value=0x!DataLine:~%StartColumn%,4!
                                set "AllValues=%AllValues%,%Value%"
                                set /A StartColumn+=4
                                if not %StartColumn% == 32 goto NextValue
                                goto :EOF


                                This batch file finished in 30 seconds on running on power supply plugged in on Windows XP x86 with HDD ST980411ASG with 7200 rpm (magnetic hard disk). The same batch file running next on battery took 37 seconds to finish on same PC.



                                It took 72 seconds on PC with Windows 7 x64 with a Samsung SSD 850 EVO (solid state disk) with power supply plugged in and 77 seconds on running on battery. I have just plugged out the power supply between the test runs, nothing else changed. There was no connection to any network, WLAN switched off per hardware switch, bluetooth is disabled in BIOS, anti-virus application disabled during execution (with exception of Windows Defender on Windows 7x 64).



                                I run this batch file again and again on PC with Windows 7 x64 and finally with fan spinning on quite often during execution, the execution time became constant with 72 seconds independent on power supply plugged in or not.



                                For comparison I executed also this batch file:



                                @echo off
                                setlocal EnableExtensions EnableDelayedExpansion
                                set "StartTime=%TIME%"
                                for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do (
                                set "DataLine=%%I"
                                set "AllValues="
                                for /L %%J in (0,4,28) do (
                                set /A Value=0x!DataLine:~%%J,4!
                                set "AllValues=!AllValues!,!Value!"
                                )
                                )
                                set "EndTime=%TIME%"
                                set /A StartTime=(1%StartTime:~0,2% - 100) * 3600 + (1%StartTime:~3,2% - 100) * 60 + 1%StartTime:~6,2% - 100
                                set /A EndTime=(1%EndTime:~0,2% - 100) * 3600 + (1%EndTime:~3,2% - 100) * 60 + 1%EndTime:~6,2% - 100
                                set /A DiffTime=EndTime - StartTime
                                echo Time: %DiffTime%
                                endlocal


                                It took 1 or 2 seconds on PC running Windows XP to finish whereby on power supply plugged in the finishing time was most often 1 second while on running on battery the finishing time was most often 2 seconds. I would need to take the milliseconds also into account to be more precise on this fast finishing solution. The execution time was between 4 and 5 seconds on PC Windows 7 x64 same trend to lower time on power supply plugged in like on other notebook with Windows XP.



                                The hard disk activity light emitting diodes on both computers don't blink different in comparison to not running the batch file. I don't hear any different sound from the HDD on Windows XP while running the batch file taking about 30 seconds to finish and even more on running on battery.



                                But I can see with using Process Monitor on both PCs that the batch file itself is permanently opened, read, closed on running the batch file with using GOTO loop while there is nearly no batch file access on using the most optimized version with FOR loop.



                                And cmd.exe really reads the batch file line by line again and again with GOTO method as it can be seen also with Process Monitor on large amount of ReadFile accesses with increasing Offset with the offsets being identical to the offsets of beginning of each line in batch file. The execution time increases dramatically on Process Monitor recording file system accesses because of more than two millions of recorded events and more than 500,000 events displayed.



                                Further, it can be seen with Process Monitor on using just the optimized FOR loop that cmd.exe reads the lines up to end of FOR loop, then reads once entire HexValues.dat with just one ReadFile access, takes 5 seconds (on Windows 7 x64) to finish the conversion from hexadecimal to decimal without any file system access and next reads the remaining lines of the batch file to finish its execution. Process Monitor records just about 50,000 events with less than 100 events displayed.



                                I suppose Intel SpeedStep technology being enabled in BIOS is the reason for increased time of batch file execution with command GOTO with label above current command line on running on battery. That would also explain in my point of view the effect that running the second posted batch in this answer again and again on Windows 7 x64 results finally in a constant execution time independent on power supply plugged in or not because of Intel SpeedStep finally increases the performance of the CPU to maximum even on running on battery because of one core runs permanently on 100%.



                                Conclusion:



                                The GOTO solution of method 1 is so much slower than all other methods because of cmd.exe does following on reaching goto NextValue according to information given by Aacini and the others analyzed and verified with Process Monitor:




                                1. Open the batch file for reading and query standard information about file to check if batch file changed since last access. Querying standard information is not done on the other steps below.

                                2. Read one line after the other from batch file with processing each line to find the line with :NextValue with no success to end of file.

                                3. Rewind to top of file on reaching end of file.

                                4. Read one line after the other from batch file now from top with processing each line to find the line with :NextValue.

                                5. Close the batch file on having found the line :NextValue and so knowing offset for next command line to process.

                                6. Open the batch file again.

                                7. Read the line set /A Value=0x!DataLine:~%StartColumn%,4!.

                                8. Close the batch file again.

                                9. Process the read command line.

                                10. Open the batch file once more.

                                11. Read next line set "AllValues=%AllValues%,%Value%" from batch file.

                                12. Close the batch file.

                                13. Process the read command line.

                                14. Open the batch file once more.

                                15. Read next line set /A StartColumn+=4 from batch file.

                                16. Close the batch file.

                                17. Process the read command line.

                                18. Open the batch file once more.

                                19. Read next line if not %StartColumn% == 32 goto NextValue from batch file.

                                20. Close the batch file.

                                21. Process the read command line.

                                22. Continue on first step if the condition is true, i.e. StartColumn is not 32.


                                And all those batch file open / read / close operations take some milliseconds even on not accessing storage media, but most likely (my assumption) on accessing DRAM on motherboard in which the file is loaded because of entire batch file content not loaded into internal cache of CPU.



                                So with using a FOR loop in the subroutine as done by method 2 the number of batch file accessing actions is already reduced dramatically because of none of the 21 steps with much more file accessing actions to read the batch file line by line and not explicitly listed all in single steps needs to be done on processing the hexadecimal values in current line read from HexValues.dat.



                                And all lines in HexValues.dat can be processed by cmd.exe without any access of the batch file on doing the entire conversion in one FOR loop as done by the methods 3 and 4. And finally some more file system accesses can be saved with outputting all lines of CSV to STDOUT (buffer in memory) and writing them just once into CSV file as done by method 4 reducing once more the total time required for this value conversion task in comparison to method 3.



                                It should be avoided having a command line with goto LABEL or call :LABEL with LABEL being above the current line or many lines below on a larger batch file doing something in a loop with hundreds or thousands of iterations. A complex respectively not easy to understand FOR loop for non-experts in batch file coding is in such cases better than a nice readable solution using GOTO or CALL. Or in other words it is always advisable to use just a FOR loop on doing something in a loop really often.






                                share|improve this answer





















                                • 1





                                  It is really interesting that the power supply affects the performance even with same settings for battery mode; unfortunately my notebook broke down, so I cannot do any experiments at the moment, but I guess Windows is still behaving differently on battery than on mains power, even if all settings are equal...

                                  – aschipfl
                                  Nov 18 '18 at 18:26











                                • I think you are wrong with these phrases: "The batch file and the data file are loaded in cache" and "although hard disk is not really accessed during batch file execution". Where did you get these points from? As far as I know, the Batch file execution repeatedly open/read/close the Batch file from disk for each executed line... I also don't agree that a solution using GOTO or CALL be "nice readable solution" when compared vs a complex FOR construct... ;)

                                  – Aacini
                                  Nov 18 '18 at 18:36













                                • Most disks have an internal cache. So I think it's safe to assume, the batch file (if not too big) is effectively running from either Windows cache or at least Disk cache (as long as there is no other heavy IO).

                                  – Stephan
                                  Nov 19 '18 at 8:55











                                • @Aacini I am really quite sure that hard disk itself is not accessed during batch file execution. I extended my answer after running more tests and using also Sysinternals Process Monitor. The file accesses are all done on cached file in memory. But it was interesting to see that on running batch file solution with GOTO again and again that after more than 10 executions it did not make anymore a difference on running on power supply or on battery on Windows 7 x64. However, the main batch file is a good performance testing script. I plan to run it also on other PCs with Windows 10 x64.

                                  – Mofi
                                  Nov 19 '18 at 10:27











                                • IMHO the central point that causes this problem is the open/read/close file method used to run Batch files that is done just once with FOR, but repeated many times with GOTO as I first mentioned in my comment. The fact that a GOTO command searches for the label in downwards order just causes to execute more lines in large files. However, in your extensive explanation of the problem you not even mentioned my comment about this point...

                                  – Aacini
                                  Nov 19 '18 at 17:37














                                0












                                0








                                0







                                Thanks for the answers. It looks like you are all right.



                                The reason is searching for label referenced by GOTO first downwards in batch file and next from top on not finding it to bottom of file.



                                I verified that with using first a batch file with following lines and a file size of 941 bytes:



                                @echo off
                                setlocal EnableExtensions EnableDelayedExpansion
                                for /F "tokens=2 delims==." %%I in ('%SystemRoot%System32wbemwmic.exe OS GET LocalDateTime /VALUE') do set "StartTime=%%I"
                                for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do call :ConvertLine "%%I"
                                for /F "tokens=2 delims==." %%I in ('%SystemRoot%System32wbemwmic.exe OS GET LocalDateTime /VALUE') do set "EndTime=%%I"
                                set /A StartTime=(1%StartTime:~8,2% - 100) * 3600 + (1%StartTime:~10,2% - 100) * 60 + 1%StartTime:~12,2% - 100
                                set /A EndTime=(1%EndTime:~8,2% - 100) * 3600 + (1%EndTime:~10,2% - 100) * 60 + 1%EndTime:~12,2% - 100
                                set /A DiffTime=EndTime - StartTime
                                echo Time: %DiffTime% seconds
                                endlocal
                                goto :EOF

                                :ConvertLine
                                set "DataLine=%~1"
                                set "AllValues="
                                set "StartColumn=0"
                                :NextValue
                                set /A Value=0x!DataLine:~%StartColumn%,4!
                                set "AllValues=%AllValues%,%Value%"
                                set /A StartColumn+=4
                                if not %StartColumn% == 32 goto NextValue
                                goto :EOF


                                It took 34 seconds to complete the task which was done with larger test batch file in 51 seconds on notebook with Windows XP.



                                Then I created a copy of this batch file and inserted between goto :EOF and :ConvertLine a block with 250 lines all with same string:



                                rem comment line of no interest


                                This batch file with 9193 bytes required 64 seconds to do exactly the same task.



                                So searching for a label which is just four lines upwards is definitely the reason for the much longer time of method 1 in comparison to method 2. And method 2 is slower than method 3 and 4 mainly because of the same reason.



                                But I have still not found out why the second batch file with 9193 bytes needs 72 seconds instead of 64 seconds on notebook running on battery instead of power supply plugged in. The batch file and the data file are loaded in cache. There is no output on running the batch file. And I have configured in the power options to use maximum performance also on running on battery. Scanning for the label in batch file is obviously slower on running on battery than on power supply plugged in although hard disk is not really accessed during batch file execution, just CPU core, CPU cache and RAM.



                                I tried also this batch code with using region dependent TIME environment variable instead of using WMIC command to get a region independent date/time. %TIME% expands on my PCs to German time format HH::MM:SS.ms.



                                @echo off
                                setlocal EnableExtensions EnableDelayedExpansion
                                set "StartTime=%TIME%"
                                for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do call :ConvertLine "%%I"
                                set "EndTime=%TIME%"
                                set /A StartTime=(1%StartTime:~0,2% - 100) * 3600 + (1%StartTime:~3,2% - 100) * 60 + 1%StartTime:~6,2% - 100
                                set /A EndTime=(1%EndTime:~0,2% - 100) * 3600 + (1%EndTime:~3,2% - 100) * 60 + 1%EndTime:~6,2% - 100
                                set /A DiffTime=EndTime - StartTime
                                echo Time: %DiffTime%
                                endlocal
                                goto :EOF

                                :ConvertLine
                                set "DataLine=%~1"
                                set "AllValues="
                                set "StartColumn=0"
                                :NextValue
                                set /A Value=0x!DataLine:~%StartColumn%,4!
                                set "AllValues=%AllValues%,%Value%"
                                set /A StartColumn+=4
                                if not %StartColumn% == 32 goto NextValue
                                goto :EOF


                                This batch file finished in 30 seconds on running on power supply plugged in on Windows XP x86 with HDD ST980411ASG with 7200 rpm (magnetic hard disk). The same batch file running next on battery took 37 seconds to finish on same PC.



                                It took 72 seconds on PC with Windows 7 x64 with a Samsung SSD 850 EVO (solid state disk) with power supply plugged in and 77 seconds on running on battery. I have just plugged out the power supply between the test runs, nothing else changed. There was no connection to any network, WLAN switched off per hardware switch, bluetooth is disabled in BIOS, anti-virus application disabled during execution (with exception of Windows Defender on Windows 7x 64).



                                I run this batch file again and again on PC with Windows 7 x64 and finally with fan spinning on quite often during execution, the execution time became constant with 72 seconds independent on power supply plugged in or not.



                                For comparison I executed also this batch file:



                                @echo off
                                setlocal EnableExtensions EnableDelayedExpansion
                                set "StartTime=%TIME%"
                                for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do (
                                set "DataLine=%%I"
                                set "AllValues="
                                for /L %%J in (0,4,28) do (
                                set /A Value=0x!DataLine:~%%J,4!
                                set "AllValues=!AllValues!,!Value!"
                                )
                                )
                                set "EndTime=%TIME%"
                                set /A StartTime=(1%StartTime:~0,2% - 100) * 3600 + (1%StartTime:~3,2% - 100) * 60 + 1%StartTime:~6,2% - 100
                                set /A EndTime=(1%EndTime:~0,2% - 100) * 3600 + (1%EndTime:~3,2% - 100) * 60 + 1%EndTime:~6,2% - 100
                                set /A DiffTime=EndTime - StartTime
                                echo Time: %DiffTime%
                                endlocal


                                It took 1 or 2 seconds on PC running Windows XP to finish whereby on power supply plugged in the finishing time was most often 1 second while on running on battery the finishing time was most often 2 seconds. I would need to take the milliseconds also into account to be more precise on this fast finishing solution. The execution time was between 4 and 5 seconds on PC Windows 7 x64 same trend to lower time on power supply plugged in like on other notebook with Windows XP.



                                The hard disk activity light emitting diodes on both computers don't blink different in comparison to not running the batch file. I don't hear any different sound from the HDD on Windows XP while running the batch file taking about 30 seconds to finish and even more on running on battery.



                                But I can see with using Process Monitor on both PCs that the batch file itself is permanently opened, read, closed on running the batch file with using GOTO loop while there is nearly no batch file access on using the most optimized version with FOR loop.



                                And cmd.exe really reads the batch file line by line again and again with GOTO method as it can be seen also with Process Monitor on large amount of ReadFile accesses with increasing Offset with the offsets being identical to the offsets of beginning of each line in batch file. The execution time increases dramatically on Process Monitor recording file system accesses because of more than two millions of recorded events and more than 500,000 events displayed.



                                Further, it can be seen with Process Monitor on using just the optimized FOR loop that cmd.exe reads the lines up to end of FOR loop, then reads once entire HexValues.dat with just one ReadFile access, takes 5 seconds (on Windows 7 x64) to finish the conversion from hexadecimal to decimal without any file system access and next reads the remaining lines of the batch file to finish its execution. Process Monitor records just about 50,000 events with less than 100 events displayed.



                                I suppose Intel SpeedStep technology being enabled in BIOS is the reason for increased time of batch file execution with command GOTO with label above current command line on running on battery. That would also explain in my point of view the effect that running the second posted batch in this answer again and again on Windows 7 x64 results finally in a constant execution time independent on power supply plugged in or not because of Intel SpeedStep finally increases the performance of the CPU to maximum even on running on battery because of one core runs permanently on 100%.



                                Conclusion:



                                The GOTO solution of method 1 is so much slower than all other methods because of cmd.exe does following on reaching goto NextValue according to information given by Aacini and the others analyzed and verified with Process Monitor:




                                1. Open the batch file for reading and query standard information about file to check if batch file changed since last access. Querying standard information is not done on the other steps below.

                                2. Read one line after the other from batch file with processing each line to find the line with :NextValue with no success to end of file.

                                3. Rewind to top of file on reaching end of file.

                                4. Read one line after the other from batch file now from top with processing each line to find the line with :NextValue.

                                5. Close the batch file on having found the line :NextValue and so knowing offset for next command line to process.

                                6. Open the batch file again.

                                7. Read the line set /A Value=0x!DataLine:~%StartColumn%,4!.

                                8. Close the batch file again.

                                9. Process the read command line.

                                10. Open the batch file once more.

                                11. Read next line set "AllValues=%AllValues%,%Value%" from batch file.

                                12. Close the batch file.

                                13. Process the read command line.

                                14. Open the batch file once more.

                                15. Read next line set /A StartColumn+=4 from batch file.

                                16. Close the batch file.

                                17. Process the read command line.

                                18. Open the batch file once more.

                                19. Read next line if not %StartColumn% == 32 goto NextValue from batch file.

                                20. Close the batch file.

                                21. Process the read command line.

                                22. Continue on first step if the condition is true, i.e. StartColumn is not 32.


                                And all those batch file open / read / close operations take some milliseconds even on not accessing storage media, but most likely (my assumption) on accessing DRAM on motherboard in which the file is loaded because of entire batch file content not loaded into internal cache of CPU.



                                So with using a FOR loop in the subroutine as done by method 2 the number of batch file accessing actions is already reduced dramatically because of none of the 21 steps with much more file accessing actions to read the batch file line by line and not explicitly listed all in single steps needs to be done on processing the hexadecimal values in current line read from HexValues.dat.



                                And all lines in HexValues.dat can be processed by cmd.exe without any access of the batch file on doing the entire conversion in one FOR loop as done by the methods 3 and 4. And finally some more file system accesses can be saved with outputting all lines of CSV to STDOUT (buffer in memory) and writing them just once into CSV file as done by method 4 reducing once more the total time required for this value conversion task in comparison to method 3.



                                It should be avoided having a command line with goto LABEL or call :LABEL with LABEL being above the current line or many lines below on a larger batch file doing something in a loop with hundreds or thousands of iterations. A complex respectively not easy to understand FOR loop for non-experts in batch file coding is in such cases better than a nice readable solution using GOTO or CALL. Or in other words it is always advisable to use just a FOR loop on doing something in a loop really often.






                                share|improve this answer















                                Thanks for the answers. It looks like you are all right.



                                The reason is searching for label referenced by GOTO first downwards in batch file and next from top on not finding it to bottom of file.



                                I verified that with using first a batch file with following lines and a file size of 941 bytes:



                                @echo off
                                setlocal EnableExtensions EnableDelayedExpansion
                                for /F "tokens=2 delims==." %%I in ('%SystemRoot%System32wbemwmic.exe OS GET LocalDateTime /VALUE') do set "StartTime=%%I"
                                for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do call :ConvertLine "%%I"
                                for /F "tokens=2 delims==." %%I in ('%SystemRoot%System32wbemwmic.exe OS GET LocalDateTime /VALUE') do set "EndTime=%%I"
                                set /A StartTime=(1%StartTime:~8,2% - 100) * 3600 + (1%StartTime:~10,2% - 100) * 60 + 1%StartTime:~12,2% - 100
                                set /A EndTime=(1%EndTime:~8,2% - 100) * 3600 + (1%EndTime:~10,2% - 100) * 60 + 1%EndTime:~12,2% - 100
                                set /A DiffTime=EndTime - StartTime
                                echo Time: %DiffTime% seconds
                                endlocal
                                goto :EOF

                                :ConvertLine
                                set "DataLine=%~1"
                                set "AllValues="
                                set "StartColumn=0"
                                :NextValue
                                set /A Value=0x!DataLine:~%StartColumn%,4!
                                set "AllValues=%AllValues%,%Value%"
                                set /A StartColumn+=4
                                if not %StartColumn% == 32 goto NextValue
                                goto :EOF


                                It took 34 seconds to complete the task which was done with larger test batch file in 51 seconds on notebook with Windows XP.



                                Then I created a copy of this batch file and inserted between goto :EOF and :ConvertLine a block with 250 lines all with same string:



                                rem comment line of no interest


                                This batch file with 9193 bytes required 64 seconds to do exactly the same task.



                                So searching for a label which is just four lines upwards is definitely the reason for the much longer time of method 1 in comparison to method 2. And method 2 is slower than method 3 and 4 mainly because of the same reason.



                                But I have still not found out why the second batch file with 9193 bytes needs 72 seconds instead of 64 seconds on notebook running on battery instead of power supply plugged in. The batch file and the data file are loaded in cache. There is no output on running the batch file. And I have configured in the power options to use maximum performance also on running on battery. Scanning for the label in batch file is obviously slower on running on battery than on power supply plugged in although hard disk is not really accessed during batch file execution, just CPU core, CPU cache and RAM.



                                I tried also this batch code with using region dependent TIME environment variable instead of using WMIC command to get a region independent date/time. %TIME% expands on my PCs to German time format HH::MM:SS.ms.



                                @echo off
                                setlocal EnableExtensions EnableDelayedExpansion
                                set "StartTime=%TIME%"
                                for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do call :ConvertLine "%%I"
                                set "EndTime=%TIME%"
                                set /A StartTime=(1%StartTime:~0,2% - 100) * 3600 + (1%StartTime:~3,2% - 100) * 60 + 1%StartTime:~6,2% - 100
                                set /A EndTime=(1%EndTime:~0,2% - 100) * 3600 + (1%EndTime:~3,2% - 100) * 60 + 1%EndTime:~6,2% - 100
                                set /A DiffTime=EndTime - StartTime
                                echo Time: %DiffTime%
                                endlocal
                                goto :EOF

                                :ConvertLine
                                set "DataLine=%~1"
                                set "AllValues="
                                set "StartColumn=0"
                                :NextValue
                                set /A Value=0x!DataLine:~%StartColumn%,4!
                                set "AllValues=%AllValues%,%Value%"
                                set /A StartColumn+=4
                                if not %StartColumn% == 32 goto NextValue
                                goto :EOF


                                This batch file finished in 30 seconds on running on power supply plugged in on Windows XP x86 with HDD ST980411ASG with 7200 rpm (magnetic hard disk). The same batch file running next on battery took 37 seconds to finish on same PC.



                                It took 72 seconds on PC with Windows 7 x64 with a Samsung SSD 850 EVO (solid state disk) with power supply plugged in and 77 seconds on running on battery. I have just plugged out the power supply between the test runs, nothing else changed. There was no connection to any network, WLAN switched off per hardware switch, bluetooth is disabled in BIOS, anti-virus application disabled during execution (with exception of Windows Defender on Windows 7x 64).



                                I run this batch file again and again on PC with Windows 7 x64 and finally with fan spinning on quite often during execution, the execution time became constant with 72 seconds independent on power supply plugged in or not.



                                For comparison I executed also this batch file:



                                @echo off
                                setlocal EnableExtensions EnableDelayedExpansion
                                set "StartTime=%TIME%"
                                for /F "usebackq delims=" %%I in ("%TEMP%HexValues.dat") do (
                                set "DataLine=%%I"
                                set "AllValues="
                                for /L %%J in (0,4,28) do (
                                set /A Value=0x!DataLine:~%%J,4!
                                set "AllValues=!AllValues!,!Value!"
                                )
                                )
                                set "EndTime=%TIME%"
                                set /A StartTime=(1%StartTime:~0,2% - 100) * 3600 + (1%StartTime:~3,2% - 100) * 60 + 1%StartTime:~6,2% - 100
                                set /A EndTime=(1%EndTime:~0,2% - 100) * 3600 + (1%EndTime:~3,2% - 100) * 60 + 1%EndTime:~6,2% - 100
                                set /A DiffTime=EndTime - StartTime
                                echo Time: %DiffTime%
                                endlocal


                                It took 1 or 2 seconds on PC running Windows XP to finish whereby on power supply plugged in the finishing time was most often 1 second while on running on battery the finishing time was most often 2 seconds. I would need to take the milliseconds also into account to be more precise on this fast finishing solution. The execution time was between 4 and 5 seconds on PC Windows 7 x64 same trend to lower time on power supply plugged in like on other notebook with Windows XP.



                                The hard disk activity light emitting diodes on both computers don't blink different in comparison to not running the batch file. I don't hear any different sound from the HDD on Windows XP while running the batch file taking about 30 seconds to finish and even more on running on battery.



                                But I can see with using Process Monitor on both PCs that the batch file itself is permanently opened, read, closed on running the batch file with using GOTO loop while there is nearly no batch file access on using the most optimized version with FOR loop.



                                And cmd.exe really reads the batch file line by line again and again with GOTO method as it can be seen also with Process Monitor on large amount of ReadFile accesses with increasing Offset with the offsets being identical to the offsets of beginning of each line in batch file. The execution time increases dramatically on Process Monitor recording file system accesses because of more than two millions of recorded events and more than 500,000 events displayed.



                                Further, it can be seen with Process Monitor on using just the optimized FOR loop that cmd.exe reads the lines up to end of FOR loop, then reads once entire HexValues.dat with just one ReadFile access, takes 5 seconds (on Windows 7 x64) to finish the conversion from hexadecimal to decimal without any file system access and next reads the remaining lines of the batch file to finish its execution. Process Monitor records just about 50,000 events with less than 100 events displayed.



                                I suppose Intel SpeedStep technology being enabled in BIOS is the reason for increased time of batch file execution with command GOTO with label above current command line on running on battery. That would also explain in my point of view the effect that running the second posted batch in this answer again and again on Windows 7 x64 results finally in a constant execution time independent on power supply plugged in or not because of Intel SpeedStep finally increases the performance of the CPU to maximum even on running on battery because of one core runs permanently on 100%.



                                Conclusion:



                                The GOTO solution of method 1 is so much slower than all other methods because of cmd.exe does following on reaching goto NextValue according to information given by Aacini and the others analyzed and verified with Process Monitor:




                                1. Open the batch file for reading and query standard information about file to check if batch file changed since last access. Querying standard information is not done on the other steps below.

                                2. Read one line after the other from batch file with processing each line to find the line with :NextValue with no success to end of file.

                                3. Rewind to top of file on reaching end of file.

                                4. Read one line after the other from batch file now from top with processing each line to find the line with :NextValue.

                                5. Close the batch file on having found the line :NextValue and so knowing offset for next command line to process.

                                6. Open the batch file again.

                                7. Read the line set /A Value=0x!DataLine:~%StartColumn%,4!.

                                8. Close the batch file again.

                                9. Process the read command line.

                                10. Open the batch file once more.

                                11. Read next line set "AllValues=%AllValues%,%Value%" from batch file.

                                12. Close the batch file.

                                13. Process the read command line.

                                14. Open the batch file once more.

                                15. Read next line set /A StartColumn+=4 from batch file.

                                16. Close the batch file.

                                17. Process the read command line.

                                18. Open the batch file once more.

                                19. Read next line if not %StartColumn% == 32 goto NextValue from batch file.

                                20. Close the batch file.

                                21. Process the read command line.

                                22. Continue on first step if the condition is true, i.e. StartColumn is not 32.


                                And all those batch file open / read / close operations take some milliseconds even on not accessing storage media, but most likely (my assumption) on accessing DRAM on motherboard in which the file is loaded because of entire batch file content not loaded into internal cache of CPU.



                                So with using a FOR loop in the subroutine as done by method 2 the number of batch file accessing actions is already reduced dramatically because of none of the 21 steps with much more file accessing actions to read the batch file line by line and not explicitly listed all in single steps needs to be done on processing the hexadecimal values in current line read from HexValues.dat.



                                And all lines in HexValues.dat can be processed by cmd.exe without any access of the batch file on doing the entire conversion in one FOR loop as done by the methods 3 and 4. And finally some more file system accesses can be saved with outputting all lines of CSV to STDOUT (buffer in memory) and writing them just once into CSV file as done by method 4 reducing once more the total time required for this value conversion task in comparison to method 3.



                                It should be avoided having a command line with goto LABEL or call :LABEL with LABEL being above the current line or many lines below on a larger batch file doing something in a loop with hundreds or thousands of iterations. A complex respectively not easy to understand FOR loop for non-experts in batch file coding is in such cases better than a nice readable solution using GOTO or CALL. Or in other words it is always advisable to use just a FOR loop on doing something in a loop really often.







                                share|improve this answer














                                share|improve this answer



                                share|improve this answer








                                edited Nov 19 '18 at 19:07

























                                answered Nov 18 '18 at 17:31









                                MofiMofi

                                28.1k83777




                                28.1k83777








                                • 1





                                  It is really interesting that the power supply affects the performance even with same settings for battery mode; unfortunately my notebook broke down, so I cannot do any experiments at the moment, but I guess Windows is still behaving differently on battery than on mains power, even if all settings are equal...

                                  – aschipfl
                                  Nov 18 '18 at 18:26











                                • I think you are wrong with these phrases: "The batch file and the data file are loaded in cache" and "although hard disk is not really accessed during batch file execution". Where did you get these points from? As far as I know, the Batch file execution repeatedly open/read/close the Batch file from disk for each executed line... I also don't agree that a solution using GOTO or CALL be "nice readable solution" when compared vs a complex FOR construct... ;)

                                  – Aacini
                                  Nov 18 '18 at 18:36













                                • Most disks have an internal cache. So I think it's safe to assume, the batch file (if not too big) is effectively running from either Windows cache or at least Disk cache (as long as there is no other heavy IO).

                                  – Stephan
                                  Nov 19 '18 at 8:55











                                • @Aacini I am really quite sure that hard disk itself is not accessed during batch file execution. I extended my answer after running more tests and using also Sysinternals Process Monitor. The file accesses are all done on cached file in memory. But it was interesting to see that on running batch file solution with GOTO again and again that after more than 10 executions it did not make anymore a difference on running on power supply or on battery on Windows 7 x64. However, the main batch file is a good performance testing script. I plan to run it also on other PCs with Windows 10 x64.

                                  – Mofi
                                  Nov 19 '18 at 10:27











                                • IMHO the central point that causes this problem is the open/read/close file method used to run Batch files that is done just once with FOR, but repeated many times with GOTO as I first mentioned in my comment. The fact that a GOTO command searches for the label in downwards order just causes to execute more lines in large files. However, in your extensive explanation of the problem you not even mentioned my comment about this point...

                                  – Aacini
                                  Nov 19 '18 at 17:37














                                • 1





                                  It is really interesting that the power supply affects the performance even with same settings for battery mode; unfortunately my notebook broke down, so I cannot do any experiments at the moment, but I guess Windows is still behaving differently on battery than on mains power, even if all settings are equal...

                                  – aschipfl
                                  Nov 18 '18 at 18:26











                                • I think you are wrong with these phrases: "The batch file and the data file are loaded in cache" and "although hard disk is not really accessed during batch file execution". Where did you get these points from? As far as I know, the Batch file execution repeatedly open/read/close the Batch file from disk for each executed line... I also don't agree that a solution using GOTO or CALL be "nice readable solution" when compared vs a complex FOR construct... ;)

                                  – Aacini
                                  Nov 18 '18 at 18:36













                                • Most disks have an internal cache. So I think it's safe to assume, the batch file (if not too big) is effectively running from either Windows cache or at least Disk cache (as long as there is no other heavy IO).

                                  – Stephan
                                  Nov 19 '18 at 8:55











                                • @Aacini I am really quite sure that hard disk itself is not accessed during batch file execution. I extended my answer after running more tests and using also Sysinternals Process Monitor. The file accesses are all done on cached file in memory. But it was interesting to see that on running batch file solution with GOTO again and again that after more than 10 executions it did not make anymore a difference on running on power supply or on battery on Windows 7 x64. However, the main batch file is a good performance testing script. I plan to run it also on other PCs with Windows 10 x64.

                                  – Mofi
                                  Nov 19 '18 at 10:27











                                • IMHO the central point that causes this problem is the open/read/close file method used to run Batch files that is done just once with FOR, but repeated many times with GOTO as I first mentioned in my comment. The fact that a GOTO command searches for the label in downwards order just causes to execute more lines in large files. However, in your extensive explanation of the problem you not even mentioned my comment about this point...

                                  – Aacini
                                  Nov 19 '18 at 17:37








                                1




                                1





                                It is really interesting that the power supply affects the performance even with same settings for battery mode; unfortunately my notebook broke down, so I cannot do any experiments at the moment, but I guess Windows is still behaving differently on battery than on mains power, even if all settings are equal...

                                – aschipfl
                                Nov 18 '18 at 18:26





                                It is really interesting that the power supply affects the performance even with same settings for battery mode; unfortunately my notebook broke down, so I cannot do any experiments at the moment, but I guess Windows is still behaving differently on battery than on mains power, even if all settings are equal...

                                – aschipfl
                                Nov 18 '18 at 18:26













                                I think you are wrong with these phrases: "The batch file and the data file are loaded in cache" and "although hard disk is not really accessed during batch file execution". Where did you get these points from? As far as I know, the Batch file execution repeatedly open/read/close the Batch file from disk for each executed line... I also don't agree that a solution using GOTO or CALL be "nice readable solution" when compared vs a complex FOR construct... ;)

                                – Aacini
                                Nov 18 '18 at 18:36







                                I think you are wrong with these phrases: "The batch file and the data file are loaded in cache" and "although hard disk is not really accessed during batch file execution". Where did you get these points from? As far as I know, the Batch file execution repeatedly open/read/close the Batch file from disk for each executed line... I also don't agree that a solution using GOTO or CALL be "nice readable solution" when compared vs a complex FOR construct... ;)

                                – Aacini
                                Nov 18 '18 at 18:36















                                Most disks have an internal cache. So I think it's safe to assume, the batch file (if not too big) is effectively running from either Windows cache or at least Disk cache (as long as there is no other heavy IO).

                                – Stephan
                                Nov 19 '18 at 8:55





                                Most disks have an internal cache. So I think it's safe to assume, the batch file (if not too big) is effectively running from either Windows cache or at least Disk cache (as long as there is no other heavy IO).

                                – Stephan
                                Nov 19 '18 at 8:55













                                @Aacini I am really quite sure that hard disk itself is not accessed during batch file execution. I extended my answer after running more tests and using also Sysinternals Process Monitor. The file accesses are all done on cached file in memory. But it was interesting to see that on running batch file solution with GOTO again and again that after more than 10 executions it did not make anymore a difference on running on power supply or on battery on Windows 7 x64. However, the main batch file is a good performance testing script. I plan to run it also on other PCs with Windows 10 x64.

                                – Mofi
                                Nov 19 '18 at 10:27





                                @Aacini I am really quite sure that hard disk itself is not accessed during batch file execution. I extended my answer after running more tests and using also Sysinternals Process Monitor. The file accesses are all done on cached file in memory. But it was interesting to see that on running batch file solution with GOTO again and again that after more than 10 executions it did not make anymore a difference on running on power supply or on battery on Windows 7 x64. However, the main batch file is a good performance testing script. I plan to run it also on other PCs with Windows 10 x64.

                                – Mofi
                                Nov 19 '18 at 10:27













                                IMHO the central point that causes this problem is the open/read/close file method used to run Batch files that is done just once with FOR, but repeated many times with GOTO as I first mentioned in my comment. The fact that a GOTO command searches for the label in downwards order just causes to execute more lines in large files. However, in your extensive explanation of the problem you not even mentioned my comment about this point...

                                – Aacini
                                Nov 19 '18 at 17:37





                                IMHO the central point that causes this problem is the open/read/close file method used to run Batch files that is done just once with FOR, but repeated many times with GOTO as I first mentioned in my comment. The fact that a GOTO command searches for the label in downwards order just causes to execute more lines in large files. However, in your extensive explanation of the problem you not even mentioned my comment about this point...

                                – Aacini
                                Nov 19 '18 at 17:37


















                                draft saved

                                draft discarded




















































                                Thanks for contributing an answer to Stack Overflow!


                                • Please be sure to answer the question. Provide details and share your research!

                                But avoid



                                • Asking for help, clarification, or responding to other answers.

                                • Making statements based on opinion; back them up with references or personal experience.


                                To learn more, see our tips on writing great answers.




                                draft saved


                                draft discarded














                                StackExchange.ready(
                                function () {
                                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53362979%2fwhy-is-a-goto-loop-much-slower-than-a-for-loop-and-depends-additionally-on-power%23new-answer', 'question_page');
                                }
                                );

                                Post as a guest















                                Required, but never shown





















































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown

































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown







                                Popular posts from this blog

                                Guess what letter conforming each word

                                Port of Spain

                                Run scheduled task as local user group (not BUILTIN)