轻量级指标

轻量级指标系统是查找关键区域的实用工具,这些区域的延迟可能会让最终用户注意到。该系统的一些优点是

  • 这是一个成本低廉的系统,开销非常小
  • 它已经为报告应用加载时间和 RPC 调用上的指标做好了准备
  • 您可以同时分析多个 GWT 模块
  • 它可以扩展以满足您自己的测量需求

由 Google 工程师和 GWT 贡献者开发的 GWT 的调试面板 使用轻量级指标系统。它提供了一种简单的方法来收集指标以及测试您的 GWT 应用。您可以在此 博客文章 中阅读有关该工具功能的更多详细信息。

  1. 工作原理
  2. 现有的可衡量事件
  3. 扩展轻量级指标系统以用于您自己的事件
  4. 同时测量多个模块

工作原理

轻量级指标系统事件

轻量级指标系统由您感兴趣的跟踪事件集和负责评估这些事件并报告指标的全局收集器方法组成。

例如,在加载 GWT 应用时,该过程中涉及的步骤包括引导应用、加载外部引用以及启动 GWT 模块。这些步骤中的每一个都进一步分解为下载引导脚本、选择应用要加载的正确排列、获取排列,等等。这在 轻量级指标设计文档 中进行了说明(参见 GWT 启动流程图)。每个较小的步骤,例如选择应用要加载的正确排列,都可以表示为要在整个应用加载时间内测量的事件。事件本身是包含以下信息的标准 JSON 对象

{ 
  moduleName : <Module name>,
  subSystem : <Subsystem name>,
  evtGroup : <Event group>,
  millis : <Current time in millis>,
  type : <Event type>
}

moduleName 是您的 GWT 模块 的名称。subSystem 指的是在您的 GWT 应用中发出这些事件的特定组件(例如,GWT RPC 子系统)。evtGroup 与一组相关的事件类似,这些事件可以被假定为遵循串行顺序。millis 字段包含事件发出时的时间戳,type 字段指示实际运行并发出事件的方法或步骤。每个 (moduleName, subSystem, evtGroup, type) 元组都可以解释为事件组中的一个检查点。

在 GWT 启动流程中,选择排列的事件可能类似于以下内容

{ 
  moduleName : 'Showcase',
  subSystem : 'startup',
  evtGroup : 'bootstrap',
  millis : new Date().getTime();
  type : 'selectingPermutation'
}

全局收集器函数

全局收集器函数名为 __gwtStatsEvent(),在您想要报告要测量的事件时调用它。它可以直接在您的主机 HTML 页面中定义,也可以动态注入。只要它在您的 GWT 引导脚本执行之前被定义,它就会接收事件指标。从全局收集函数实现内部,您可以决定如何显示为给定的已测量进程收集的指标。收集器函数必须返回 truefalse 以指示事件是否成功记录。

以下是一个 __gwtStatsEvent() 函数示例,如果您想记录在您的 GWT 应用中计时过的所有事件

<head>
  <title>Hello</title>

  <script language='javascript'>
    function eventToString(event) {
      // return some string representation of this event
      return event.evtGroup + " | " + event.moduleName + " | " + event.subSystem + " | " + event.type + " | " + event.millis;
    }

    window.__gwtStatsEvent = function(event) {
      var loggingDiv = document.getElementById('log');
      if (!loggingDiv) {
        // Our logging div is not yet attached to the DOM
        // Initialize a temporary buffer if needed
        this.buffer = (this.buffer) ? this.buffer : [];
        // log data here
        this.buffer.push(event);
      } else {
        if (this.buffer) {
        // We have some data that was reported before the div was connected
          for (var i = 0; i < buffer.length; i++) {
            // print it all to the div
            var bufferedEvent = buffer[i];
            var logline = document.createElement("div");
            logline.id = "logline";
            logline.innerHTML = eventToString(bufferedEvent);
            loggingDiv.appendChild(logline);
          }
          this.buffer = null;
        }
        // log the current event to the div
        var logline = document.createElement("div");
        logline.id = "logline";
        logline.innerHTML = eventToString(event);
        loggingDiv.appendChild(logline);
      }
      // The collector function should indicate success
      return true;
    }
  </script>
</head>
<body>
  <div id="log"><h3>Statistics for Events Logged</h3></div>
  <script type="text/javascript" language="javascript" src="hello/hello.nocache.js"></script>
  <iframe src="javascript:''" id="__gwt_historyFrame" style="position:absolute;width:0;height:0;border:0"></iframe>
</body>

现有的可衡量事件

GWT 引导序列和 GWT RPC 机制已经过仪器化。您只需定义全局收集器函数即可开始收集有关这些事件的信息。要查看系统的工作方式,将上面的代码段中的全局收集器函数添加到您的主机 HTML 页面。

扩展轻量级指标系统以用于您自己的事件

您可以使用轻量级指标系统来测量特定于您自己应用的重要事件。例如,假设您在入口点 onModuleLoad() 中有一个可能很昂贵的函数调用,名为 createWidget()。创建以下函数,该函数调用全局统计收集器函数以测量 createWidget() 执行所需的时间

public class StatsEventLogger {
  public static native void logEvent(String moduleName, String subSystem,
      String eventGroup, double millis, String type) /*-{
    $wnd.__gwtStatsEvent({
      'moduleName' : moduleName,
      'subSystem' : subSystem,
      'evtGroup' : eventGroup,
      'millis' : millis,
      'type' : type
    });
  }-*/;
}

接下来,在您想要分析的代码之前和之后添加调用,如下所示

public FlexTable createWidget() {
  FlexTable listings = new FlexTable();
  double startTime = Duration.currentTimeMillis();
  StatsEventLogger.logEvent(GWT.getModuleName(), "listings", "loadListings", startTime, "begin");
  loadListings(listings, range);
  double endTime = Duration.currentTimeMillis();
  StatsEventLogger.logEvent(GWT.getModuleName(), "listings", "loadListings", endTime, "end");
  return listings;
}

同时测量多个模块

可以同时测量多个模块的初始加载时间以及 RPC 执行时间,因为它们都将对 __gwtStatsEvent() 全局收集器函数进行相同的调用,并将它们各自的模块名称作为报告的事件信息的一部分传递。这使得评估方法的不同功能实现和比较它们的性能变得更容易。您还可以通过使用 evtGroup 字段对各个模块中的事件组进行分组来比较各个模块中的事件组,从而允许您直接比较不同交互实现。