206 lines
8.2 KiB
V
206 lines
8.2 KiB
V
import os
|
|
import flag
|
|
import scripting
|
|
import vgit
|
|
|
|
const (
|
|
tool_version = '0.0.5'
|
|
tool_description = ' Compares V executable size and performance,
|
|
between 2 commits from V\'s local git history.
|
|
When only one commit is given, it is compared to master.
|
|
'
|
|
)
|
|
|
|
struct Context {
|
|
cwd string // current working folder
|
|
mut:
|
|
vgo vgit.VGitOptions
|
|
a string // the full path to the 'after' folder inside workdir
|
|
b string // the full path to the 'before' folder inside workdir
|
|
vc string // the full path to the vc folder inside workdir. It is used during bootstrapping v from the C source.
|
|
commit_before string // the git commit for the 'before' state
|
|
commit_after string // the git commit for the 'after' state
|
|
warmups int // how many times to execute a command before gathering stats
|
|
hyperfineopts string // use for additional CLI options that will be given to the hyperfine command
|
|
vflags string // other v options to pass to compared v commands
|
|
}
|
|
|
|
fn new_context() Context {
|
|
return Context{
|
|
cwd: os.getwd()
|
|
commit_after: 'master'
|
|
warmups: 4
|
|
}
|
|
}
|
|
|
|
fn (c Context) compare_versions() {
|
|
// Input is validated at this point...
|
|
// Cleanup artifacts from previous runs of this tool:
|
|
scripting.chdir(c.vgo.workdir)
|
|
scripting.run('rm -rf "$c.a" "$c.b" "$c.vc" ')
|
|
// clone the VC source *just once per comparison*, and reuse it:
|
|
scripting.run('git clone --quiet "$c.vgo.vc_repo_url" "$c.vc" ')
|
|
println('Comparing V 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)
|
|
scripting.chdir(c.vgo.workdir)
|
|
|
|
if c.vflags.len > 0 {
|
|
os.setenv('VFLAGS', c.vflags, true)
|
|
}
|
|
|
|
// The first is the baseline, against which all the others will be compared.
|
|
// It is the fastest, since hello_world.v has only a single println in it,
|
|
mut perf_files := []string
|
|
perf_files << c.compare_v_performance('source_hello', [
|
|
'vprod @DEBUG@ -o source.c examples/hello_world.v',
|
|
'vprod -o source.c examples/hello_world.v',
|
|
'v @DEBUG@ -o source.c examples/hello_world.v',
|
|
'v -o source.c examples/hello_world.v',
|
|
])
|
|
|
|
perf_files << c.compare_v_performance('source_v', [
|
|
'vprod @DEBUG@ -o source.c @COMPILER@',
|
|
'vprod -o source.c @COMPILER@',
|
|
'v @DEBUG@ -o source.c @COMPILER@',
|
|
'v -o source.c @COMPILER@',
|
|
])
|
|
|
|
perf_files << c.compare_v_performance('binary_hello', [
|
|
'vprod -o hello examples/hello_world.v',
|
|
'v -o hello examples/hello_world.v',
|
|
])
|
|
|
|
perf_files << c.compare_v_performance('binary_v', [
|
|
'vprod -o binary @COMPILER@',
|
|
'v -o binary @COMPILER@',
|
|
])
|
|
|
|
println('All performance files:')
|
|
for f in perf_files {
|
|
println(' $f')
|
|
}
|
|
}
|
|
|
|
fn (c &Context) prepare_v(cdir string, commit string) {
|
|
mut cc := os.getenv('CC')
|
|
if cc == '' {
|
|
cc = 'cc'
|
|
}
|
|
mut vgit_context := vgit.VGitContext{
|
|
cc: cc
|
|
commit_v: commit
|
|
path_v: cdir
|
|
path_vc: c.vc
|
|
workdir: c.vgo.workdir
|
|
v_repo_url: c.vgo.v_repo_url
|
|
vc_repo_url: c.vgo.vc_repo_url
|
|
}
|
|
vgit_context.compile_oldv_if_needed()
|
|
scripting.chdir(cdir)
|
|
println('Making a v compiler in $cdir')
|
|
scripting.run('./v -cc ${cc} -o v $vgit_context.vvlocation')
|
|
println('Making a vprod compiler in $cdir')
|
|
scripting.run('./v -cc ${cc} -prod -o vprod $vgit_context.vvlocation')
|
|
println('Stripping and compressing cv v and vprod binaries in $cdir')
|
|
scripting.run('cp cv cv_stripped')
|
|
scripting.run('cp v v_stripped')
|
|
scripting.run('cp vprod vprod_stripped')
|
|
scripting.run('strip *_stripped')
|
|
scripting.run('cp cv_stripped cv_stripped_upxed')
|
|
scripting.run('cp v_stripped v_stripped_upxed')
|
|
scripting.run('cp vprod_stripped vprod_stripped_upxed')
|
|
scripting.run('upx -qqq --lzma cv_stripped_upxed')
|
|
scripting.run('upx -qqq --lzma v_stripped_upxed')
|
|
scripting.run('upx -qqq --lzma vprod_stripped_upxed')
|
|
scripting.show_sizes_of_files(['$cdir/cv', '$cdir/cv_stripped', '$cdir/cv_stripped_upxed'])
|
|
scripting.show_sizes_of_files(['$cdir/v', '$cdir/v_stripped', '$cdir/v_stripped_upxed'])
|
|
scripting.show_sizes_of_files(['$cdir/vprod', '$cdir/vprod_stripped', '$cdir/vprod_stripped_upxed'])
|
|
vversion := scripting.run('$cdir/v -version')
|
|
vcommit := scripting.run('git rev-parse --short --verify HEAD')
|
|
println('V version is: ${vversion} , local source commit: ${vcommit}')
|
|
if vgit_context.vvlocation == 'cmd/v' {
|
|
println('Source lines of the compiler: ' + scripting.run('wc cmd/v/*.v vlib/compiler/*.v | tail -n -1'))
|
|
} else if vgit_context.vvlocation == 'v.v' {
|
|
println('Source lines of the compiler: ' + scripting.run('wc v.v vlib/compiler/*.v | tail -n -1'))
|
|
}else{
|
|
println('Source lines of the compiler: ' + scripting.run('wc compiler/*.v | tail -n -1'))
|
|
}
|
|
}
|
|
|
|
fn (c Context) compare_v_performance(label string, commands []string) string {
|
|
println('---------------------------------------------------------------------------------')
|
|
println('Compare v performance when doing the following commands ($label):')
|
|
mut source_location_a := ''
|
|
mut source_location_b := ''
|
|
if os.exists('$c.a/cmd/v') {
|
|
source_location_a = 'cmd/v'
|
|
} else {
|
|
source_location_a = if os.exists('$c.a/v.v') { 'v.v ' } else { 'compiler/ ' }
|
|
}
|
|
if os.exists('$c.b/cmd/v') {
|
|
source_location_b = 'cmd/v'
|
|
} else {
|
|
source_location_b = if os.exists('$c.b/v.v') { 'v.v ' } else { 'compiler/ ' }
|
|
}
|
|
timestamp_a,_ := vgit.line_to_timestamp_and_commit(scripting.run('cd $c.a/ ; git rev-list -n1 --timestamp HEAD'))
|
|
timestamp_b,_ := vgit.line_to_timestamp_and_commit(scripting.run('cd $c.b/ ; git rev-list -n1 --timestamp HEAD'))
|
|
debug_option_a := if timestamp_a > 1570877641 { '-g ' } else { '-debug ' }
|
|
debug_option_b := if timestamp_b > 1570877641 { '-g ' } else { '-debug ' }
|
|
mut hyperfine_commands_arguments := []string
|
|
for cmd in commands {
|
|
println(cmd)
|
|
}
|
|
for cmd in commands {
|
|
hyperfine_commands_arguments << " \'cd ${c.b:-34s} ; ./$cmd \' ".replace_each(['@COMPILER@', source_location_b, '@DEBUG@', debug_option_b])
|
|
}
|
|
for cmd in commands {
|
|
hyperfine_commands_arguments << " \'cd ${c.a:-34s} ; ./$cmd \' ".replace_each(['@COMPILER@', source_location_a, '@DEBUG@', debug_option_a])
|
|
}
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
cmd_stats_file := os.real_path([c.vgo.workdir, 'v_performance_stats_${label}.json'].join(os.path_separator))
|
|
comparison_cmd := 'hyperfine $c.hyperfineopts ' + '--export-json ${cmd_stats_file} ' + '--time-unit millisecond ' + '--style full --warmup $c.warmups ' + hyperfine_commands_arguments.join(' ')
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
if c.vgo.verbose {
|
|
println(comparison_cmd)
|
|
}
|
|
os.system(comparison_cmd)
|
|
println('The detailed performance comparison report was saved to: $cmd_stats_file .')
|
|
println('')
|
|
return cmd_stats_file
|
|
}
|
|
|
|
fn main() {
|
|
scripting.used_tools_must_exist(['cp', 'rm', 'strip', 'make', 'git', 'upx', 'cc', 'wc', 'tail', 'hyperfine'])
|
|
mut context := new_context()
|
|
mut fp := flag.new_flag_parser(os.args)
|
|
fp.application(os.file_name(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)
|
|
|
|
context.vflags = fp.string('vflags', 0, '', 'Additional options to pass to the v commands, for example "-cc tcc"')
|
|
context.hyperfineopts = fp.string('hyperfine_options', 0, '',
|
|
'Additional options passed to hyperfine.
|
|
${flag.space}For example on linux, you may want to pass:
|
|
${flag.space}--hyperfine_options "--prepare \'sync; echo 3 | sudo tee /proc/sys/vm/drop_caches\'"
|
|
')
|
|
commits := vgit.add_common_tool_options(mut context.vgo, mut fp)
|
|
context.commit_before = commits[0]
|
|
if commits.len > 1 {
|
|
context.commit_after = commits[1]
|
|
}
|
|
context.b = vgit.normalized_workpath_for_commit(context.vgo.workdir, context.commit_before)
|
|
context.a = vgit.normalized_workpath_for_commit(context.vgo.workdir, context.commit_after)
|
|
context.vc = vgit.normalized_workpath_for_commit(context.vgo.workdir, 'vc')
|
|
if !os.is_dir(context.vgo.workdir) {
|
|
msg := 'Work folder: ' + context.vgo.workdir + ' , does not exist.'
|
|
eprintln(msg)
|
|
exit(2)
|
|
}
|
|
|
|
context.compare_versions()
|
|
}
|