Why is a GOTO loop much slower than a FOR loop and depends additionally on power supply?
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
add a comment |
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
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 at32767
. Add agoto :bottom
. At bottom, add:bottom
andgoto :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 inHexValues.dat
don't make any file access onHexValues.dat
and the batch file while processing all lines inHexValues.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
add a comment |
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
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
windows batch-file cmd
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 at32767
. Add agoto :bottom
. At bottom, add:bottom
andgoto :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 inHexValues.dat
don't make any file access onHexValues.dat
and the batch file while processing all lines inHexValues.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
add a comment |
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 at32767
. Add agoto :bottom
. At bottom, add:bottom
andgoto :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 inHexValues.dat
don't make any file access onHexValues.dat
and the batch file while processing all lines inHexValues.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
add a comment |
5 Answers
5
active
oldest
votes
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.
add a comment |
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.
add a comment |
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.
add a comment |
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
2
I suggested somewhere to use:@F
label to define a repeated label that will only be used to jump a few lines forward viaGOTO @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
add a comment |
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:
- 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.
- 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. - Rewind to top of file on reaching end of file.
- Read one line after the other from batch file now from top with processing each line to find the line with
:NextValue
. - Close the batch file on having found the line
:NextValue
and so knowing offset for next command line to process. - Open the batch file again.
- Read the line
set /A Value=0x!DataLine:~%StartColumn%,4!
. - Close the batch file again.
- Process the read command line.
- Open the batch file once more.
- Read next line
set "AllValues=%AllValues%,%Value%"
from batch file. - Close the batch file.
- Process the read command line.
- Open the batch file once more.
- Read next line
set /A StartColumn+=4
from batch file. - Close the batch file.
- Process the read command line.
- Open the batch file once more.
- Read next line
if not %StartColumn% == 32 goto NextValue
from batch file. - Close the batch file.
- Process the read command line.
- Continue on first step if the condition is true, i.e.
StartColumn
is not32
.
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.
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
|
show 3 more comments
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
add a comment |
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.
add a comment |
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.
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.
edited Nov 18 '18 at 17:47
answered Nov 18 '18 at 16:49
mnisticmnistic
7,1731923
7,1731923
add a comment |
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
answered Nov 18 '18 at 16:40
Matt TimmermansMatt Timmermans
19.2k11532
19.2k11532
add a comment |
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
answered Nov 18 '18 at 16:41
StephanStephan
35k43255
35k43255
add a comment |
add a comment |
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
2
I suggested somewhere to use:@F
label to define a repeated label that will only be used to jump a few lines forward viaGOTO @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
add a comment |
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
2
I suggested somewhere to use:@F
label to define a repeated label that will only be used to jump a few lines forward viaGOTO @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
add a comment |
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
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
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 viaGOTO @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
add a comment |
2
I suggested somewhere to use:@F
label to define a repeated label that will only be used to jump a few lines forward viaGOTO @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
add a comment |
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:
- 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.
- 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. - Rewind to top of file on reaching end of file.
- Read one line after the other from batch file now from top with processing each line to find the line with
:NextValue
. - Close the batch file on having found the line
:NextValue
and so knowing offset for next command line to process. - Open the batch file again.
- Read the line
set /A Value=0x!DataLine:~%StartColumn%,4!
. - Close the batch file again.
- Process the read command line.
- Open the batch file once more.
- Read next line
set "AllValues=%AllValues%,%Value%"
from batch file. - Close the batch file.
- Process the read command line.
- Open the batch file once more.
- Read next line
set /A StartColumn+=4
from batch file. - Close the batch file.
- Process the read command line.
- Open the batch file once more.
- Read next line
if not %StartColumn% == 32 goto NextValue
from batch file. - Close the batch file.
- Process the read command line.
- Continue on first step if the condition is true, i.e.
StartColumn
is not32
.
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.
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
|
show 3 more comments
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:
- 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.
- 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. - Rewind to top of file on reaching end of file.
- Read one line after the other from batch file now from top with processing each line to find the line with
:NextValue
. - Close the batch file on having found the line
:NextValue
and so knowing offset for next command line to process. - Open the batch file again.
- Read the line
set /A Value=0x!DataLine:~%StartColumn%,4!
. - Close the batch file again.
- Process the read command line.
- Open the batch file once more.
- Read next line
set "AllValues=%AllValues%,%Value%"
from batch file. - Close the batch file.
- Process the read command line.
- Open the batch file once more.
- Read next line
set /A StartColumn+=4
from batch file. - Close the batch file.
- Process the read command line.
- Open the batch file once more.
- Read next line
if not %StartColumn% == 32 goto NextValue
from batch file. - Close the batch file.
- Process the read command line.
- Continue on first step if the condition is true, i.e.
StartColumn
is not32
.
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.
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
|
show 3 more comments
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:
- 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.
- 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. - Rewind to top of file on reaching end of file.
- Read one line after the other from batch file now from top with processing each line to find the line with
:NextValue
. - Close the batch file on having found the line
:NextValue
and so knowing offset for next command line to process. - Open the batch file again.
- Read the line
set /A Value=0x!DataLine:~%StartColumn%,4!
. - Close the batch file again.
- Process the read command line.
- Open the batch file once more.
- Read next line
set "AllValues=%AllValues%,%Value%"
from batch file. - Close the batch file.
- Process the read command line.
- Open the batch file once more.
- Read next line
set /A StartColumn+=4
from batch file. - Close the batch file.
- Process the read command line.
- Open the batch file once more.
- Read next line
if not %StartColumn% == 32 goto NextValue
from batch file. - Close the batch file.
- Process the read command line.
- Continue on first step if the condition is true, i.e.
StartColumn
is not32
.
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.
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:
- 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.
- 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. - Rewind to top of file on reaching end of file.
- Read one line after the other from batch file now from top with processing each line to find the line with
:NextValue
. - Close the batch file on having found the line
:NextValue
and so knowing offset for next command line to process. - Open the batch file again.
- Read the line
set /A Value=0x!DataLine:~%StartColumn%,4!
. - Close the batch file again.
- Process the read command line.
- Open the batch file once more.
- Read next line
set "AllValues=%AllValues%,%Value%"
from batch file. - Close the batch file.
- Process the read command line.
- Open the batch file once more.
- Read next line
set /A StartColumn+=4
from batch file. - Close the batch file.
- Process the read command line.
- Open the batch file once more.
- Read next line
if not %StartColumn% == 32 goto NextValue
from batch file. - Close the batch file.
- Process the read command line.
- Continue on first step if the condition is true, i.e.
StartColumn
is not32
.
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.
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
|
show 3 more comments
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
|
show 3 more comments
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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 at32767
. Add agoto :bottom
. At bottom, add:bottom
andgoto :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 onHexValues.dat
and the batch file while processing all lines inHexValues.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