在PyV8中分析Javascript

Profiling Javascript in PyV8

本文关键字:Javascript PyV8      更新时间:2023-09-26

我有一个JS代码库在PyV8中运行。现在我想提高它的性能,但似乎没有任何钩子来启用V8分析器。在PyV8的旧主干版本中,有一些引用分析器的选项,但我没有找到任何关于它的文档。你有关于如何在PyV8配置文件没有我重写python到js包装的想法吗?

你知道有哪个js -only框架使用猴子补丁来生成时序配置文件吗?如果涉及到一些开销,这不是什么大问题——总比根本没有配置文件要好。

最后我找到了我在《Pro Javascript设计模式》一书中需要的提示:使用闭包和func.apply在函数上应用instrumentation。不幸的是,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;
}
<<p> PyV8一边/strong>
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