checker,cgen: implement v.profile.on/1, support `[if profile]` and `-d no_profile_startup` (#13232)

pull/13240/head
Delyan Angelov 2022-01-21 03:26:05 +02:00 committed by GitHub
parent 9e0156b46a
commit 295156e054
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 97 additions and 7 deletions

View File

@ -84,8 +84,16 @@ NB: the build flags are shared with the run command too:
b) how much *nanoseconds in total* it took b) how much *nanoseconds in total* it took
c) an average for each function (i.e. (b) / (a) ) c) an average for each function (i.e. (b) / (a) )
d) the function name d) the function name
NB: if you want to output the profile info to stdout, use `-profile -`. 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 <limit> -message-limit <limit>
The maximum amount of warnings / errors / notices, that will be accumulated (defaults to 100). 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. The checker will abort prematurely once this limit has been reached.

View File

@ -26,7 +26,7 @@ const (
valid_comptime_if_cpu_features = ['x64', 'x32', 'little_endian', 'big_endian'] valid_comptime_if_cpu_features = ['x64', 'x32', 'little_endian', 'big_endian']
valid_comptime_if_other = ['apk', 'js', 'debug', 'prod', 'test', 'glibc', 'prealloc', valid_comptime_if_other = ['apk', 'js', 'debug', 'prod', 'test', 'glibc', 'prealloc',
'no_bounds_checking', 'freestanding', 'threads', 'js_node', 'js_browser', 'js_freestanding', '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() valid_comptime_not_user_defined = all_valid_comptime_idents()
array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice',
'sort', 'contains', 'index', 'wait', 'any', 'all', 'first', 'last', 'pop'] 'sort', 'contains', 'index', 'wait', 'any', 'all', 'first', 'last', 'pop']

View File

@ -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() } 'js' { return !c.pref.backend.is_js() }
'debug' { return !c.pref.is_debug } 'debug' { return !c.pref.is_debug }
'prod' { return !c.pref.is_prod } 'prod' { return !c.pref.is_prod }
'profile' { return !c.pref.is_prof }
'test' { return !c.pref.is_test } 'test' { return !c.pref.is_test }
'glibc' { return false } // TODO 'glibc' { return false } // TODO
'threads' { return c.table.gostmts == 0 } 'threads' { return c.table.gostmts == 0 }

View File

@ -703,6 +703,9 @@ pub fn (mut g Gen) init() {
if g.pref.is_test || 'test' in g.pref.compile_defines { if g.pref.is_test || 'test' in g.pref.compile_defines {
g.comptime_definitions.writeln('#define _VTEST (1)') 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 { if g.pref.autofree {
g.comptime_definitions.writeln('#define _VAUTOFREE (1)') g.comptime_definitions.writeln('#define _VAUTOFREE (1)')
// g.comptime_definitions.writeln('unsigned char* g_cur_str;') // g.comptime_definitions.writeln('unsigned char* g_cur_str;')

View File

@ -96,6 +96,9 @@ fn (mut g Gen) gen_c_main_header() {
if g.pref.is_livemain { if g.pref.is_livemain {
g.generate_hotcode_reloading_main_caller() 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() { pub fn (mut g Gen) gen_c_main_footer() {

View File

@ -650,6 +650,9 @@ fn (mut g Gen) comptime_if_to_ifdef(name string, is_comptime_optional bool) ?str
'prod' { 'prod' {
return '_VPROD' return '_VPROD'
} }
'profile' {
return '_VPROFILE'
}
'test' { 'test' {
return '_VTEST' return '_VTEST'
} }

View File

@ -14,16 +14,17 @@ fn (mut g Gen) profile_fn(fn_decl ast.FnDecl) {
return return
} }
fn_name := fn_decl.name 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 = '' g.defer_profile_code = ''
} else { } else {
measure_fn_name := if g.pref.os == .macos { 'time__vpc_now_darwin' } else { 'time__vpc_now' } 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 := 'vpc_$g.last_fn_c_name'
fn_profile_counter_name_calls := '${fn_profile_counter_name}_calls' fn_profile_counter_name_calls := '${fn_profile_counter_name}_calls'
g.writeln('') 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.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_declarations.writeln('double $fn_profile_counter_name = 0.0; u64 $fn_profile_counter_name_calls = 0;')
g.pcs << ProfileCounterMeta{ g.pcs << ProfileCounterMeta{
fn_name: g.last_fn_c_name 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('\tfclose(fp);')
} }
g.pcs_declarations.writeln('}') 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('')
} }

View File

@ -1,7 +1,8 @@
module main module main
import time import time
import v.profile
const ( const profiled_program_time_used = time.seconds_per_minute
profiled_program_time_used = time.seconds_per_minute
) const profiled_program_profile_used = profile.state()

View File

@ -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
}

View File

@ -21,3 +21,29 @@ fn test_v_profile_works() {
assert res.output.contains(' println') assert res.output.contains(' println')
assert res.output.contains(' strconv__atoi') 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)
}

View File

@ -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)
}