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
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 <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.

View File

@ -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']

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() }
'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 }

View File

@ -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;')

View File

@ -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() {

View File

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

View File

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

View File

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

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(' 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)
}