aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/cmd/tools/performance_compare.v
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/cmd/tools/performance_compare.v')
-rw-r--r--v_windows/v/cmd/tools/performance_compare.v215
1 files changed, 215 insertions, 0 deletions
diff --git a/v_windows/v/cmd/tools/performance_compare.v b/v_windows/v/cmd/tools/performance_compare.v
new file mode 100644
index 0000000..6723231
--- /dev/null
+++ b/v_windows/v/cmd/tools/performance_compare.v
@@ -0,0 +1,215 @@
+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.
+| ".strip_margin()
+)
+
+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' {
+ if os.exists('vlib/v/ast/ast.v') {
+ println('Source lines of the compiler: ' +
+ scripting.run('find cmd/v/ vlib/v/ -name "*.v" | grep -v /tests/ | xargs wc | tail -n -1'))
+ } else {
+ 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 { '-cg ' } else { '-debug ' }
+ debug_option_b := if timestamp_b > 1570877641 { '-cg ' } 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',
+ 'find', 'xargs', '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()
+}