代码之家  ›  专栏  ›  技术社区  ›  Michel Müller

在PyV8中评测Javascript

  •  0
  • Michel Müller  · 技术社区  · 10 年前

    我有一个运行在PyV8中的JS代码库。现在我想改进它的性能,但似乎没有任何挂钩来启用V8探查器。在里面 an older trunk version of PyV8 有一些选项引用了分析器,但我没有找到任何相关文档。你知道如何在PyV8中进行评测,而不需要我重写Python到JS包装器吗?

    你知道有任何只使用JS的框架使用monkey补丁来生成时序配置文件吗?如果涉及到一些开销,这并不是什么大问题——总比根本没有个人资料要好。

    1 回复  |  直到 10 年前
        1
  •  0
  •   Michel Müller    10 年前

    最后,我在《Pro Javascript设计模式》一书中找到了我需要的提示:将闭包与 func.apply 对函数应用检测。不幸的是,JS修饰函数的方式并不像Python那样干净——但是,嘿,它是有效的,我得到了深入了解代码性能特征所需的信息。

    配置文件.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
    
    推荐文章