0

我有一个在 PyV8 中运行的 JS 代码库。现在我想提高它的性能,但似乎没有任何钩子可以启用 V8 分析器。在较旧的 PyV8 主干版本中,有一些选项引用分析器,但我没有找到任何文档。您是否知道如何在 PyV8 中进行分析而无需我重写 Python-to-JS 包装器?

你知道任何使用猴子补丁来生成时序配置文件的纯 JS 框架吗?如果涉及一些开销,这没什么大不了的 - 总比没有配置文件要好。

4

1 回答 1

0

最后,我在“Pro Javascript Design Patterns”一书中找到了我需要的提示:使用闭包以及func.apply在函数上应用检测。不幸的是,JS 装饰函数的方式不像 Python 那样干净——但是,它可以工作,我得到了深入了解代码性能特征所需的信息。

profile.js

function mod_profiler() {
    var profile_by_function_name = {};
    var total_time = 0;
    var time_counting_for_function = null;

    function get_function_name(func) {
        var result = func.toString();
        result = result.substr('function '.length);
        result = result.substr(0, result.indexOf('('));
        return result;
    }

    function update_profile(function_name, elapsed_time) {
        var profile = profile_by_function_name[function_name];
        if (profile === undefined) {
            profile = {calls:0, elapsed_time:0};
            profile_by_function_name[function_name] = profile;
        }
        profile.calls += 1;
        profile.elapsed_time += elapsed_time;
        if (time_counting_for_function === function_name) {
            total_time += elapsed_time;
        }
    }

    function profile(func) {
        function profiled() {
            var function_name = get_function_name(func);
            if (time_counting_for_function === null) {
                time_counting_for_function = function_name;
            }
            var start_time = new Date().getTime()
            var result = func.apply(undefined, arguments);
            var elapsed_time = new Date().getTime() - start_time;
            update_profile(function_name, elapsed_time);
            if (time_counting_for_function === function_name) {
                time_counting_for_function = null;
            }
            return result;
        }
        return profiled;
    }

    function get_formatted_result() {
        function get_whitespace(length) {
            var result = "";
            for (var i = 0; i < length; i++) {
                result += " ";
            }
            return result;
        }

        var function_names = Object.keys(profile_by_function_name);
        var function_names_sorted_by_elapsed_time = function_names.sort(function (a,b) {
            var elapsed_a = profile_by_function_name[a].elapsed_time;
            var elapsed_b = profile_by_function_name[b].elapsed_time;
            if (elapsed_a < elapsed_b) {
                return 1;
            }
            if (elapsed_a > elapsed_b) {
                return -1;
            }
            return 0;
        });
        var max_name_length = 0;
        for (var i = 0; i < function_names_sorted_by_elapsed_time.length; i++) {
            if (function_names_sorted_by_elapsed_time[i].length > max_name_length) {
                max_name_length = function_names_sorted_by_elapsed_time[i].length;
            }
        }
        var result = "\n" + get_whitespace(max_name_length) + " " + "#calls\telapsed\t% of total\n";
        for (var i = 0; i < function_names_sorted_by_elapsed_time.length; i++) {
            if (total_time === 0) {
                break;
            }
            var function_name = function_names_sorted_by_elapsed_time[i];
            var percentage_elapsed = profile_by_function_name[function_name].elapsed_time * 100 / total_time;
            if (percentage_elapsed < 0.3) {
                break;
            }
            result += function_name + ":" + get_whitespace(max_name_length - 1 - function_name.length) + profile_by_function_name[function_name].calls + "\t" + profile_by_function_name[function_name].elapsed_time + "\t" + percentage_elapsed.toFixed(2) + "\n";
        }
        result += "==========\n";
        result += "total time accounted for [ms]: " + total_time;
        return result;
    }

    return {
        profile: profile,
        get_formatted_result: get_formatted_result
    }
}

my_module_1.js

function mod_1(profiler_param) {
    var profiler = profiler_param;

    function my_private_func() {
        return "hello world2";
    }
    if (typeof(profiler) === 'object' && profiler !== null) {
    render_user_menu = profiler.profile(render_user_menu);
} //private functions need the instrumentation to be added manually or else they're not included in the profiling.

    function my_public_func1() {
        return "hello world";
    }

    function my_public_func2(input1, input2) {
        return my_private_func() + input1 + input2;
    }

    //public functions get the instrumentations automatically as long as they're called from outside the module
    var public_function_by_names = {
        my_public_func1: my_public_func1
        my_public_func2: my_public_func2
    }

    var result = {};
    var public_function_names = Object.keys(public_function_by_names);
    for (var i = 0; i < public_function_names.length; i++) {
        var func = public_function_by_names[public_function_names[i]];
        if (typeof(profiler) === 'object' && profiler !== null) {
            result[public_function_names[i]] = profiler.profile(func);
        }
        else {
            result[public_function_names[i]] = func;
        }
    }
    return result;
}

PyV8 端

with X4GEJSContext(extensions=['profile', 'my_module_1']) as ctx:
    if self.enable_profiling == True:
    ctx.eval("var profiler = mod_profiler();")
        ctx.eval("var mod1 = mod_1(profiler);")
        #note: you can pass profiler to as many modules as you want and they get instrumented together.
        logging.info(ctx.eval("mod1.my_public_func_1() + mod1.my_public_func_2('a', 3);"))
        logging.info(ctx.eval("profiler.get_formatted_result();"))
    else:
        ctx.eval("var mod1 = mod_1();") #it still works without the profiler

输出

"hello worldhelloworld2a3"
                                                                                 #calls elapsed % of total
  my_public_func1:                                                                 1    31  50.00
  my_public_func2:                                                                 1    31  50.00
  my_private_func:                                                                 1    31  50.00

  ==========
  total time accounted for [ms]: 62
于 2014-03-16T20:23:00.513 回答