简体   繁体   English

在PyV8中分析Javascript

[英]Profiling Javascript in PyV8

I have a JS codebase running within PyV8. 我有一个在PyV8中运行的JS代码库。 Now I'd like to improve its performance, but there don't seem to be any hooks to enable the V8 profiler. 现在,我想提高它的性能,但是似乎没有任何挂钩可以启用V8分析器。 In an older trunk version of PyV8 there are some options referencing the profiler but I don't find any documentation on it. 较旧的PyV8中继版本中,有一些选项引用了探查器,但我没有找到任何文档。 Do you have any idea on how to profile in PyV8 without me having to rewrite the Python-to-JS wrapper? 您是否有关于如何在PyV8中进行配置的想法,而无需重写Python-to-JS包装器?

Do you know of any JS-only-framework that uses monkey patching in order to generate a timing profile? 您是否知道使用猴子补丁程序生成时序配置文件的仅JS框架? It's not a big deal if there is some overhead involved - better than not having a profile at all. 如果涉及一些开销,这没什么大不了的-总比完全没有配置文件要好。

At the end I've found the hint I needed in the book 'Pro Javascript Design Patterns': Use a closure together with func.apply to apply instrumentation on functions. 最后,我在“ Pro Javascript Design Patterns”一书中找到了所需的提示:将闭包与func.apply一起使用,以对功能应用检测。 Unfortunately, the JS way of decorating functions is not quite as clean as Python's - but hey, it works and I get the information I need to drill down into the code's performance characteristics. 不幸的是,装饰函数的JS方式不像Python那样整洁-嘿,它可以工作,并且我获得了深入了解代码性能特征所需的信息。

profile.js 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 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 side 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

Output 输出量

"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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM