erekle's blog

By erekle, 4 years ago, In English

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.        %self% prog.exe
	echo.        %self% prog.exe param1 param2 param3 ...
	echo.        %self% prog.exe -f input.txt
	exit /b
)

IF NOT EXIST %executable% (
	echo Program not found: %executable%
	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 Option -f should be followed by input file name, for example:
		echo.      %self% %executable% -f input.txt
		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 Input file not found: %infile%
	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 Warning: Exit code:             %errorlevel%
) ELSE (
	echo Exit code:                      %errorlevel%
)

Usage

  • You can save this code in a text file with extension .bat (for example timeit.bat)
  • Do not use time.bat though, as time 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 ;)

  • Vote: I like it
  • +80
  • Vote: I do not like it

»
4 years ago, # |
  Vote: I like it 0 Vote: I do not like it

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

  • Here are the colors:

  • Here are the codes:
@echo off
cls
echo  STYLES 
echo ^<ESC^>[0m Reset
echo ^<ESC^>[1m Bold
echo ^<ESC^>[4m Underline
echo ^<ESC^>[7m Inverse
echo.
echo  NORMAL FOREGROUND COLORS 
echo ^<ESC^>[30m Black (black)
echo ^<ESC^>[31m Red
echo ^<ESC^>[32m Green
echo ^<ESC^>[33m Yellow
echo ^<ESC^>[34m Blue
echo ^<ESC^>[35m Magenta
echo ^<ESC^>[36m Cyan
echo ^<ESC^>[37m White
echo.
echo  NORMAL BACKGROUND COLORS 
echo ^<ESC^>[40m Black
echo ^<ESC^>[41m Red
echo ^<ESC^>[42m Green
echo ^<ESC^>[43m Yellow
echo ^<ESC^>[44m Blue
echo ^<ESC^>[45m Magenta
echo ^<ESC^>[46m Cyan
echo ^<ESC^>[47m White (white)
echo.
echo  STRONG FOREGROUND COLORS 
echo ^<ESC^>[90m White
echo ^<ESC^>[91m Red
echo ^<ESC^>[92m Green
echo ^<ESC^>[93m Yellow
echo ^<ESC^>[94m Blue
echo ^<ESC^>[95m Magenta
echo ^<ESC^>[96m Cyan
echo ^<ESC^>[97m White
echo.
echo  STRONG BACKGROUND COLORS 
echo ^<ESC^>[100m Black
echo ^<ESC^>[101m Red
echo ^<ESC^>[102m Green
echo ^<ESC^>[103m Yellow
echo ^<ESC^>[104m Blue
echo ^<ESC^>[105m Magenta
echo ^<ESC^>[106m Cyan
echo ^<ESC^>[107m White
echo.
echo  COMBINATIONS 
echo ^<ESC^>[31m                     red foreground color
echo ^<ESC^>[7m                      inverse foreground ^<-^> background
echo ^<ESC^>[7;31m                   inverse red foreground color
echo ^<ESC^>[7m and nested ^<ESC^>[31m before nested
echo ^<ESC^>[31m and nested ^<ESC^>[7m before nested
»
4 years ago, # |
  Vote: I like it +53 Vote: I do not like it

at least until we save money for a Mac

What about installing Linux today?

»
4 years ago, # |
  Vote: I like it +13 Vote: I do not like it

There's also Measure-Command in powershell now.

  • »
    »
    4 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

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

»
4 years ago, # |
  Vote: I like it +8 Vote: I do not like it

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.

»
4 years ago, # |
  Vote: I like it 0 Vote: I do not like it

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

cerr << "Run time: " << fixed << setprecision(3) << (double)clock() / CLOCKS_PER_SEC << "s" << endl;

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

»
4 years ago, # |
  Vote: I like it 0 Vote: I do not like it

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"

  • »
    »
    4 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    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.

»
4 years ago, # |
  Vote: I like it 0 Vote: I do not like it

Thanks, I didn't know we had this command for Ubuntu:p

»
4 years ago, # |
  Vote: I like it +8 Vote: I do not like it

This is really useful, thanks a lot!