Enhancements on wasm function execution time statistic (#2985)

Enhance the statistic of wasm function execution time, or the performance
profiling feature:
- Add os_time_thread_cputime_us() to get the cputime of a thread,
  and use it to calculate the execution time of a wasm function
- Support the statistic of the children execution time of a function,
  and dump it in wasm_runtime_dump_perf_profiling
- Expose two APIs:
  wasm_runtime_sum_wasm_exec_time
  wasm_runtime_get_wasm_func_exec_time

And rename os_time_get_boot_microsecond to os_time_get_boot_us.
This commit is contained in:
liang.he
2024-01-17 09:51:54 +08:00
committed by GitHub
parent 25ccc9f2d5
commit 5c8b8a17a6
24 changed files with 336 additions and 86 deletions

View File

@ -2392,53 +2392,103 @@ wasm_call_function(WASMExecEnv *exec_env, WASMFunctionInstance *function,
return !wasm_copy_exception(module_inst, NULL);
}
#if WASM_ENABLE_PERF_PROFILING != 0
void
wasm_dump_perf_profiling(const WASMModuleInstance *module_inst)
#if WASM_ENABLE_PERF_PROFILING != 0 || WASM_ENABLE_DUMP_CALL_STACK != 0
/* look for the function name */
static char *
get_func_name_from_index(const WASMModuleInstance *inst, uint32 func_index)
{
WASMExportFuncInstance *export_func;
WASMFunctionInstance *func_inst;
char *func_name;
uint32 i, j;
char *func_name = NULL;
WASMFunctionInstance *func_inst = inst->e->functions + func_index;
os_printf("Performance profiler data:\n");
for (i = 0; i < module_inst->e->function_count; i++) {
func_inst = module_inst->e->functions + i;
if (func_inst->is_import_func) {
func_name = func_inst->u.func_import->field_name;
}
if (func_inst->is_import_func) {
func_name = func_inst->u.func_import->field_name;
}
else {
#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0
else if (func_inst->u.func->field_name) {
func_name = func_inst->u.func->field_name;
}
func_name = func_inst->u.func->field_name;
#endif
else {
func_name = NULL;
for (j = 0; j < module_inst->export_func_count; j++) {
export_func = module_inst->export_functions + j;
/* if custom name section is not generated,
search symbols from export table */
if (!func_name) {
unsigned j;
for (j = 0; j < inst->export_func_count; j++) {
WASMExportFuncInstance *export_func =
inst->export_functions + j;
if (export_func->function == func_inst) {
func_name = export_func->name;
break;
}
}
}
}
return func_name;
}
#endif /*WASM_ENABLE_PERF_PROFILING != 0 || WASM_ENABLE_DUMP_CALL_STACK != 0*/
#if WASM_ENABLE_PERF_PROFILING != 0
void
wasm_dump_perf_profiling(const WASMModuleInstance *module_inst)
{
WASMFunctionInstance *func_inst;
char *func_name;
uint32 i;
os_printf("Performance profiler data:\n");
for (i = 0; i < module_inst->e->function_count; i++) {
func_inst = module_inst->e->functions + i;
if (func_inst->total_exec_cnt == 0)
continue;
func_name = get_func_name_from_index(module_inst, i);
if (func_name)
os_printf(
" func %s, execution time: %.3f ms, execution count: %" PRIu32
" times\n",
func_name,
module_inst->e->functions[i].total_exec_time / 1000.0f,
module_inst->e->functions[i].total_exec_cnt);
" times, children execution time: %.3f ms\n",
func_name, func_inst->total_exec_time / 1000.0f,
func_inst->total_exec_cnt,
func_inst->children_exec_time / 1000.0f);
else
os_printf(" func %" PRIu32
", execution time: %.3f ms, execution count: %" PRIu32
" times\n",
i, module_inst->e->functions[i].total_exec_time / 1000.0f,
module_inst->e->functions[i].total_exec_cnt);
" times, children execution time: %.3f ms\n",
i, func_inst->total_exec_time / 1000.0f,
func_inst->total_exec_cnt,
func_inst->children_exec_time / 1000.0f);
}
}
#endif
double
wasm_summarize_wasm_execute_time(const WASMModuleInstance *inst)
{
double ret = 0;
unsigned i;
for (i = 0; i < inst->e->function_count; i++) {
WASMFunctionInstance *func = inst->e->functions + i;
ret += (func->total_exec_time - func->children_exec_time) / 1000.0f;
}
return ret;
}
double
wasm_get_wasm_func_exec_time(const WASMModuleInstance *inst,
const char *func_name)
{
unsigned i;
for (i = 0; i < inst->e->function_count; i++) {
char *name_in_wasm = get_func_name_from_index(inst, i);
if (name_in_wasm && strcmp(name_in_wasm, func_name) == 0) {
WASMFunctionInstance *func = inst->e->functions + i;
return (func->total_exec_time - func->children_exec_time) / 1000.0f;
}
}
return -1.0;
}
#endif /*WASM_ENABLE_PERF_PROFILING != 0*/
uint32
wasm_module_malloc_internal(WASMModuleInstance *module_inst,
@ -2933,29 +2983,7 @@ wasm_interp_create_call_stack(struct WASMExecEnv *exec_env)
frame.func_offset = (uint32)(cur_frame->ip - func_code_base);
}
/* look for the function name */
if (func_inst->is_import_func) {
func_name = func_inst->u.func_import->field_name;
}
else {
#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0
func_name = func_inst->u.func->field_name;
#endif
/* if custom name section is not generated,
search symbols from export table */
if (!func_name) {
uint32 i;
for (i = 0; i < module_inst->export_func_count; i++) {
WASMExportFuncInstance *export_func =
module_inst->export_functions + i;
if (export_func->function == func_inst) {
func_name = export_func->name;
break;
}
}
}
}
func_name = get_func_name_from_index(module_inst, frame.func_index);
frame.func_name_wp = func_name;
if (!bh_vector_append(module_inst->frames, &frame)) {
@ -3402,7 +3430,7 @@ llvm_jit_alloc_frame(WASMExecEnv *exec_env, uint32 func_index)
frame->ip = NULL;
frame->sp = frame->lp;
#if WASM_ENABLE_PERF_PROFILING != 0
frame->time_started = os_time_get_boot_microsecond();
frame->time_started = os_time_thread_cputime_us();
#endif
frame->prev_frame = wasm_exec_env_get_cur_frame(exec_env);
wasm_exec_env_set_cur_frame(exec_env, frame);
@ -3424,8 +3452,13 @@ llvm_jit_free_frame(WASMExecEnv *exec_env)
#if WASM_ENABLE_PERF_PROFILING != 0
if (frame->function) {
frame->function->total_exec_time +=
os_time_get_boot_microsecond() - frame->time_started;
os_time_thread_cputime_us() - frame->time_started;
frame->function->total_exec_cnt++;
/* parent function */
if (prev_frame)
prev_frame->function->children_exec_time =
frame->function->total_exec_time;
}
#endif
wasm_exec_env_free_wasm_frame(exec_env, frame);