Determine if a request download time is fast?

I have a 11k image being preloaded in my head as it’s the featured blog image. I see it as a cache HIT in Cloudflare, and TTFB is 568ms and Content Download is 108ms for a total of 712ms. This seems slow to me. But I have no data to support this conclusion.

So to answer my question, I want to “backwards” engineer the answer. I want to limit my analysis to sites that have a fast renderStart time (>2000ms) and requests served by a CDN. Then I want to find the median size and time.

I created a query to help me see this (thanks to Paul Calvano and Rick Viscomi for helping out). But I’m not confident that my query is correct.

  • I’m not sure I’m calculating type per page correctly? I believe that it is saying for pages with a renderStart < 2000, the median number of a type is X. It is not saying that all FAST pages have all these types. And this is NOT an aggregated number – so I am not sure what it tells me about the other values.
  • The “time” represents “all_ms”, so this might be a problem.

My goal is to be able to judge if a request is “fast” or not. So this is dependant on size. But is it really? I’m not asking if it is “fast” for its size. Sorta like a user, they don’t care if a page is fast for a landing page or home page. They just care if its fast.

If it’s slow its slow, maybe size is a factor but maybe the size is okay and the CDN is slow.

For instance, my findings say that on a “fast” page the median size of an image is 17k with a all_ms time of 700ms. This seems to indicate that my above image is actually “fast”. So fine. But then if I look at scripts, it says that median script is 5k and downloads in 251ms. Not sure it makes sense.

SELECT requests.type,
       COUNT(*) AS requests,
       COUNT(DISTINCT requests.pageid) AS pageids,
       round(COUNT(*) / COUNT(DISTINCT requests.pageid)) as type_per_page,
       APPROX_QUANTILES(requests.respBodySize, 100)[OFFSET(50)]  AS size_50P,
       APPROX_QUANTILES(requests.time, 100)[OFFSET(50)] AS time_50P,
       APPROX_QUANTILES(requests.respBodySize, 100)[OFFSET(95)]  AS size_95P,
       APPROX_QUANTILES(requests.time, 100)[OFFSET(95)] AS time_95P,
       round(avg(requests.time)) as time_avg,
       round(avg(requests.respBodySize)) as size_avg
FROM
  `httparchive.summary_pages.2021_03_01_desktop` pages
INNER JOIN
  `httparchive.summary_requests.2021_03_01_desktop` requests
ON
  pages.pageid = requests.pageid
WHERE requests._cdn_provider != ""
AND pages.renderStart < 2000
AND requests.status = 200
AND NET.REG_DOMAIN(requests.url) = NET.REG_DOMAIN(pages.url)
AND requests.respBodySize > 1024
GROUP BY type
ORDER BY requests DESC

Hi @timbednar. The query itself looks good to me from a correctness standpoint but I don’t entirely follow the problem statement you’re trying to solve.

Whether something is fast or slow is a similar problem to the one we tackled when setting the fast/slow thresholds for Core Web Vitals. Bryan McQuade wrote about it in https://web.dev/defining-core-web-vitals-thresholds/. The two factors were quality of experience and achievability.

The CWV metrics are all user-centric, and there’s research into user-perceived performance, which makes them good candidates for objective thresholds. In your case, you’re not measuring something user-centric so it’s going to be harder to define an objective threshold where everything worse is slower and better is faster.

To get more meaningful results I think your best bet is to do something like plot a distribution of the values and calculate the percentile for a given request. For example, you could say that your 712ms image loaded faster than X% of images. You could add constraints like you did in your query for first party images, served by a CDN, over 1 KB, etc.

Here’s an example of a query that generates a cumulative distribution frequency of images at 100ms increments:

SELECT
  time,
  ROUND(pct_requests, 4) AS pct_requests,
  ROUND(SUM(pct_requests) OVER (ORDER BY time), 4) AS cumulative_pct_requests
FROM (
  SELECT
    IF(time < 7500, FLOOR(time / 100) * 100, 7500) AS time,
    COUNT(0) / SUM(COUNT(0)) OVER () AS pct_requests
  FROM
    `httparchive.summary_pages.2021_03_01_desktop` AS pages
  JOIN
    `httparchive.summary_requests.2021_03_01_desktop` AS requests
  USING
    (pageid)
  WHERE
    _cdn_provider != ""
    AND renderStart < 2000
    AND status = 200
    AND NET.REG_DOMAIN(requests.url) = NET.REG_DOMAIN(pages.url)
    AND respBodySize > 1024
    AND type = 'image'
  GROUP BY
    time)
ORDER BY
  time
time pct_requests cumulative_pct_requests
0 6.44% 6.44%
100 10.55% 16.98%
200 9.15% 26.13%
300 8.23% 34.36%
400 6.37% 40.73%
500 5.13% 45.86%
600 4.11% 49.96%
700 3.46% 53.42%
800 2.92% 56.34%
900 2.53% 58.87%
1,000 2.19% 61.06%
7,500 6.46% 100.00%

So for example, your 712ms image is in the ~53rd percentile.

Let me know if that helps.