1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

tooling: add tools/compare_v_performance_between_commits

easily compare v performance/size across commits.

* fix eprintln on linux (it now uses stderr, and flushes it).

* flag: cleaner usage information.
This commit is contained in:
Delyan Angelov
2019-09-28 14:17:16 +03:00
committed by Alexander Medvednikov
parent 5c79c0e743
commit 366c50674c
6 changed files with 361 additions and 44 deletions

View File

@@ -0,0 +1,98 @@
#!/bin/sh
set -e
msg() {
printf '%s\n' "$*";
}
if [ $# -ne 2 ]; then
msg "Usage: compare_v_to_c_performance COMMIT_BEFORE COMMIT_AFTER"
exit 1
fi
depend_on() {
type "$1" >/dev/null 2>&1 || {
printf 'ERR: missing tool "%s"\n' "$1" >&2; exit 1;
}
}
depend_on sh
depend_on cp
depend_on rm
depend_on wc
depend_on head
depend_on cc
depend_on strip
depend_on git
depend_on upx
depend_on make
depend_on hyperfine
######################################################################
## NB: cc should be a working, recent, sane C99 compiler
## cc is used by the Makefile to bootstrap v (both gcc/clang work)
##
## If you are a C/V developer in a unix environment, you most probably
## already have the above installed, with the possible exception of:
## https://github.com/sharkdp/hyperfine
##
## Installing them is out of scope of this tool.
######################################################################
COMMIT_B="$1"
COMMIT_A="$2"
CWD="$(pwd)"
WORKDIR="/tmp"
B="$WORKDIR/v_at_$COMMIT_B"
A="$WORKDIR/v_at_$COMMIT_A"
prepare_v() {
msg
msg "Cloning current v source to $1 ..."
git clone --quiet "$CWD" "$1"
cd "$1"
git checkout --quiet "$2"
msg "Making v and vprod compilers in $1"
make > /dev/null
./v -o v compiler
./v -prod -o vprod compiler
cp v v_stripped
cp vprod vprod_stripped
strip *_stripped
cp v_stripped v_stripped_upxed
cp vprod_stripped vprod_stripped_upxed
upx -qqq --lzma v_stripped_upxed
upx -qqq --lzma vprod_stripped_upxed
wc -c "$1/v" "$1/v_stripped" "$1/v_stripped_upxed" "$1/vprod" "$1/vprod_stripped" "$1/vprod_stripped_upxed" | head -n -1
VVERSION="$($1/v --version)"
GVERSION="$(git rev-parse --short --verify HEAD)"
msg "V version is: $VVERSION , local source commit: $GVERSION"
}
compare_v_performance() {
CMD="$1"
msg "---------------------------------------------------------------------------------"
msg "Compare '$CMD'"
hyperfine --warmup=3 "cd '$B/' && $CMD " "cd '$A/' && $CMD "
msg
}
##############################################################################
# Cleanup artifacts from previous runs of this tool:
cd "$WORKDIR"
rm -rf "$A/" "$B/"
##############################################################################
msg "Comparing v compiler performance of commit $COMMIT_B (before) vs commit $COMMIT_A (after) ..."
prepare_v "$B" "$COMMIT_B"
prepare_v "$A" "$COMMIT_A"
cd "$WORKDIR"
compare_v_performance "./v -o x.c compiler"
compare_v_performance "./vprod -o x.c compiler"
compare_v_performance "./vprod -o x compiler"

158
tools/performance_compare.v Normal file
View File

@@ -0,0 +1,158 @@
import os
import flag
const (
tool_version = '0.0.3'
tool_description = '' +
' Compares V executable size and performance,\n' +
' between 2 commits from V\'s local git history.\n' +
' When only one commit is given, it is compared to master.'
)
struct Context {
cwd string // current working folder
mut:
workdir string // the working folder (typically /tmp), where the tool will write
a string // the full path to the 'after' folder inside workdir
b string // the full path to the 'before' folder inside workdir
commit_before string // the git commit for the 'before' state
commit_after string // the git commit for the 'after' state
}
fn new_context() Context {
return Context{ cwd: os.getwd(), commit_after: 'master' }
}
////// The stuff in this block may be reusable for other v cli tools? /////////////////
fn run(cmd string) string {
x := os.exec(cmd) or { return '' }
if x.exit_code == 0 { return x.output }
return ''
}
fn command_exits_with_zero_status(cmd string) bool {
x := os.exec(cmd) or { return false }
if x.exit_code == 0 { return true }
return false
}
fn tool_must_exist(toolcmd string) {
if command_exits_with_zero_status( 'type $toolcmd' ) { return }
eprintln('Missing tool: $toolcmd')
eprintln('Please try again after you install it.')
exit(1)
}
fn used_tools_must_exist(tools []string) {
for t in tools {
tool_must_exist(t)
}
}
//////////////////////////////////////////////////////////////////////////
fn (c Context) compare_versions() {
// Input is validated at this point...
//Cleanup artifacts from previous runs of this tool:
os.chdir( c.workdir )
run('rm -rf "$c.a" "$c.b" ')
println('Comparing v compiler performance of commit $c.commit_before (before) vs commit $c.commit_after (after) ...')
c.prepare_v( c.b , c.commit_before )
c.prepare_v( c.a , c.commit_after )
os.chdir( c.workdir )
c.compare_v_performance( 'v -o source.c compiler' )
c.compare_v_performance( 'vprod -o source.c compiler' )
c.compare_v_performance( 'vprod -o binary compiler' )
}
fn show_sizes_of_files(files []string) {
for f in files {
size := os.file_size(f)
println('${size:10d} $f')
}
}
fn (c &Context) prepare_v( cdir string, commit string ) {
println('')
println('Cloning current v source to $cdir ...')
os.system('git clone --quiet \'$c.cwd\' \'$cdir\' ')
os.chdir( cdir )
os.system('git checkout --quiet \'$commit\' ')
println('Making v and vprod compilers in $cdir')
run('make')
run('./v -o v compiler/ ')
run('./v -prod -o vprod compiler/ ')
run('cp v v_stripped')
run('cp vprod vprod_stripped')
run('strip *_stripped')
run('cp v_stripped v_stripped_upxed')
run('cp vprod_stripped vprod_stripped_upxed')
run('upx -qqq --lzma v_stripped_upxed')
run('upx -qqq --lzma vprod_stripped_upxed')
show_sizes_of_files(["$cdir/v", "$cdir/v_stripped", "$cdir/v_stripped_upxed"])
show_sizes_of_files(["$cdir/vprod", "$cdir/vprod_stripped", "$cdir/vprod_stripped_upxed"])
println("V version is: " + run("$cdir/v --version") + " , local source commit: " + run("git rev-parse --short --verify HEAD") )
}
fn (c Context) compare_v_performance( cmd string ) {
println('---------------------------------------------------------------------------------')
println('Compare \'$cmd\'')
comparison_cmd := 'hyperfine --warmup=3 \'cd $c.b ; ./$cmd \' \'cd $c.a ; ./$cmd \' '
os.system( comparison_cmd )
println('')
}
fn (c Context) normalized_workpath_for_commit( commit string ) string {
nc := 'v_at_' + commit.replace('^','_').replace('-','_').replace('/','_')
return os.realpath( c.workdir + os.PathSeparator + nc )
}
fn validate_commit_exists( commit string ){
cmd := 'git cat-file -t ' + "'" + commit + "'"
if !command_exits_with_zero_status(cmd) {
eprintln("Commit: '" + commit + "' does not exist in the current repository.")
exit(3)
}
}
fn main(){
used_tools_must_exist(['cp','rm','strip','make','git','upx','cc','hyperfine'])
mut context := new_context()
mut fp := flag.new_flag_parser(os.args)
fp.application(os.filename(os.executable()))
fp.version( tool_version )
fp.description( tool_description )
fp.arguments_description('COMMIT_BEFORE [COMMIT_AFTER]')
fp.skip_executable()
fp.limit_free_args(1,2)
show_help:=fp.bool('help', false, 'Show this help screen')
context.workdir = os.realpath( fp.string('workdir', '/tmp', 'A writable folder, where the comparison will be done.') )
if( show_help ){
println( fp.usage() )
exit(0)
}
commits := fp.finalize() or {
eprintln('Error: ' + err)
exit(1)
}
context.commit_before = commits[0]
if commits.len > 1 { context.commit_after = commits[1] }
validate_commit_exists( context.commit_before )
validate_commit_exists( context.commit_after )
context.b = context.normalized_workpath_for_commit( context.commit_before )
context.a = context.normalized_workpath_for_commit( context.commit_after )
if !os.is_dir( context.workdir ) {
msg := 'Work folder: ' + context.workdir + ' , does not exist.'
eprintln(msg)
exit(2)
}
context.compare_versions()
}