v145 · Web APIs · Timing Analysis

Timing Analysis

How to use Resource Timing fields to distinguish cache hits from network fetches, which fields reveal each phase, and how to compute meaningful percentiles for each population separately.

Resource Timing fields for population detection

Field Cache hit Network fetch Notes
transferSize 0 > 0 Primary classifier. 0 = served from cache or service worker with no-body response.
encodedBodySize > 0 > 0 The encoded size of the response body. Present even for cache hits.
connectStart 0 > 0 (or equals fetchStart if reused connection) 0 for cache hits; non-zero when a new TCP connection was needed.
domainLookupStart equals fetchStart equals fetchStart or later For cache hits, no DNS lookup occurs.
duration < 5ms (typically) 20–2000ms (network) The most directly observable bimodal split.

classifying entries

function classifyEntry(entry) {
  // transferSize=0 with encodedBodySize>0 → cache or service worker
  if (entry.transferSize === 0 && entry.encodedBodySize > 0) {
    // Further distinguish memory cache vs disk cache by duration:
    if (entry.duration < 1) return 'memory-cache';
    return 'disk-cache';
  }
  // transferSize > 0 → actual network fetch
  if (entry.connectStart > 0) return 'network-new-connection';
  return 'network-reused-connection';
}

const entries = performance.getEntriesByType('resource');
const groups = { 'memory-cache': [], 'disk-cache': [], 'network-new-connection': [], 'network-reused-connection': [] };

for (const e of entries) {
  const cls = classifyEntry(e);
  groups[cls].push(e.duration);
}

for (const [cls, durations] of Object.entries(groups)) {
  if (!durations.length) continue;
  durations.sort((a, b) => a - b);
  console.log(cls, 'p50:', durations[Math.floor(durations.length * 0.5)].toFixed(2) + 'ms');
}

why averages hide bimodal distributions

// Example: 90% cache hits at 2ms, 10% network at 400ms
// Average = (0.9 × 2) + (0.1 × 400) = 41.8ms
// p50 = 2ms (cache — correctly)
// p95 = 2ms (still cache)
// p99 = 400ms (network)

// The average (41.8ms) represents nothing real —
// no request actually takes ~42ms. The bimodal split
// reveals the true performance landscape:
//   Fast path (cache): p50=2ms, p95=3ms — great
//   Slow path (network): p50=400ms, p95=1200ms — needs work

// Actionable insight from bimodal analysis:
// → Improve cache hit rate (preload, service worker)
// → Optimise network path (CDN, compression, HTTP/3)

see also

scenario focus

Select a scenario to focus its rendered example and summary.