Firefox Developer Tools

JavaScript Profiler

使用Profiler工具找到你的JavaScript代码的瓶颈. Profiler会定期统计JavaScript样本的堆栈信息.

你可以通过在Web Developer菜单下选择Profiler来启动它. 在Linux和OS X下,你可以在Tools菜单下找到Web Developer,而在windows下,Web Developer则在FIrefox菜单下.



JavaScript Profiler是一个抽样分析器. 这意味着它会定期对JavaScript引擎的状态取样, 并且记录取样时代码运行的堆栈信息. 统计学上, 我们运行可接受样本数量的函数,花费的时间相当于浏览器运行它的时间,所以你可以很好的从你的代码里找到瓶颈。


function doSomething() {
  var x = getTheValue();
  x = x + 1;                   // -> sample A
function getTheValue() {
  return 5;
function logTheValue(x) {
 console.log(x);               // -> sample B, sample C

Suppose we run this program with the profiler active, and in the time it takes to run, the profiler takes three samples, as indicated in the inline comments above.

They're all taken from inside doSomething(), but the second two are inside the logTheValue() function called by doSomething(). So the profile would consist of three stack traces, like this:

Sample A: doSomething()
Sample B: doSomething() > logTheValue()
Sample C: doSomething() > logTheValue()

This obviously isn't enough data to tell us anything, but with a lot more samples we might be able to conclude that logTheValue() is the bottleneck in our code.

Creating a profile


Once you've clicked "Stop", the new profile will open automatically:

This pane's divided into two parts:

  • The left-hand side lists all the profiles you've captured and allows you to load each one. Just above this there are two buttons: the stopwatch button allows you to record a new profile while the Import... button allows you to import previously saved data. When profile is selected, you can save its data as a JSON file by clicking the Save button.
  • The right-hand side displays the currently loaded profile.

Analyzing a profile

The profile is split into two parts:

Profile timeline

The profile timeline occupies the top part of the profile display:

The horizontal axis is time, and the vertical axis is call stack size at that sample. Call stack represents the amount of active functions at the time when the sample was taken.

Red samples along the chart indicate the browser was unresponsive during that time, and a user would notice pauses in animations and responsiveness. If a profile has red samples, consider breaking this code up into several events, and look into using requestAnimationFrame and Workers.

You can examine a specific range within the profile by clicking and dragging inside the timeline:

A new button then appears above the timeline labeled "Sample Range [AAA, BBB]". Clicking that button zooms the profile, and the details view underneath it, to that timeslice:

Profile details

The profile details occupy the bottom part of the profile display:

When you first open a new sample, the sample pane contains a single row labeled "(total)", as in the screenshot above. If you click the arrow next to "(total)", you'll see a list of all the top-level functions which appear in a sample.

Running time shows the total number of samples in which this function appeared1, followed by the percentage of all samples in the profile in which this function appeared. The first row above shows that there were 2021 samples in the entire profile, and the second row shows that 1914, or 94.7%, of them were inside the detectImage() function.

Self shows the number of samples in which the sample was taken while executing this function itself, and not a function it was calling. In the simple example above, doSomething() would have a Running time of 3 (samples A, B, and C), but a Self value of 1 (sample A).

The third column gives the name of the function along with the filename and line number (for local functions) or basename and domain name (for remote functions). Functions in gray are built-in browser functions: functions in black represent JavaScript loaded by the page. If you hover the mouse over a row you'll see an arrow to the right of the function's identifier: click the arrow and you'll be taken to the function source.

Expanding the call tree

In a given row, if there are any samples taken while we were in a function called by this function (that is, if Running Time is greater than Self for a given row) then an arrow appears to the left of the function's name, enabling you to expand the call tree.

In the simple example above, the fully-expanded call tree would look like this:

Running Time Self  
3            100% 1 doSomething()
2              67% 2 logTheValue()

To take a more realistic example: in the screenshot below, looking at the second row we can see that 1914 samples were taken inside the detectImage() function. But all of them were taken in functions called by detectImage() (the Self cell is zero). We can expand the call tree to find out which functions were actually running when most of the samples were taken:

This tells us that 6 samples were taken when we were actually executing detectAtScale(), 12 when we were executing getRect() and so on.


  1. If the function is called multiple times from different sources, it will be represented multiple times in the Profiler output. So generic functions like forEach will appear multiple times in the call tree.