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
|
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.
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -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;')
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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'
|
||||||
}
|
}
|
||||||
|
|
|
@ -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('')
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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(' 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)
|
||||||
|
}
|
||||||
|
|
|
@ -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