I want to offer a solution for the problem of measuring the execution time of code, especially when it needs to read data from standard input.
MacOS, Unix, Linux, Ubuntu
If you happen to be on MacOS or another Unix-like operating system, you are probably smiling right now. This is because you have available to you a handy little utility called time
. Problem solved! time
does exactly what we want. And is easy to use, if you do not mind redirecting standard input:
time mytask < in.txt
Windows
Windows 2003, released two years before I was born, apparently featured a similar utility called timeit
. But Microsoft removed it from the next versions of Windows. This simple tool was replaced by a convoluted and largely unusable Measure-Command
inside PowerShell.
Goal
I want to re-create timeit
on Windows which will allow easily sending input data to my compiled program .exe
, measure execution time with precision, show output, and report the exit code of my program.
For example, if I have a program add.cpp
which takes three numbers from standard input and adds them up (see below), I want to be able to time its execution like this:
timeit add.exe 2 4 5
Solution
Of course, my first and natural impulse was to count Mississippily — something like https://youtu.be/_RP3DZNKJ28?t=142
But with time this evolved slightly into the following bat
file
Windows Batch file
@echo off
REM By erekle https://codeforces.net/profile/erekle
REM ------------------------------------------
REM ------------------------------------------
REM This utility times execution of programmes
REM ------------------------------------------
REM ------------------------------------------
REM For usage and help, run this bat file without any arguments
REM -------- BEGIN: Read main parameters --------
set self=%~n0
set executable=%1
set flag=%2
set infile=%3
REM -------- BEGIN: Read main parameters --------
REM -------- BEGIN: Read command input --------
set params=%2
shift
shift
:loop1
if "%1"=="" goto after_loop
set params=%params% %1
shift
goto loop1
:after_loop
REM -------- E N D: Read command input --------
IF [%executable%]==[] (
echo.
echo %self%: Times execution of a program or a command
echo.
echo Usage:
echo. [97m%self% [96mprog.exe
echo. [97m%self% [96mprog.exe [92mparam1 param2 param3 [90m...
echo. [97m%self% [96mprog.exe [95m-f [93minput.txt[0m
exit /b
)
IF NOT EXIST %executable% (
echo [91mProgram not found: [95m%executable%[0m
exit /b
)
IF [%flag%]==[] (
powershell -Command "$duration = Measure-Command { .\%executable% | Out-Default }; $seconds = $duration.TotalSeconds; Write-Host "`n____________________________________________" -ForegroundColor Cyan; Write-Host 'Execution time (seconds):' -NoNewline; Write-Host "`t$seconds" -ForegroundColor Cyan; exit $LastExitCode"
GOTO checkerrors
exit /b
)
IF [%flag%]==[-f] (
IF [%infile%]==[] (
echo [91mOption -f should be followed by input file name[0m, for example:
echo. [96m%self% %executable% [95m-f [93minput.txt[0m
exit /b
)
) ELSE (
powershell -Command "$duration = Measure-Command { echo %params% | .\%executable% | Out-Default }; $seconds = $duration.TotalSeconds; Write-Host "`n____________________________________________" -ForegroundColor Cyan; Write-Host 'Execution time (seconds):' -NoNewline; Write-Host "`t$seconds" -ForegroundColor Cyan; exit $LastExitCode"
GOTO checkerrors
exit /b
)
REM Consider last option: %flag% is -f and %infile% was supplied
IF NOT EXIST %infile% (
echo [91mInput file not found: [95m%infile%[0m
exit /b
)
powershell -Command "$duration = Measure-Command { .\%executable% | Out-Default }" < %infile%; $seconds = $duration.TotalSeconds; Write-Host "`n____________________________________________" -ForegroundColor Cyan; Write-Host 'Execution time (seconds):' -NoNewline; Write-Host "`t$seconds" -ForegroundColor Cyan; exit $LastExitCode
:checkerrors
IF NOT %errorlevel%==0 (
echo [91mWarning: [0mExit code: [91m%errorlevel%[0m
) ELSE (
echo [0mExit code: [92m%errorlevel%[0m
)
Usage
- You can save this code in a text file with extension
.bat
(for exampletimeit.bat
) - Do not use
time.bat
though, astime
is an existing Windows command with different functionality - Place
timeit.bat
somewhere on Windows %PATH% so that it can be found from any directory - Now you can run
timeit
and it will report three different ways to use it
One option allows you to just put your input data right in the command line and your code will receive it on its standard input!
For example, if you execute the following command, add.exe
will receive 2 4 5
on standard input:
timeit add.exe 2 4 5
This might even work if you manually enter 200,000 positive integers from the keyboard ;)
Here is the full example:
Final Standings
With this code, your situation on Windows might be even slightly better than the situation on a Mac or Linux.
I hope this brings some hope back to Windows users — at least until we save money for a Mac ;)
If you are interested in changing colors for the output, this is where I found the color codes:
https://gist.githubusercontent.com/mlocati/fdabcaeb8071d5c75a2d51712db24011/raw/b710612d6320df7e146508094e84b92b34c77d48/win10colors.cmd
What about installing Linux today?
There's also Measure-Command in powershell now.
Thanks for the suggestion. It is a very useful tool.
My code uses powershell and
Measure-Command
behind the curtain, but provides a more convenient interface in my opinion.In terms of input, my code allows you to give input to your code from the command line. And in terms of output, it outputs the exit code which
Measure-Command
does not. It also re-formats the statistics in a fashion more appropriate for competitive programming (it removes days, hours, minutes, etc.).You can use the 'run' utility from IFMO training site:
https://neerc.ifmo.ru/trains/information/software.html
It measures time and memory, and allows you to constrain the execution as well.
Desktop Windows is quite slow when starting processes, especially when under load. This doesn't matter much on fast hardware such as Codeforces invokers, but keep in mind you this utility may measure bigger runtime than in reality. I usually add
...to the end of the program just before
return 0;
.Additionally, if your disk is slow,
timeit
will include system time (i.e. data load time) while this solution won't.Hello, I have a small problem. When my file name contains white spaces, using
""
doesn't seem to work. For example:timeit "hello world.exe"
Currently, my code will not be able to parse your syntax.
After seeing your comment I did spend time trying to support this syntax but did not find an easy way to do it. Apologies for that.
I hope this is not too much of an inconvenience.
Thanks, I didn't know we had this command for Ubuntu:p
This is really useful, thanks a lot!