YUI 3: Profiler

The YUI Profiler is a simple, non-visual code profiler for JavaScript. Unlike most code profilers, this one allows you to specify exactly what parts of your application to profile. You can also programmatically retrieve profiling information as the application is running, allowing you to create performance tests YUI Test or other unit testing frameworks.

Getting Started

Include Dependencies

The easiest way to include the source files for Profiler and its dependencies is to add the YUI seed file to your page, using the following script tag, and allow the YUI instance to download any additional files which may be required:

  1. <script src="http://yui.yahooapis.com/3.0.0/build/yui/yui-min.js"></script>
<script src="http://yui.yahooapis.com/3.0.0/build/yui/yui-min.js"></script>

The YUI instance will automatically pull down Profiler's source files and any missing dependencies when the profiler module is used. This helps you avoid having to manually manage the list of files needed on your page to support multiple components while also optimizing your initial page weight by loading files only when they are required.

If you do want to include file dependencies manually on your page, the YUI Dependency Configurator can be used to determine the list of files you need to include in order to use Profiler.

The YUI Instance

Once you have the YUI seed file on your page (yui-min.js), you can create a new YUI instance for your application to use and populate it with the modules you need, specified as the first set of arguments to the use method:

  1. // Create new YUI instance, and populate it with the required modules
  2. YUI().use('profiler', function(Y) {
  3.  
  4. // Profiler available, and ready for use.
  5.  
  6. });
// Create new YUI instance, and populate it with the required modules
YUI().use('profiler', function(Y) {
 
    // Profiler available, and ready for use.
 
});

The last argument passed to use is a callback function. The callback function will be invoked as soon as the YUI instance is done downloading any required files missing from your page. Once those files are loaded, your local YUI instance will be supplemented with the classes which make up the profiler module and any modules it depends on. A reference to the populated YUI instance (Y) is passed back to your callback function. Within your callback, then, you can start writing your application code based on your own custom instance of YUI.

For more information on creating instances of YUI and the use method, see the YUI Global object documentation.

Profiling Functions

The simplest way to use is Profiler is to register a single function for profiling using the registerFunction() method. In order to register a function, it must exist on an object. Since global functions exist on the window object, they can be profiled; functions declared within other functions cannot be profiled unless assigned onto an object. If the function exists globally, then you can just pass in the fully-qualified name of the function:

  1. //global function
  2. function sayHi(){
  3. alert("hi");
  4. }
  5.  
  6. var myObject = {
  7. getName : function(){
  8. return "MyObject";
  9. }
  10. };
  11.  
  12. //create new instance and load profiler
  13. YUI().use("profiler", function(Y){
  14.  
  15. //register the global function for profiling - pass in window to indicate a global function
  16. Y.Profiler.registerFunction("sayHi", window);
  17.  
  18. //register method on a global object - no second argument needed
  19. Y.Profiler.registerFunction("myObject.getName");
  20.  
  21. //alternate - providing second argument doesn't hurt
  22. Y.Profiler.registerFunction("myObject.getName", myObject);
  23. });
  24.  
//global function
function sayHi(){
    alert("hi");
}
 
var myObject = {
    getName : function(){
        return "MyObject";
    }    
};
 
//create new instance and load profiler
YUI().use("profiler", function(Y){
 
    //register the global function for profiling - pass in window to indicate a global function
    Y.Profiler.registerFunction("sayHi", window);
 
    //register method on a global object - no second argument needed
    Y.Profiler.registerFunction("myObject.getName");
 
    //alternate - providing second argument doesn't hurt
    Y.Profiler.registerFunction("myObject.getName", myObject);                
});
 

In this example, there is a global function sayHi() and a global object myObject. These can both be profiled by calling the registerFunction() method. For sayHi(), the first argument is the name of the function and the second argument is the owner object, window. For the myObject.getName() method, the second argument is not necessary because the first argument contains the fully-qualified name of method. Since myObject exists globally, this string can be evaluated to get all of the information that the Profiler needs.

Once a function is registered for profiling, it can be called as usual. The Profiler can then be queried to retrieve information about any of the functions it is profiling. To retrieve information about a particular function, use any of the following methods:

  • getAverage(name) - returns the average amount of time (in milliseconds) that the function takes to complete.
  • getCallCount(name) - returns the number of times that the given function was called.
  • getMax(name) - returns the minimum amount of time (in milliseconds) that the function takes to complete.
  • getMin(name) - returns the maximum amount of time (in milliseconds) that the function takes to complete.
  • getReport(name) - returns an object containing all of the profiling information for the function.

Each of these methods accepts a single argument: the name of the function. This is the fully-qualified name that was used with registerFunction(). For example:

  1. //create new instance and load profiler
  2. YUI().use("profiler", function(Y){
  3.  
  4. //get the average amount of time it took sayHi() to run
  5. var average = Y.Profiler.getAverage("sayHi");
  6.  
  7. //get the number of times myObject.getName() was called
  8. var callCount = Y.Profiler.getCallCount("myObject.getName");
  9.  
  10. //get the full report for sayHi()
  11. var report = Y.Profiler.getReport("sayHi");
  12. });
  13.  
//create new instance and load profiler
YUI().use("profiler", function(Y){
 
    //get the average amount of time it took sayHi() to run
    var average = Y.Profiler.getAverage("sayHi");
 
    //get the number of times myObject.getName() was called
    var callCount = Y.Profiler.getCallCount("myObject.getName");
 
    //get the full report for sayHi()
    var report = Y.Profiler.getReport("sayHi");                
});                
 

When you are done profiling, you can unregister the functions by using unregisterFunction(), which undoes all of the profiling instrumentation and deletes all profiling data about the given function. Always make sure to retrieve the profiling data for functions before calling unregisterFunction(). To unregister a function, just pass in the same name that was passed into registerFunction(); no other information is necessary.

  1. //create new instance and load profiler
  2. YUI().use("profiler", function(Y){
  3. //unregister sayHi
  4. Y.Profiler.unregisterFunction("sayHi");
  5.  
  6. //unregister myObject.getName
  7. Y.Profiler.unregisterFunction("myObject.getName");
  8. });
  9.  
//create new instance and load profiler
YUI().use("profiler", function(Y){
    //unregister sayHi
    Y.Profiler.unregisterFunction("sayHi");
 
    //unregister myObject.getName
    Y.Profiler.unregisterFunction("myObject.getName");                                                    
});                
 

Profiling Anonymous Functions

Since scripts can consist of methods that aren't accessible via normal means, this represents a distinct challenge to the profiling process. The Profiler doesn't know about any functions that exist in private scopes or that aren't attached to other objects. Even though these can't be profiled automatically, you can use the instrument() method to create a version of any function that contains profiling instrumentation and will be tracked just as any other profiled method. Example:

  1. //create new instance and load profiler
  2. YUI().use("profiler", function(Y){
  3.  
  4. //create instrumented version of the function
  5. var instrumentedFunction = Y.Profiler.instrument("anonymous1", function(num1, num2){
  6. return num1 + num2;
  7. });
  8.  
  9. //call it
  10. instrumentedFunction(5, 10);
  11.  
  12. //get the report
  13. var report = Y.Profiler.getReport("anonymous1");
  14.  
  15. });
  16.  
//create new instance and load profiler
YUI().use("profiler", function(Y){
 
    //create instrumented version of the function
    var instrumentedFunction = Y.Profiler.instrument("anonymous1", function(num1, num2){
        return num1 + num2;
    });
 
    //call it
    instrumentedFunction(5, 10);
 
    //get the report
    var report = Y.Profiler.getReport("anonymous1");
 
});            
 

In this example, the instrument() method is used to create an instrumented version of an anonymous function. This function is given the name "anonymous1" so it can be referenced later. The instrumented function is returned from instrument() and is then called. The report for this function is retrieved using getReport(), just like any other profiled function. While not ideal, the instrument() method is useful if you need finer-grained profiling information.

Profiling Constructors

Profiling constructors is very similar to profiling functions, with the sole exception being the registration of all methods on the prototype for profiling as well. Registering a constructor means that all object instances created via that constructor are being profiled and the results are being aggregated into a single record. For example:

  1. //constructor
  2. function MyObject(name){
  3. this.name = name;
  4. }
  5.  
  6. MyObject.prototype.getName = function(){
  7. return this.name;
  8. };
  9.  
  10. MyObject.prototype.setName = function(name){
  11. this.name = name;
  12. };
  13.  
  14. //create new instance and load profiler
  15. YUI().use("profiler", function(Y){
  16. //register the constructor
  17. Y.Profiler.registerConstructor("MyObject", window);
  18.  
  19. //create some instances
  20. var o1 = new MyObject("Instance 1");
  21. var o2 = new MyObject("Instance 2");
  22. var o3 = new MyObject("Instance 3");
  23.  
  24. //make some calls
  25. var name = o1.getName();
  26. o2.setName("Another name");
  27. o1.setName("And another name");
  28.  
  29. //get the information
  30. var constructorCalls = Y.Profiler.getCallCount("MyObject"); //3
  31. var getNameCalls = Y.Profiler.getCallCount("MyObject.prototype.getName"); //1
  32. var setNameCalls = Y.Profiler.getCallCount("MyObject.prototype.setName"); //2
  33. });
  34.  
//constructor
function MyObject(name){
    this.name = name;
}
 
MyObject.prototype.getName = function(){
    return this.name;
};
 
MyObject.prototype.setName = function(name){
    this.name = name;
};
 
//create new instance and load profiler
YUI().use("profiler", function(Y){
    //register the constructor
    Y.Profiler.registerConstructor("MyObject", window);
 
    //create some instances
    var o1 = new MyObject("Instance 1");
    var o2 = new MyObject("Instance 2");
    var o3 = new MyObject("Instance 3");
 
    //make some calls
    var name = o1.getName();
    o2.setName("Another name");
    o1.setName("And another name");
 
    //get the information
    var constructorCalls = Y.Profiler.getCallCount("MyObject"); //3
    var getNameCalls = Y.Profiler.getCallCount("MyObject.prototype.getName"); //1
    var setNameCalls = Y.Profiler.getCallCount("MyObject.prototype.setName"); //2                                  
});                 
 

In this example, there is a global constructor MyObject that has two methods on its prototype. By registering the constructor, three entries are made in profiler, one for MyObject, one for MyObject.prototype.getName and one for MyObject.prototype.setName. When the constructor is used to create new object instances, the profiler automatically takes note and aggregates that information. Even though methods are called on individual instances, the data is still collected into one location.

Note: The Profiler cannot profile methods that are defined inside of the constructor. If you create objects that have methods defined in the constructor, it is better to create the instance and then use registerObject() on the instance.

When you are done profiling, you can unregister the constructor by using unregisterConstructor(), which undoes all of the profiling instrumentation and deletes all profiling data about the given constructor and all of its methods. To unregister a constructor, just pass in the same name that was passed into registerConstructor(); no other information is necessary.

  1. //create new instance and load profiler
  2. var Y = YUI().use("profiler", function(Y){
  3.  
  4. //unregister MyObject
  5. Y.Profiler.unregisterConstructor("MyObject");
  6. });
  7.  
//create new instance and load profiler
var Y = YUI().use("profiler", function(Y){
 
    //unregister MyObject
    Y.Profiler.unregisterConstructor("MyObject");                
});
 

Profiling Objects

When an object exists with multiple methods to be profiled, it may be faster to call registerObject(), which registers every method found on the object. This can be especially useful in the case of object literals and inheritance done without using prototypes. The first argument is the name of the object (its name in the profiler) while the second argument is the actual object. Each method is registered as objectName.methodName in the profiler. Example:

  1. //object
  2. var obj = {
  3.  
  4. add : function (num1, num2) {
  5. return num1 + num2;
  6. },
  7.  
  8. subtract : function (num1, num2){
  9. return num1 - num2;
  10. }
  11. };
  12.  
  13. //create new instance and load profiler
  14. YUI().use("profiler", function(Y){
  15. //register the object
  16. Y.Profiler.registerObject("obj", obj);
  17.  
  18. //use the methods
  19. var sum = obj.add(5, 10);
  20. var diff = obj.subtract(20, 12);
  21. var sum2 = obj.add(10, 40);
  22.  
  23. //get the information
  24. var addCalls = Y.Profiler.getCallCount("obj.add"); //2
  25. var subtractCalls = Y.Profiler.getCallCount("obj.subtract"); //1
  26. });
  27.  
//object
var obj = {
 
    add : function (num1, num2) {
        return num1 + num2;
    },
 
    subtract : function (num1, num2){
        return num1 - num2;
    }    
};
 
//create new instance and load profiler
YUI().use("profiler", function(Y){
    //register the object
    Y.Profiler.registerObject("obj", obj);
 
    //use the methods
    var sum = obj.add(5, 10);
    var diff = obj.subtract(20, 12);
    var sum2 = obj.add(10, 40);
 
    //get the information
    var addCalls = Y.Profiler.getCallCount("obj.add"); //2
    var subtractCalls = Y.Profiler.getCallCount("obj.subtract"); //1                   
});     
 

In this example, an object obj contains two methods, add() and subtract(). Both methods are registered when obj is passed into the registerObject() method. Information about the methods is then returned via getCallCount() by passing in the complete method names of obj.add and obj.subtract.

When you are done profiling, you can unregister the object by using unregisterObject(), which undoes all of the profiling instrumentation and deletes all profiling data about the given object and all of its methods. To unregister an object, just pass in the same name that was passed into registerObject(); no other information is necessary.

  1. //create new instance and load profiler
  2. var Y = YUI().use("profiler", function(Y){
  3.  
  4. //unregister MyObject
  5. Y.Profiler.unregisterObject("obj");
  6. });
  7.  
//create new instance and load profiler
var Y = YUI().use("profiler", function(Y){
 
    //unregister MyObject
    Y.Profiler.unregisterObject("obj");                
});
 

Reporting Results

If you'd like to get the results of all profiling, the getFullReport() method can be called. This method returns an object containing all of the profiling information for every registered function (the data for each function is destroyed when it's unregistered, so this method should be called before unregistering all functions). The getFullReport() method returns an object in the following format:

  1. {
  2. "function_name1": {
  3. calls : 0,
  4. avg : 0,
  5. max: 0,
  6. min: 0,
  7. points : []
  8. },
  9.  
  10. "function_name2": {
  11. calls : 0,
  12. avg : 0,
  13. max: 0,
  14. min: 0,
  15. points : []
  16. },
  17.  
  18. "function_name3": {
  19. calls : 0,
  20. avg : 0,
  21. max: 0,
  22. min: 0,
  23. points : []
  24. }
  25.  
  26. }
  27.  
{
    "function_name1": {
        calls : 0,
        avg : 0,
        max: 0,
        min: 0,
        points : []
    },
 
    "function_name2": {
        calls : 0,
        avg : 0,
        max: 0,
        min: 0,
        points : []
    },
 
    "function_name3": {
        calls : 0,
        avg : 0,
        max: 0,
        min: 0,
        points : []
    }
 
}
 

If you'd like to only return profiling information based on certain criteria, you can pass in an optional filter function to getFullReport(). This filter function receives a single argument, which is the report for an individual function. You can use this data to determine which data to include. The function should return true to include the data and false to ignore it. For example, to get a report for functions that were called at least once, the following can be used:

  1. //create new instance and load profiler
  2. var Y = YUI().use("profiler", function(Y){
  3.  
  4. //get report
  5. var report = Y.Profiler.getFullReport(function(report){
  6. return (report.calls > 0);
  7. });
  8. });
  9.  
//create new instance and load profiler
var Y = YUI().use("profiler", function(Y){
 
    //get report
    var report = Y.Profiler.getFullReport(function(report){
        return (report.calls > 0);
    });                
});
 

Using a filter produces an object in the same format as when the filter is not provided; the only difference is the set of functions included in the report.

Stopwatch Functionality

If you want to profile just a specific part of a function, you can do so using the stopwatch functionality that's built into the Profiler. The start() and stop() methods each take a single argument, which is a name that refers to the functionality being profiled. This data is stored in the Profiler along with all other data and can be retrieved using getReport() later on. For example:

  1. //create new instance and load profiler
  2. var Y = YUI().use("profiler", function(Y){
  3.  
  4. Y.Profiler.start("looptime");
  5.  
  6. for (var i=0; i < 100000; i++){
  7. }
  8.  
  9. Y.Profiler.stop("looptime");
  10.  
  11. //get report
  12. var report = Y.Profiler.getReport("looptime");
  13. });
  14.  
//create new instance and load profiler
var Y = YUI().use("profiler", function(Y){
 
    Y.Profiler.start("looptime");
 
    for (var i=0; i < 100000; i++){
    }
 
    Y.Profiler.stop("looptime");
 
    //get report
    var report = Y.Profiler.getReport("looptime");                
});
 

This code measures how long it takes to complete a loop that increments a single variable 100,000 times. The name of the Profiler entry is "looptime" and is used in both the start() and stop() methods. Once stop() is called, the data is written into the report and can be retrieved via getReport() in the usual way.

Known Limitations

Since the Profiler works from within JavaScript, there are some limitations:

  • Functions can only be profiled if they're attached to objects.
  • Functions called recursively using arguments.callee will not be profiled correctly. If possible, avoid using arguments.callee in favor of the fully-qualified function name.
  • In order for subclassing using YAHOO.lang.extend() to be profiled correctly, both the superclass constructor and the subclass constructor must be registered with the Profiler prior to the call.

YUI on Mobile: Using Profiler with "A-Grade" Mobile Browsers

About this Section: YUI generally works well with mobile browsers that are based on A-Grade browser foundations. For example, Nokia's N-series phones, including the N95, use a browser based on Webkit — the same foundation shared by Apple's Safari browser, which is found on the iPhone. The fundamental challenges in developing for this emerging class of full, A-Grade-derived browsers on handheld devices are:

  • Screen size: You have a much smaller canvas;
  • Input devices: Mobile devices generally do not have mouse input, and therefore are missing some or all mouse events (like mouseover);
  • Processor power: Mobile devices have slower processors that can more easily be saturated by JavaScript and DOM interactions — and processor usage affects things like battery life in ways that don't have analogues in desktop browsers;
  • Latency: Most mobile devices have a much higher latency on the network than do terrestrially networked PCs; this can make pages with many script, css or other types of external files load much more slowly.

There are other considerations, many of them device/browser specific (for example, current versions of the iPhone's Safari browser do not support Flash). The goal of these sections on YUI User's Guides is to provide you some preliminary insights about how specific components perform on this emerging class of mobile devices. Although we have not done exhaustive testing, and although these browsers are revving quickly and present a moving target, our goal is to provide some early, provisional advice to help you get started as you contemplate how your YUI-based application will render in the mobile world.

More Information:

The YUI Profiler works without any major issues on the Nokia N95 and Apple iPhone default browsers and we'd expect similar behavior on other A-Grade-based mobile browsers.

Support & Community

Forums & Blog

YUI 3 discussion forums are hosted on YUILibrary.com.

In addition, please visit the YUIBlog for updates and articles about the YUI Library written by the library's developers.

Filing Bugs & Feature Requests

The YUI Library's public bug tracking and feature request repositories are located on the YUILibrary.com site. Before filing new feature requests or bug reports, please review our reporting guidelines.

Copyright © 2010 Yahoo! Inc. All rights reserved. Copyright | Privacy Policy | Terms of Use | Job Openings