From 295156e0543f1da36791bd034bd576f2b1724aac Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Fri, 21 Jan 2022 03:26:05 +0200 Subject: [PATCH] checker,cgen: implement v.profile.on/1, support `[if profile]` and `-d no_profile_startup` (#13232) --- cmd/v/help/build.txt | 8 ++++++++ vlib/v/checker/checker.v | 2 +- vlib/v/checker/comptime.v | 1 + vlib/v/gen/c/cgen.v | 3 +++ vlib/v/gen/c/cmain.v | 3 +++ vlib/v/gen/c/comptime.v | 3 +++ vlib/v/gen/c/profile.v | 15 ++++++++++++--- vlib/v/preludes/profiled_program.v | 7 ++++--- vlib/v/profile/api.v | 13 +++++++++++++ vlib/v/tests/profile/profile_test.v | 26 ++++++++++++++++++++++++++ vlib/v/tests/profile/profile_test_2.v | 23 +++++++++++++++++++++++ 11 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 vlib/v/profile/api.v create mode 100644 vlib/v/tests/profile/profile_test_2.v diff --git a/cmd/v/help/build.txt b/cmd/v/help/build.txt index 65089b158..13e037a42 100644 --- a/cmd/v/help/build.txt +++ b/cmd/v/help/build.txt @@ -84,8 +84,16 @@ NB: the build flags are shared with the run command too: b) how much *nanoseconds in total* it took c) an average for each function (i.e. (b) / (a) ) d) the function name + NB: if you want to output the profile info to stdout, use `-profile -`. + NB: you can use `import v.profile`, and then calls to `profile.on(false)` + and `profile.on(true)` to temporarily turn it off and on again. + + NB: if you do NOT want the profile to contain information from before your + program's `fn main()` starts, pass `-d no_profile_startup` too. + (V constants, and module init() functions are evaluated before `main()` is called) + -message-limit The maximum amount of warnings / errors / notices, that will be accumulated (defaults to 100). The checker will abort prematurely once this limit has been reached. diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index c1f02725c..14dde985d 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -26,7 +26,7 @@ const ( valid_comptime_if_cpu_features = ['x64', 'x32', 'little_endian', 'big_endian'] valid_comptime_if_other = ['apk', 'js', 'debug', 'prod', 'test', 'glibc', 'prealloc', 'no_bounds_checking', 'freestanding', 'threads', 'js_node', 'js_browser', 'js_freestanding', - 'interpreter', 'es5'] + 'interpreter', 'es5', 'profile'] valid_comptime_not_user_defined = all_valid_comptime_idents() array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort', 'contains', 'index', 'wait', 'any', 'all', 'first', 'last', 'pop'] diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 59a5b99d6..6f07bb144 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -532,6 +532,7 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Position) bool { 'js' { return !c.pref.backend.is_js() } 'debug' { return !c.pref.is_debug } 'prod' { return !c.pref.is_prod } + 'profile' { return !c.pref.is_prof } 'test' { return !c.pref.is_test } 'glibc' { return false } // TODO 'threads' { return c.table.gostmts == 0 } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 8da0ded09..490943bc5 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -703,6 +703,9 @@ pub fn (mut g Gen) init() { if g.pref.is_test || 'test' in g.pref.compile_defines { g.comptime_definitions.writeln('#define _VTEST (1)') } + if g.pref.is_prof || 'profile' in g.pref.compile_defines { + g.comptime_definitions.writeln('#define _VPROFILE (1)') + } if g.pref.autofree { g.comptime_definitions.writeln('#define _VAUTOFREE (1)') // g.comptime_definitions.writeln('unsigned char* g_cur_str;') diff --git a/vlib/v/gen/c/cmain.v b/vlib/v/gen/c/cmain.v index 04bf404aa..e80ff8a19 100644 --- a/vlib/v/gen/c/cmain.v +++ b/vlib/v/gen/c/cmain.v @@ -96,6 +96,9 @@ fn (mut g Gen) gen_c_main_header() { if g.pref.is_livemain { g.generate_hotcode_reloading_main_caller() } + if 'no_profile_startup' in g.pref.compile_defines { + g.writeln('vreset_profile_stats();') + } } pub fn (mut g Gen) gen_c_main_footer() { diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index 61cdd8f91..893343e0c 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -650,6 +650,9 @@ fn (mut g Gen) comptime_if_to_ifdef(name string, is_comptime_optional bool) ?str 'prod' { return '_VPROD' } + 'profile' { + return '_VPROFILE' + } 'test' { return '_VTEST' } diff --git a/vlib/v/gen/c/profile.v b/vlib/v/gen/c/profile.v index 602c7b050..5aa81c807 100644 --- a/vlib/v/gen/c/profile.v +++ b/vlib/v/gen/c/profile.v @@ -14,16 +14,17 @@ fn (mut g Gen) profile_fn(fn_decl ast.FnDecl) { return } fn_name := fn_decl.name - if fn_name.starts_with('time.vpc_now') { + if fn_name.starts_with('time.vpc_now') || fn_name.starts_with('v.profile.') { g.defer_profile_code = '' } else { measure_fn_name := if g.pref.os == .macos { 'time__vpc_now_darwin' } else { 'time__vpc_now' } fn_profile_counter_name := 'vpc_$g.last_fn_c_name' fn_profile_counter_name_calls := '${fn_profile_counter_name}_calls' g.writeln('') - g.writeln('\tdouble _PROF_FN_START = ${measure_fn_name}(); $fn_profile_counter_name_calls++; // $fn_name') + g.writeln('\tdouble _PROF_FN_START = ${measure_fn_name}();') + g.writeln('\tif(v__profile_enabled) { $fn_profile_counter_name_calls++; } // $fn_name') g.writeln('') - g.defer_profile_code = '\t$fn_profile_counter_name += ${measure_fn_name}() - _PROF_FN_START;' + g.defer_profile_code = '\tif(v__profile_enabled) { $fn_profile_counter_name += ${measure_fn_name}() - _PROF_FN_START; }' g.pcs_declarations.writeln('double $fn_profile_counter_name = 0.0; u64 $fn_profile_counter_name_calls = 0;') g.pcs << ProfileCounterMeta{ fn_name: g.last_fn_c_name @@ -49,4 +50,12 @@ pub fn (mut g Gen) gen_vprint_profile_stats() { g.pcs_declarations.writeln('\tfclose(fp);') } g.pcs_declarations.writeln('}') + g.pcs_declarations.writeln('') + g.pcs_declarations.writeln('void vreset_profile_stats(){') + for pc_meta in g.pcs { + g.pcs_declarations.writeln('\t$pc_meta.vpc_calls = 0;') + g.pcs_declarations.writeln('\t$pc_meta.vpc_name = 0.0;') + } + g.pcs_declarations.writeln('}') + g.pcs_declarations.writeln('') } diff --git a/vlib/v/preludes/profiled_program.v b/vlib/v/preludes/profiled_program.v index 66bb20946..ccf2bf4df 100644 --- a/vlib/v/preludes/profiled_program.v +++ b/vlib/v/preludes/profiled_program.v @@ -1,7 +1,8 @@ module main import time +import v.profile -const ( - profiled_program_time_used = time.seconds_per_minute -) +const profiled_program_time_used = time.seconds_per_minute + +const profiled_program_profile_used = profile.state() diff --git a/vlib/v/profile/api.v b/vlib/v/profile/api.v new file mode 100644 index 000000000..a0582d9b5 --- /dev/null +++ b/vlib/v/profile/api.v @@ -0,0 +1,13 @@ +[has_globals] +module profile + +__global v__profile_enabled = true + +pub fn state() bool { + return v__profile_enabled +} + +[if profile] +pub fn on(state bool) { + v__profile_enabled = state +} diff --git a/vlib/v/tests/profile/profile_test.v b/vlib/v/tests/profile/profile_test.v index f8c99b45a..8ef38e74a 100644 --- a/vlib/v/tests/profile/profile_test.v +++ b/vlib/v/tests/profile/profile_test.v @@ -21,3 +21,29 @@ fn test_v_profile_works() { assert res.output.contains(' println') assert res.output.contains(' strconv__atoi') } + +fn test_v_profile_on_off_api_works() { + os.chdir(vroot) or {} + program_source := os.join_path(vroot, 'vlib/v/tests/profile/profile_test_2.v') + res := os.execute('"$vexe" -profile - run $program_source') + // eprintln('res: $res') + assert res.exit_code == 0 + assert res.output.len > 0 + assert res.output.contains(' builtin_init') + assert res.output.contains(' main__main') + assert res.output.contains(' main__abc') + res_lines := res.output.split_into_lines() + abc_count := res_lines.filter(it.contains('main__abc'))[0].trim_space().all_before(' ').int() + assert abc_count == 1 + + // test that `-d no_profile_startup` *also* works: + res2 := os.execute('"$vexe" -d no_profile_startup -profile - run $program_source') + assert res2.exit_code == 0 + assert res2.output.len > 0 + assert !res2.output.contains(' builtin_init') + assert res2.output.contains(' main__main') + assert res2.output.contains(' main__abc') + res2_lines := res2.output.split_into_lines() + assert res_lines.len > res2_lines.len + // dump(res2_lines) +} diff --git a/vlib/v/tests/profile/profile_test_2.v b/vlib/v/tests/profile/profile_test_2.v new file mode 100644 index 000000000..0ba83b0f4 --- /dev/null +++ b/vlib/v/tests/profile/profile_test_2.v @@ -0,0 +1,23 @@ +import v.profile + +fn abc() { + eprintln(@FN) +} + +fn main() { + profile.on(false) + for _ in 0 .. 3 { + abc() + } + println('>>>>>>>>>>') + + profile.on(true) + abc() + profile.on(false) + + println('>>>>>>>>>>') + for _ in 0 .. 3 { + abc() + } + profile.on(true) +} -- 2.30.2