【问题标题】:Chrome Developer Tools - Performance profilingChrome 开发者工具 - 性能分析
【发布时间】:2020-06-27 02:00:26
【问题描述】:

在下图中(来自 API 调用的 chrome 性能分析选项卡),什么是资源加载,需要 719 毫秒?

如果我访问网络选项卡,对于同一个 API 调用,我只看到 10.05 秒。

这里的资源加载是什么意思?浏览器收到数据后是否有任何特定活动?

【问题讨论】:

  • source code 表示“将资源提供给渲染器进程所需的时间。”
  • 感谢您提供的信息。
  • @wOxxOm,该声明没有说明任何内容,为什么要花这么多时间才能提供资源?

标签: google-chrome-devtools


【解决方案1】:

正如@wOxxOM 所说,buildNetworkRequestDetails 是从Source Code 调用的

来自@Sanju singh 的声明:

那句话没有说明什么,为什么要花这么多时间来提供资源?

我认为有必要准确分析正在发生的事情.. 总结: Activity Browser 和 Network Activity 使用不同的算法来计算完成度。 Network Activity 正在计算请求的响应时间,而 Activity Browser 正在计算响应时间 + 将其添加到 WebInspector 跟踪器所用的时间。

看着

/**
   * @param {!TimelineModel.TimelineModel.NetworkRequest} request
   * @param {!TimelineModel.TimelineModel.TimelineModelImpl} model
   * @param {!Components.Linkifier.Linkifier} linkifier
   * @return {!Promise<!DocumentFragment>}
   */
  static async buildNetworkRequestDetails(request, model, linkifier) {
    const target = model.targetByEvent(request.children[0]);
    const contentHelper = new TimelineDetailsContentHelper(target, linkifier);
    const category = TimelineUIUtils.networkRequestCategory(request);
    const color = TimelineUIUtils.networkCategoryColor(category);
    contentHelper.addSection(ls`Network request`, color);

    if (request.url) {
      contentHelper.appendElementRow(ls`URL`, Components.Linkifier.Linkifier.linkifyURL(request.url));
    }

    // The time from queueing the request until resource processing is finished.
    const fullDuration = request.endTime - (request.getStartTime() || -Infinity);
    if (isFinite(fullDuration)) {
      let textRow = Number.millisToString(fullDuration, true);
      // The time from queueing the request until the download is finished. This
      // corresponds to the total time reported for the request in the network tab.
      const networkDuration = request.finishTime - request.getStartTime();
      // The time it takes to make the resource available to the renderer process.
      const processingDuration = request.endTime - request.finishTime;
      if (isFinite(networkDuration) && isFinite(processingDuration)) {
        const networkDurationStr = Number.millisToString(networkDuration, true);
        const processingDurationStr = Number.millisToString(processingDuration, true);
        const cacheOrNetworkLabel = request.cached() ? ls`load from cache` : ls`network transfer`;
        textRow += ls` (${networkDurationStr} ${cacheOrNetworkLabel} + ${processingDurationStr} resource loading)`;
      }
      contentHelper.appendTextRow(ls`Duration`, textRow);
    }

    if (request.requestMethod) {
      contentHelper.appendTextRow(ls`Request Method`, request.requestMethod);
    }
    if (typeof request.priority === 'string') {
      const priority = PerfUI.NetworkPriorities.uiLabelForNetworkPriority(
          /** @type {!Protocol.Network.ResourcePriority} */ (request.priority));
      contentHelper.appendTextRow(ls`Priority`, priority);
    }
    if (request.mimeType) {
      contentHelper.appendTextRow(ls`Mime Type`, request.mimeType);
    }
    let lengthText = '';
    if (request.memoryCached()) {
      lengthText += ls` (from memory cache)`;
    } else if (request.cached()) {
      lengthText += ls` (from cache)`;
    } else if (request.timing && request.timing.pushStart) {
      lengthText += ls` (from push)`;
    }
    if (request.fromServiceWorker) {
      lengthText += ls` (from service worker)`;
    }
    if (request.encodedDataLength || !lengthText) {
      lengthText = `${Number.bytesToString(request.encodedDataLength)}${lengthText}`;
    }
    contentHelper.appendTextRow(ls`Encoded Data`, lengthText);
    if (request.decodedBodyLength) {
      contentHelper.appendTextRow(ls`Decoded Body`, Number.bytesToString(request.decodedBodyLength));
    }
    const title = ls`Initiator`;
    const sendRequest = request.children[0];
    const topFrame = TimelineModel.TimelineModel.TimelineData.forEvent(sendRequest).topFrame();
    if (topFrame) {
      const link = linkifier.maybeLinkifyConsoleCallFrame(target, topFrame, {tabStop: true});
      if (link) {
        contentHelper.appendElementRow(title, link);
      }
    } else {
      const initiator = TimelineModel.TimelineModel.TimelineData.forEvent(sendRequest).initiator();
      if (initiator) {
        const initiatorURL = TimelineModel.TimelineModel.TimelineData.forEvent(initiator).url;
        if (initiatorURL) {
          const link = linkifier.maybeLinkifyScriptLocation(target, null, initiatorURL, 0, {tabStop: true});
          if (link) {
            contentHelper.appendElementRow(title, link);
          }
        }
      }
    }

    if (!request.previewElement && request.url && target) {
      request.previewElement = await Components.ImagePreview.ImagePreview.build(
          target, request.url, false,
          {imageAltText: Components.ImagePreview.ImagePreview.defaultAltTextForImageURL(request.url)});
    }
    if (request.previewElement) {
      contentHelper.appendElementRow(ls`Preview`, request.previewElement);
    }
    return contentHelper.fragment;
  }

我们很容易看出请求参数的类型

`TimelineModel.TimelineModel.NetworkRequest`

NetWorkRequest 有以下代码:

_didStopRecordingTraceEvents: function()
    {
        var metadataEvents = this._processMetadataEvents();
        this._injectCpuProfileEvents(metadataEvents);
        this._tracingModel.tracingComplete();
        this._resetProcessingState();
        var startTime = 0;
        for (var i = 0, length = metadataEvents.page.length; i < length; i++) {
            var metaEvent = metadataEvents.page[i];
            var process = metaEvent.thread.process();
            var endTime = i + 1 < length ? metadataEvents.page[i + 1].startTime : Infinity;
            this._currentPage = metaEvent.args["data"] && metaEvent.args["data"]["page"];
            for (var thread of process.sortedThreads()) {
                if (thread.name() === WebInspector.TimelineModel.WorkerThreadName && !metadataEvents.workers.some(function(e) { return e.args["data"]["workerThreadId"] === thread.id(); }))
                    continue;
                this._processThreadEvents(startTime, endTime, metaEvent.thread, thread);
            }
            startTime = endTime;
        }
        this._inspectedTargetEvents.sort(WebInspector.TracingModel.Event.compareStartTime);
        this._cpuProfiles = null;
        this._buildTimelineRecords();
        this._buildGPUTasks();
        this._insertFirstPaintEvent();
        this._resetProcessingState();
        this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordingStopped);
    },

我们可以看到 endTime 是从以下位置计算的:

metaEvent.thread.process()

我们可以看到 metaEvent.page 正在被设置:

_processMetadataEvents: function()
    {
        var metadataEvents = this._tracingModel.devToolsMetadataEvents();
        var pageDevToolsMetadataEvents = [];
        var workersDevToolsMetadataEvents = [];
        for (var event of metadataEvents) {
            if (event.name === WebInspector.TimelineModel.DevToolsMetadataEvent.TracingStartedInPage)
                pageDevToolsMetadataEvents.push(event);
            else if (event.name === WebInspector.TimelineModel.DevToolsMetadataEvent.TracingSessionIdForWorker)
                workersDevToolsMetadataEvents.push(event);
        }
        if (!pageDevToolsMetadataEvents.length) {
            // The trace is probably coming not from DevTools. Make a mock Metadata event.
            var pageMetaEvent = this._loadedFromFile ? this._makeMockPageMetadataEvent() : null;
            if (!pageMetaEvent) {
                console.error(WebInspector.TimelineModel.DevToolsMetadataEvent.TracingStartedInPage + " event not found.");
                return {page: [], workers: []};
            }
            pageDevToolsMetadataEvents.push(pageMetaEvent);
        }
        var sessionId = pageDevToolsMetadataEvents[0].args["sessionId"] || pageDevToolsMetadataEvents[0].args["data"]["sessionId"];
        this._sessionId = sessionId;
        var mismatchingIds = new Set();
        /**
         * @param {!WebInspector.TracingModel.Event} event
         * @return {boolean}
         */
        function checkSessionId(event)
        {
            var args = event.args;
            // FIXME: put sessionId into args["data"] for TracingStartedInPage event.
            if (args["data"])
                args = args["data"];
            var id = args["sessionId"];
            if (id === sessionId)
                return true;
            mismatchingIds.add(id);
            return false;
        }
        var result = {
            page: pageDevToolsMetadataEvents.filter(checkSessionId).sort(WebInspector.TracingModel.Event.compareStartTime),
            workers: workersDevToolsMetadataEvents.filter(checkSessionId).sort(WebInspector.TracingModel.Event.compareStartTime)
        };
        if (mismatchingIds.size)
            WebInspector.console.error("Timeline recording was started in more than one page simultaneously. Session id mismatch: " + this._sessionId + " and " + mismatchingIds.valuesArray() + ".");
        return result;
    }

【讨论】: