[翻译]High Performance JavaScript(030)

第十章  Tools  工具

 

    Having the right software is essential for identifying bottlenecks in both the loading and running of scripts. A number of browser vendors and large-scale websites have shared techniques and tools to help make the Web faster and more efficient. This chapter focuses on some of the free tools available for:

    当确定脚本加载和运行时的瓶颈所在时,合手的工具是必不可少的。许多浏览器厂商和大型网站分享了一些技术和工具,帮助开发者使网页更快,效率更高。本章关注于这些免费工具:

 

Profiling  性能分析

    Timing various functions and operations during script execution to identify areas for optimization

    在脚本运行期定时执行不同函数和操作,找出需要优化的部分

 

Network analysis  网络分析
    Examining the loading of images, stylesheets, and scripts and their effect on overall page load and rendering

    检查图片,样式表,和脚本的加载过程,汇报它们对整个页面加载和渲染的影响

 

    When a particular script or application is performing less than optimally, a profiler can help prioritize areas for optimization. This can get tricky because of the range of supported browsers, but many vendors now provide a profiler along with their debugging tools. In some cases, performance issues may be specific to a particular browser; other times, the symptoms may occur across multiple browsers. Keep in mind that the optimizations applied to one browser might benefit other browsers, but they might have the opposite effect as well. Rather than assuming which functions or operations are slow, profilers ensure that optimization time is spent on the slowest areas of the system that affect the most browsers.

    当一个特定的脚本或应用程序没有达到最优状态时,一个性能分析器有助于安排优化工作的先后次序。不过,因为浏览器支持的范围不同,这可能变得很麻烦,但许多厂商在他们的调试工具中提供了性能分析器。有些情况下,性能问题可能与特定浏览器有关,其他情况下,这些症状可能出现在多个浏览器。请记住,在一个浏览器上所进行的优化可能适用于其他浏览器,也可能产生相反的效果。性能分析工具确保优化工作花费在系统中最慢,影响大多数浏览器的地方,而不是去判定那些函数或操作缓慢。

 

    While the bulk of this chapter focuses on profiling tools, network analyzers can be highly effective in helping to ensure that scripts and pages are loading and running as quickly as possible. Before diving into tweaking code, you should be sure that all scripts and other assets are being loaded optimally. Image and stylesheet loading can affect the loading of scripts, depending on how many concurrent requests the browser allows and how many assets are being loaded.

    虽然本章大多数内容关注于性能分析工具,其实网络分析工具可以极大提高分析效率,以确保脚本和页面尽可能快地加载运行。在调整代码之前,您应该确保脚本和其他资源的加载过程已经优化过了。图片和样式表加载会影响脚本加载,这取决于浏览器允许多少并发请求,有多少资源需要加载。

 

    Some of these tools provide tips on how to improve the performance of web pages. Keep in mind that the best way to interpret the information these tools provide is to learn more about the rationale behind the rules. As with most rules, there are exceptions, and a deeper understanding of the rules allows you to know when to break them.

    这里的一些工具提供了如何优化网页性能的秘诀。请记住,要充分利用这些工具所提供的信息,首先要深入了解这些规则背后的理由。正如大多数规则一样,总会有例外发生,深入理解这些规则使得您知道什么情况下应当突破规则。

 

JavaScript Profiling  JavaScript性能分析

 

    The tool that comes with all JavaScript implementations is the language itself. Using the Date object, a measurement can be taken at any given point in a script. Before other tools existed, this was a common way to time script execution, and it is still occasionally useful. By default the Date object returns the current time, and subtracting one Date instance from another gives the elapsed time in milliseconds. Consider the following example, which compares creating elements from scratch with cloning from an existing element (see Chapter 3, DOM Scripting):

    此工具与所有JavaScript实例与生俱来,正是语言自身。使用Data对象可以测量脚本的任何部分。在其它工具出现之前,测试脚本运行时间是一种常用手段,现在仍然会不时用到。通常使用Data返回当前时间,然后减去另一个Data值以得到以毫秒为单位的时间差。考虑下面的例子,它比较创建新元素和克隆已有元素所用的时间(参见第三章,DOM编程):

 

var start = new Date(),
    count = 10000,
    i, element, time;


for (i = 0; i < count; i++) {
  element = document.createElement('div');
}


time = new Date() - start;
alert('created ' + count + ' in ' + time + 'ms');


start = new Date();
for (i = 0, i < count; i++) {
  element = element.cloneNode(false);
}


time = new Date() - start;
alert('created ' + count + ' in ' + time + 'ms');

 

    This type of profiling is cumbersome to implement, as it requires manually instrumenting your own timing code. A Timer object that handles the time calculations and stores the data would be a good next step.

    此类性能分析十分繁琐,它需要手动添加定时器代码。可定义一个Timer对象处理时间计算并存放那些下一步会用到的时间值。

 

Var Timer = {
  _data: {},


  start: function(key) {
    Timer._data[key] = new Date();
  },


  stop: function(key) {
    var time = Timer._data[key];
    if (time) {
      Timer._data[key] = new Date() - time;
    }
  },


  getTime: function(key) {
    return Timer._data[key];
  }
};


Timer.start('createElement');
for (i = 0; i < count; i++) {
  element = document.createElement('div');
}


Timer.stop('createElement');
alert('created ' + count + ' in ' + Timer.getTime('createElement');

    As you can see, this still requires manual instrumentation, but provides a pattern for building a pure JavaScript profiler. By extending the Timer object concept, a profiler can be constructed that registers functions and instruments them with timing code.

    正如你看到的,这样做仍需要手工添加代码,但提供了一个建立纯JavaScript性能分析的模式。通过扩展Timer对象的概念,一个性能分析工具可以在构造时注册函数并在计时代码中调用它们。

 

YUI Profiler  YUI分析器

 

    The YUI Profiler (http://developer.yahoo.com/yui/profiler/), contributed by Nicholas Zakas, is a JavaScript profiler written in JavaScript. In addition to timer functionality, it provides interfaces for profiling functions, objects, and constructors, as well as detailed reports of the profile data. It enables profiling across various browsers and data exporting for more robust reporting and analysis.

    YUI分析器(http://developer.yahoo.com/yui/profiler/),由Nicholas Zakas提供,是用JavaScript编写的JavaScript分析器。除了计时功能,它还提供了用于函数、对象、和构造器的性能分析接口,还包括性能分析数据的详细报告。它可以跨浏览器工作,其输出数据可提供更强大的报告和分析。

 

    The YUI Profiler provides a generic timer that collects performance data. Profiler provides static methods for starting and stopping named timings and retrieving profile data.

    YUI分析器提供一个通用定时器用于收集性能数据。Profiler提供一些静态函数,用于启动和停止命名定时器,以及获取性能数据。

 

var count = 10000, i, element;
Y.Profiler.start('createElement');


for (i = 0; i < count; i++) {
  element = document.createElement('div');
}


Y.Profiler.stop('createElement');
alert('created ' + count + ' in ' + Y.Profiler.getAverage('createElement') + 'ms');

    This clearly improves upon the inline Date and Timer approach and provides additional profile data regarding the number of times called, as well as the average, minimum, and maximum times. This data is collected and can be analyzed alongside other profile results.

    很明显,它改进了内联Data和Timer方法,提供额外的性能数据包括调用次数,平均时间,最小时间,最大时间等。这些数据收集起来可以与其他测试结果综合分析。

 

    Functions can be registered for profiling as well. The registered function is instrumented with code that collects performance data. For example, to profile the global initUI method from Chapter 2, all that is required is the name:

    函数分析只需要注册一下。注册的函数被收集性能数据的代码调用。例如,要分析第二章提到的全局initUI方法,仅仅需要传入它的名字:

 

Y.Profiler.registerFunction("initUI");

    Many functions are bound to objects in order to prevent pollution of the global namespace. Object methods can be registered by passing the object in as the second argument to registerFunction. For example, assume an object called uiTest that implements two initUI approaches as uiTest.test1 and uiTest.test2. Each can be registered individually:

    许多函数是与对象绑定的,以防止污染全局命名空间。对象方法也可以通过reguisterFunction注册,只要将对象作为第二个参数传入。例如,假设一个称作uiTest的对象实现了两个方法,分别为uiTest.test1和uiTest.test2,每个方法都可以独立注册:

 

Y.Profiler.registerFunction("test1", uiTest);
Y.Profiler.registerFunction("test2", uiTest);

    This works well enough, but doesn't really scale for profiling many functions or an entire application. The registerObject method automatically registers every method bound to the object:

    一切正常,但还是不能真正测量多个函数或整个应用程序。registerObject方法自动注册绑定到对象的每一个方法:

 

Y.Profiler.registerObject("uiTest", uiTest); 

    The first argument is the name of the object (for reporting purposes), and the second is the object itself. This will instrument profiling for all of the uiTest methods.

    第一个参数是对象的名字(用于报告),第二个参数是对象本身。它将分析uiTest的所有方法。

 

    Objects that rely on prototype inheritance need special handling. YUI's profiler allows the registration of a constructor function that will instrument all methods on all instances of the object:

    那些从原形继承的对象需要特殊处理。YUI分析工具允许注册构造器函数,它可以调用对象的所有实例中的所有方法:

 

Y.Profiler.registerConstructor("MyWidget", myNameSpace);

    Now every function on each instance of myNameSpace.MyWidget will be measured and reported on. An individual report can be retrieved as an object:

    现在,所有myNameSpace.MyWidget实例的每个函数都将被测量并记入报告。一个独立的报告可像获取对象那样获取:

 

var initUIReport = Y.Profiler.getReport("initUI");

    This provides an object containing the profile data, including an array of points, which are the timings for each call, in the order they were called. These points can be plotted and analyzed in other interesting ways to examine the variations in time. This object has the following fields:

    这样得到一个包含分析数据的对象,它包含一个由时间点构成的数组,它们按照调用顺序排列。这些时间点可用于绘图或者用其他感兴趣的方法进行分析,以检查时间上的变化。这个对象具有如下字段:

 

{
  min: 100,
  max: 250,
  calls: 5,
  avg: 120,
  points: [100, 200, 250, 110, 100]
};

    Sometimes you may want only the value of a particular field. Static Profiler methods provide discrete data per function or method:

    有时您只关心其中的某些字段。静态Profiler方法提供每个函数或方法的离散数据:

 

var uiTest1Report = {
  calls: Y.Profiler.getCalls("uiTest.test1"),
  avg: Y.Profiler.getAvg("uiTest.test1")
};

    A view that highlights the slowest areas of the code is really what is needed in order to properly analyze a script's performance. A report of all registered functions called on the object or constructor is also available:

    一个视图高亮显示出代码中最慢的部分,那也是真正需要分析脚本性能的地方。另外一个功能可报告出对象或构造器所调用的所有已注册的函数:

 

var uiTestReport = Y.Profiler.getReport("uiTest");

    This returns an object with the following data:

    它返回的对象包含如下数据:

 

{
  test1: {
    min: 100,
    max: 250,
    calls: 10,
    avg: 120
  },
  test2:
    min: 80,
    max: 210,
    calls: 10,
    avg: 90
  }
};

    This provides the opportunity to sort and view the data in more meaningful ways, allowing the slower areas of the code to be scrutinized more closely. A full report of all of the current profile data can also be generated. This, however, may contain useless information, such as functions that were called zero times or that are already meeting performance expectations. In order to minimize this type of noise, an optional function can be passed in to filter the data:

    还可以排序以及采用更有意义的方法察看数据,使代码中速度慢的部分得到更密切的检查。获得所有分析数据的完整报告会包含许多无用信息,诸如那些调用次数为零的函数,或者那些性能已经达到预期指标的函数。为降低这些干扰,可传入一个选择函数来过滤这些数据:

 

var fullReport = Y.Profiler.getFullReport(function(data) {
  return (data.calls > 0 && data.avg > 5);
};

    The Boolean value returned will indicate whether the function should be included in the report, allowing the less interesting data to be suppressed.

    其返回的布尔值用于指出该函数是否应当加入到报告之中,让不感兴趣的数据被抑制掉。

 

    When finished profiling, functions, objects, and constructors can be unregistered individually, clearing the profile data:

    当分析完成后,函数,对象,还有构造器应当分别注销,清理分析数据:

 

Y.Profiler.unregisterFunction("initUI");
Y.Profiler.unregisterObject("uiTests");
Y.Profiler.unregisterConstructor("MyWidget");

    The clear() method keeps the current profile registry but clears the associated data. This function can be called individually per function or timing:

    clear()方法保留当前分析目标的注册状态,但清除相关数据。此函数可在每个函数或计时中单独调用:

 

Y.Profiler.clear("initUI");

    Or all data may be cleared at once by omitting the name argument:

    如果不传参数,那么所有数据都会被一次性清理:

 

Y.Profiler.clear();

    Because it is in JSON format, the profile report data can be viewed in any number of ways. The simplest way to view it is on a web page by outputting as HTML. It can also be sent to a server, where it can be stored in a database for more robust reporting. This is especially useful when comparing various optimization techniques across browsers.

    因为它使用JSON格式,所以分析报告有多种察看方法。最简单的办法就是在网页上输出为HTML。还可以将它发送到服务器,存入数据库,以实现更强大的报告功能。特别当比较不同的跨浏览器优化技术时特别有用。

 

    It is worth noting that anonymous functions are especially troublesome for this type of profiler because there is no name to report with. The YUI Profiler provides a mechanism for instrumenting anonymous functions, allowing them to be profiled. Registering an anonymous function returns a wrapper function that can be called instead of the anonymous function:

    没有什么比匿名函数更难以分析了,因为它们没有名字。YUI分析器提供了一种调用匿名函数的机制,使得它们可以被分析。注册一个匿名函数会返回一个封装函数,可以调用它而不是调用匿名函数:

 

var instrumentedFunction =
  Y.Profiler.instrument("anonymous1", function(num1, num2){
    return num1 + num2;
  });
instrumentedFunction(3, 5);

    This adds the data for the anonymous function to the Profiler's result set, allowing it to be retrieved in the same manner as other profile data:

    它将匿名函数的数据添加到Profiler的返回集中,获取它的方式与其他分析数据相同:

 

var report = Y.Profiler.getReport("anonymous1");

 

Anonymous Functions  匿名函数

 

    Depending on the profiler, some data can be obscured by the use of anonymous functions or function assignments. As this is a common pattern in JavaScript, many of the functions being profiled may be anonymous, making it difficult or impossible to measure and analyze. The best way to enable profiling of anonymous functions is to name them. Using pointers to object methods rather than closures will allow the broadest possible profile coverage.

    使用匿名函数或函数分配会造成分析器的数据模糊。由于这是JavaScript的通用模式,许多被分析的函数可能是匿名的,对它们测量和分析很困难或根本无法进行。分析匿名函数的最佳办法是给它们取个名字。使用指针指向对象方法而不是闭包,可以实现最广泛的分析覆盖。

 

    Compare using an inline function:

    比较两种方法,其中一个使用内联函数:

 

myNode.onclick = function() {
  myApp.loadData();
};

    with a method call:

    另一个使用方法调用:

 

myApp._onClick = function() {
  myApp.loadData();
};
myNode.onclick = myApp._onClick;

    Using the method call allows any of the reviewed profilers to automatically instrument the onclick handler. This is not always practical, as it may require significant refactoring in order to enable profiling.

    使用函数调用可使回顾式分析器自动调用onclick句柄。这不总是一种实用的方法,因为它可能需要对代码进行大量重构:

 

    For profilers that automatically instrument anonymous functions, adding an inline name makes the reports more readable:

    为了让分析器能够自动调用匿名函数,添加一个内联名称使报告更加可读:

 

myNode.onclick = function myNodeClickHandler() {
  myApp.loadData();
};

    This also works with functions declared as variables, which some profilers have trouble gleaning a name from:

    当函数被定义为变量时也可使用这种方法,有些分析器在拾取名称时会遇到麻烦:

 

var onClick = function myNodeClickHandler() {
  myApp.loadData();
};

    The anonymous function is now named, providing most profilers with something meaningful to display along with the profile results. These names require little effort to implement, and can even be inserted automatically as part of a debug build process.

    此匿名函数现在被命名了,使大多数分析器的分析结果显示出有意义的内容。这些命名工作几乎不需要什么工作量,而且可以用开发调试工具自动插入。

阅读更多
个人分类: 计算机技术
上一篇[翻译]High Performance JavaScript(029)
下一篇[翻译]High Performance JavaScript(031)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭