checker,cgen: implement v.profile.on/1, support `[if profile]` and `-d no_profile_startup` (#13232)
parent
9e0156b46a
commit
295156e054
|
@ -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.
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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;')
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
|
|
|
@ -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('')
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
Loading…
Reference in New Issue