<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>performance - Deployment Every Day</title>
	<atom:link href="http://deploymenteveryday.com/tag/performance/feed/" rel="self" type="application/rss+xml" />
	<link>http://deploymenteveryday.com</link>
	<description>JVM (java/kotlin) performance, programming, frequent deployments, devops</description>
	<lastBuildDate>Mon, 23 Dec 2024 11:24:18 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.2</generator>
	<item>
		<title>Is (website) performance improved after improving percentile 90?</title>
		<link>http://deploymenteveryday.com/is-website-performance-improved-after-improving-percentile-90/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=is-website-performance-improved-after-improving-percentile-90</link>
					<comments>http://deploymenteveryday.com/is-website-performance-improved-after-improving-percentile-90/#respond</comments>
		
		<dc:creator><![CDATA[Mikolaj Grzaslewicz]]></dc:creator>
		<pubDate>Mon, 09 Sep 2024 15:14:25 +0000</pubDate>
				<category><![CDATA[performance]]></category>
		<category><![CDATA[distribution]]></category>
		<category><![CDATA[fcp]]></category>
		<category><![CDATA[first contentful paint]]></category>
		<category><![CDATA[p90]]></category>
		<category><![CDATA[percentile]]></category>
		<category><![CDATA[statistics]]></category>
		<category><![CDATA[web vitals]]></category>
		<guid isPermaLink="false">https://deploymenteveryday.com/?p=236</guid>

					<description><![CDATA[]]></description>
										<content:encoded><![CDATA[<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<p class="responsive-video-wrap clr"><iframe title="Is (website) performance improved when percentile 90 is improved?" width="1200" height="675" src="https://www.youtube.com/embed/1Fbi2EgqUpM?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p>
</div></figure><p>The post <a href="http://deploymenteveryday.com/is-website-performance-improved-after-improving-percentile-90/">Is (website) performance improved after improving percentile 90?</a> first appeared on <a href="http://deploymenteveryday.com">Deployment Every Day</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>http://deploymenteveryday.com/is-website-performance-improved-after-improving-percentile-90/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to make performance benchmark 530% worse by relying on web app automation defaults?</title>
		<link>http://deploymenteveryday.com/how-to-make-performance-benchmark-530-worse-by-relying-on-web-app-automation-defaults/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-make-performance-benchmark-530-worse-by-relying-on-web-app-automation-defaults</link>
					<comments>http://deploymenteveryday.com/how-to-make-performance-benchmark-530-worse-by-relying-on-web-app-automation-defaults/#comments</comments>
		
		<dc:creator><![CDATA[Mikolaj Grzaslewicz]]></dc:creator>
		<pubDate>Sun, 05 May 2024 12:26:21 +0000</pubDate>
				<category><![CDATA[performance]]></category>
		<category><![CDATA[benchmark]]></category>
		<category><![CDATA[DOMContentLoaded]]></category>
		<category><![CDATA[end-to-end]]></category>
		<category><![CDATA[playwright]]></category>
		<category><![CDATA[selenium]]></category>
		<guid isPermaLink="false">https://deploymenteveryday.com/?p=112</guid>

					<description><![CDATA[<p>Let&#8217;s start with a necessary definitions: what are DOMContentLoaded and load events in the web browser? DOMContentLoaded According to docs when browser fires this event it means load event When browser fires load event it means DOM content is fully loaded. Browser has finished Both events are NOT about REST calls. And both are not [&#8230;]</p>
<p>The post <a href="http://deploymenteveryday.com/how-to-make-performance-benchmark-530-worse-by-relying-on-web-app-automation-defaults/">How to make performance benchmark 530% worse by relying on web app automation defaults?</a> first appeared on <a href="http://deploymenteveryday.com">Deployment Every Day</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">Let&#8217;s start with a necessary definitions: what are <code>DOMContentLoaded</code> and <code>load</code> events in the web browser?</h2>



<h3 class="wp-block-heading"><code>DOMContentLoaded</code></h3>



<p class="wp-block-paragraph">According to <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event">docs</a> when browser fires this event it means</p>



<ul class="wp-block-list">
<li>DOM has been completely parsed</li>



<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#defer"><code>&lt;script defer src="…"&gt;</code></a>&nbsp;and&nbsp;<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#module"><code>&lt;script type="module"&gt;</code></a> has been loaded</li>



<li>async script, images, frames might not be loaded yet</li>
</ul>



<h3 class="wp-block-heading"><code>load event</code></h3>



<p class="wp-block-paragraph">When browser fires <code>load event</code> it means DOM content is fully loaded. Browser has finished</p>



<ul class="wp-block-list">
<li>executing all asynchronous resources from the DOM tree</li>



<li>loading images, frames etc.</li>



<li>it comes always after <code>DOMContentLoaded</code></li>
</ul>



<p class="wp-block-paragraph">Both events are NOT about REST calls. And both are not relevant to the user browsing the website.</p>



<h2 class="wp-block-heading">What do web apps automation tools like playwright and selenium do by default?</h2>



<p class="wp-block-paragraph">Take a look at following lines of code measuring navigating to a page and waiting for a button to be visible.</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="1359" height="607" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-button.png" alt="" class="wp-image-116" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-button.png 1359w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-button-300x134.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-button-1024x457.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-button-768x343.png 768w" sizes="(max-width: 1359px) 100vw, 1359px" /></figure>



<p class="wp-block-paragraph">using playwright</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers cbp-blur-enabled cbp-unblur-on-hover" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="val millisBefore = System.currentTimeMillis()
page.navigate(&quot;https://github.com&quot;)
page.getByText(&quot;Try Now&quot;).waitFor()
val latency = System.currentTimeMillis() - millisBefore" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> millisBefore </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> System.</span><span style="color: #88C0D0">currentTimeMillis</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line cbp-no-blur"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;https://github.com&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line cbp-no-blur"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">getByText</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;Try Now&quot;</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">waitFor</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> latency </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> System.</span><span style="color: #88C0D0">currentTimeMillis</span><span style="color: #D8DEE9FF">() </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> millisBefore</span></span></code></pre></div>



<p class="wp-block-paragraph">or doing the same with selenium</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers cbp-blur-enabled cbp-unblur-on-hover" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="val millisBefore = System.currentTimeMillis()
driver.navigate().to(&quot;https://github.com&quot;)
WebDriverWait(driver, 30, 100).until(ExpectedConditions.visibilityOfElementLocated(By.linkText(&quot;Sign up for GitHub&quot;)))
val latency = System.currentTimeMillis() - millisBefore" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> millisBefore </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> System.</span><span style="color: #88C0D0">currentTimeMillis</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line cbp-no-blur"><span style="color: #D8DEE9FF">driver.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">to</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;https://github.com&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line cbp-no-blur"><span style="color: #88C0D0">WebDriverWait</span><span style="color: #D8DEE9FF">(driver, </span><span style="color: #B48EAD">30</span><span style="color: #D8DEE9FF">, </span><span style="color: #B48EAD">100</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">until</span><span style="color: #D8DEE9FF">(ExpectedConditions.</span><span style="color: #88C0D0">visibilityOfElementLocated</span><span style="color: #D8DEE9FF">(By.</span><span style="color: #88C0D0">linkText</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;Sign up for GitHub&quot;</span><span style="color: #D8DEE9FF">)))</span></span>
<span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> latency </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> System.</span><span style="color: #88C0D0">currentTimeMillis</span><span style="color: #D8DEE9FF">() </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> millisBefore</span></span></code></pre></div>



<p class="wp-block-paragraph">When you navigate to a page, by default those automation tools wait for <code>load</code> event. So the line with locator is not executed until then.</p>



<p class="wp-block-paragraph">You might think <em>Pff, so what. It&#8217;s just a technical detail</em></p>



<p class="wp-block-paragraph">It is indeed a technical detail but it impacts performance benchmark <mark>dramatically</mark>.</p>



<h2 class="wp-block-heading">Waiting for <code>DOMContentLoaded</code> VS <code>load</code> VS <code>NOT waiting for any event</code>. Fight!</h2>



<p class="wp-block-paragraph">Let&#8217;s measure how quickly user can see github call to action button.</p>



<h3 class="wp-block-heading">Baseline benchmark &#8211; wait for <code>load</code> event</h3>



<p class="wp-block-paragraph">Take 1000 samples. Wait for <code>load</code> event, then wait for github call to action button</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="// only most important code shown here. Processing measurements is out of the scope
page.navigate(&quot;https://github.com&quot;)
page.getByText(&quot;Sign up for GitHub&quot;).first().waitFor(WaitForOptions().setTimeout(2000.0))" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// only most important code shown here. Processing measurements is out of the scope</span></span>
<span class="line"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;https://github.com&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">getByText</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;Sign up for GitHub&quot;</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">first</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">waitFor</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">WaitForOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setTimeout</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">2000.0</span><span style="color: #D8DEE9FF">))</span></span></code></pre></div>



<p class="wp-block-paragraph">It looks like this</p>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1303" height="885" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-load-event-p95.png" alt="" class="wp-image-117" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-load-event-p95.png 1303w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-load-event-p95-300x204.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-load-event-p95-1024x696.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-load-event-p95-768x522.png 768w" sizes="(max-width: 1303px) 100vw, 1303px" /><figcaption class="wp-element-caption">wait for <code>load</code> event` at percentile 95, button measured as visible after 914 ms</figcaption></figure>



<h4 class="wp-block-heading">How to interpret the video of web app measurement?</h4>



<p class="wp-block-paragraph">Playwright video recording is imperfect. Video frame is often not aligned with what was seen in web browser. I confirmed that with modifying the measured page after measurement end.</p>



<p class="wp-block-paragraph">During recording web app, right after measurement end timer is added to the page. This way it doesn&#8217;t interfere with measurement and helps to see when measurement has actually finished on the video.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="const measuerementEndMillis = ..; // depends on the measuerement
const timerDiv = document.createElement('div');
timerDiv.style.position = 'fixed';
timerDiv.style.top = '0';
timerDiv.style.left = '0';
timerDiv.style.opacity = '0.8';
timerDiv.style.fontSize = '14px';
timerDiv.style.backgroundColor = 'yellow';
timerDiv.style.zIndex = '10000';
document.body.appendChild(timerDiv);

setInterval(function() {
  const now = performance.now();
  timerDiv.textContent = now.toFixed(0) + ' ms, ' + (now - measurementEndMillis).toFixed(0) + ' ms';
}, 20);" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">measuerementEndMillis</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">..</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// depends on the measuerement</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">timerDiv</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">document</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">createElement</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">div</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">style</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">position</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">fixed</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">style</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">top</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">0</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">style</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">left</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">0</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">style</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">opacity</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">0.8</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">style</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">fontSize</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">14px</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">style</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">backgroundColor</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">yellow</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">style</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">zIndex</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">10000</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">document</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">body</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">appendChild</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">timerDiv</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">setInterval</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">function</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">now</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">performance</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">now</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">textContent</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">now</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">toFixed</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C"> ms, </span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">now</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">measurementEndMillis</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">toFixed</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C"> ms</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">20</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span></code></pre></div>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" width="556" height="140" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-timer.png" alt="" class="wp-image-136" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-timer.png 556w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-timer-300x76.png 300w" sizes="(max-width: 556px) 100vw, 556px" /><figcaption class="wp-element-caption">X, Y<br>X &#8211; milliseconds since time origin (page started loading)<br>Y &#8211; milliseconds since measurement end. Timer is added after locator is satisfied so it doesn&#8217;t disturb the measurement</figcaption></figure>
</div>


<p class="wp-block-paragraph"><strong>When measuring very fast page like github.com landing page, you&#8217;re often going to see a blank page and right after that frames recorded some time after measurement end (signup button visible). Frames are lost. Based on that you should treat every moment of playwright video as possibly inaccurate</strong>.</p>



<p class="wp-block-paragraph">Having said that, let&#8217;s move on.</p>



<figure class="wp-block-video"><video height="450" style="aspect-ratio: 800 / 450;" width="800" controls src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-load-event-p95.webm"></video><figcaption class="wp-element-caption">wait for <code>load</code> event at percentile 95, button measured as visible after 914 ms. Mind the significant part of the video with button visible, yet measurement is not finished</figcaption></figure>



<h3 class="wp-block-heading">Experiment benchmark 1 &#8211; no waiting</h3>



<p class="wp-block-paragraph">Take 1000 samples. Don&#8217;t wait for any event, wait for github call to action button</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers cbp-blur-enabled cbp-unblur-on-hover" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import com.microsoft.playwright.Page.NavigateOptions
import com.microsoft.playwright.options.WaitUntilState.COMMIT

// from playwright docs WaitUntilState.COMMIT:
// consider operation to be finished when network response is received and the document started loading
page.navigate(&quot;https://github.com&quot;, NavigateOptions().setWaitUntil(COMMIT))
page.locator(&quot;#summary-val&quot;).first().waitFor()" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page.NavigateOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.options.WaitUntilState.COMMIT</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// from playwright docs WaitUntilState.COMMIT:</span></span>
<span class="line"><span style="color: #616E88">// consider operation to be finished when network response is received and the document started loading</span></span>
<span class="line cbp-no-blur"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;https://github.com&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">NavigateOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setWaitUntil</span><span style="color: #D8DEE9FF">(COMMIT))</span></span>
<span class="line cbp-no-blur"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">locator</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;#summary-val&quot;</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">first</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">waitFor</span><span style="color: #D8DEE9FF">()</span></span></code></pre></div>



<p class="wp-block-paragraph">In selenium you have to opt out from waiting for <code>DOMCompleteEvent</code> this way</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="val options = new ChromeOptions()
options.setPageLoadStrategy(PageLoadStrategy.NONE)    
val driver = new ChromeDriver(options)
driver.navigate(..).to()" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> options </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> new </span><span style="color: #88C0D0">ChromeOptions</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">options.</span><span style="color: #88C0D0">setPageLoadStrategy</span><span style="color: #D8DEE9FF">(PageLoadStrategy.NONE)    </span></span>
<span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> driver </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> new </span><span style="color: #88C0D0">ChromeDriver</span><span style="color: #D8DEE9FF">(options)</span></span>
<span class="line"><span style="color: #D8DEE9FF">driver.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">..</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">to</span><span style="color: #D8DEE9FF">()</span></span></code></pre></div>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1301" height="887" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-no-wait-p95.png" alt="" class="wp-image-119" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-no-wait-p95.png 1301w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-no-wait-p95-300x205.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-no-wait-p95-1024x698.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-no-wait-p95-768x524.png 768w" sizes="(max-width: 1301px) 100vw, 1301px" /><figcaption class="wp-element-caption">no wait for event at percentile 95, button measured as visible after 156 ms</figcaption></figure>



<figure class="wp-block-video"><video height="450" style="aspect-ratio: 800 / 450;" width="800" controls src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-no-wait-p95.webm"></video><figcaption class="wp-element-caption">no wait for event at percentile 95, button measured as visible after 156 ms</figcaption></figure>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading">Experiment benchmark 2 &#8211; wait for <code>DOMContentLoaded</code> event</h3>



<p class="wp-block-paragraph">Take 1000 samples. Wait for <code>DOMContentLoaded</code>, wait wait for github call to action button</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers cbp-blur-enabled cbp-unblur-on-hover" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import com.microsoft.playwright.Page.NavigateOptions
import com.microsoft.playwright.options.WaitUntilState.DOMCONTENTLOADED

// from playwright docs WaitUntilState.DOMCONTENTLOADED:
// consider operation to be finished when the DOMContentLoaded event is fired.
page.navigate(&quot;https://github.com&quot;, NavigateOptions().setWaitUntil(DOMCONTENTLOADED))
page.locator(&quot;#summary-val&quot;).first().waitFor()" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page.NavigateOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.options.WaitUntilState.DOMCONTENTLOADED</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// from playwright docs WaitUntilState.DOMCONTENTLOADED:</span></span>
<span class="line"><span style="color: #616E88">// consider operation to be finished when the DOMContentLoaded event is fired.</span></span>
<span class="line cbp-no-blur"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;https://github.com&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">NavigateOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setWaitUntil</span><span style="color: #D8DEE9FF">(DOMCONTENTLOADED))</span></span>
<span class="line cbp-no-blur"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">locator</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;#summary-val&quot;</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">first</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">waitFor</span><span style="color: #D8DEE9FF">()</span></span></code></pre></div>



<p class="wp-block-paragraph">In selenium you have to opt out from waiting for <code>DOMContentLoaded</code> event this way</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="val options = new ChromeOptions()
options.setPageLoadStrategy(PageLoadStrategy.EAGER)    
val driver = new ChromeDriver(options)
driver.navigate(..).to()" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> options </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> new </span><span style="color: #88C0D0">ChromeOptions</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">options.</span><span style="color: #88C0D0">setPageLoadStrategy</span><span style="color: #D8DEE9FF">(PageLoadStrategy.EAGER)    </span></span>
<span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> driver </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> new </span><span style="color: #88C0D0">ChromeDriver</span><span style="color: #D8DEE9FF">(options)</span></span>
<span class="line"><span style="color: #D8DEE9FF">driver.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">..</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">to</span><span style="color: #D8DEE9FF">()</span></span></code></pre></div>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1303" height="887" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-DOMContentLoaded-p95.png" alt="" class="wp-image-121" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-DOMContentLoaded-p95.png 1303w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-DOMContentLoaded-p95-300x204.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-DOMContentLoaded-p95-1024x697.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-DOMContentLoaded-p95-768x523.png 768w" sizes="(max-width: 1303px) 100vw, 1303px" /><figcaption class="wp-element-caption">wait for <code>DOMContentLoaded</code> event at percentile 95, button measured as visible after 802 ms</figcaption></figure>



<figure class="wp-block-video"><video height="450" style="aspect-ratio: 800 / 450;" width="800" controls src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-DOMContentLoaded-p95.webm"></video><figcaption class="wp-element-caption">wait for <code>DOMContentLoaded</code> event at percentile 95, button measured as visible after 802 ms. Mind the significant part of the video with button visible, yet measurement is not finished</figcaption></figure>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading">And now let&#8217;s compare the results</h3>



<h4 class="wp-block-heading">P95 comparison</h4>



<figure class="wp-block-table"><table><tbody><tr><td>no waiting</td><td>wait for <code>DOMContentLoaded</code></td><td>wait for <code>load</code> event</td></tr><tr><td>156 ms</td><td>802 ms</td><td>914 ms</td></tr></tbody></table><figcaption class="wp-element-caption">95th percentile  across different type of loading website in playwright/selenium</figcaption></figure>



<p class="wp-block-paragraph">Percentiles do not answer the question <em>how distribution compares</em>? Down the line you&#8217;re going to see <a href="https://en.wikipedia.org/wiki/Hodges%E2%80%93Lehmann_estimator" target="_blank" rel="noopener" title="">Hodges-Lehman estimator</a></p>



<h4 class="wp-block-heading">What hypotheses are we verifying here?</h4>



<ul class="wp-block-list">
<li><code>load</code> event
<ul class="wp-block-list">
<li>(baseline) null hypothesis: waiting for <code>load</code> event has completion has no performance impact</li>



<li>(experiment 1) alternative hypothesis: waiting for <code>load</code>event has impact on measured latencies</li>
</ul>
</li>



<li><code>DOMContentLoaded</code> event
<ul class="wp-block-list">
<li>(baseline) null hypothesis: waiting for <code>DOMContentLoad</code> event has has no impact on measured latencies</li>



<li>(experiment 2) alternative hypothesis: waiting for <code>DOMContentLoad</code>event has impact on measured latencies</li>
</ul>
</li>
</ul>



<h3 class="wp-block-heading">Waiting for <code>load</code> event VS not waiting for any event</h3>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1303" height="429" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-load-vs-no-wait.png" alt="" class="wp-image-124" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-load-vs-no-wait.png 1303w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-load-vs-no-wait-300x99.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-load-vs-no-wait-1024x337.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-load-vs-no-wait-768x253.png 768w" sizes="(max-width: 1303px) 100vw, 1303px" /><figcaption class="wp-element-caption">83.829% improvement when not waiting for any browser event</figcaption></figure>



<p class="wp-block-paragraph">Title of this blog post says there is 530% performance degradation when you rely on browser automation defaults. So where is it? When you reverse baseline VS experiment, there it is</p>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1301" height="279" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load.png" alt="" class="wp-image-125" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load.png 1301w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-300x64.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-1024x220.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-768x165.png 768w" sizes="(max-width: 1301px) 100vw, 1301px" /><figcaption class="wp-element-caption">Null hypothesis is rejected, waiting for <code>load</code> event comes with bigger latencies</figcaption></figure>



<p class="wp-block-paragraph">Null hypothesis is rejected, waiting for <code>load</code> event comes with bigger latencies</p>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1302" height="312" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-after-ttfb.png" alt="" class="wp-image-126" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-after-ttfb.png 1302w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-after-ttfb-300x72.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-after-ttfb-1024x245.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-after-ttfb-768x184.png 768w" sizes="(max-width: 1302px) 100vw, 1302px" /><figcaption class="wp-element-caption">Time to first byte is irrelevant for hypothesis verification. When you remove it from measured latencies it unfolds more accurate impact on frontend part.</figcaption></figure>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading">Waiting for <code>DOMContentLoaded</code> VS not waiting for any event</h3>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1300" height="279" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-no-wait.png" alt="" class="wp-image-128" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-no-wait.png 1300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-no-wait-300x64.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-no-wait-1024x220.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-no-wait-768x165.png 768w" sizes="(max-width: 1300px) 100vw, 1300px" /><figcaption class="wp-element-caption">79.642% smaller latencies when not waiting for any browser event</figcaption></figure>



<p class="wp-block-paragraph">Null hypothesis is rejected, waiting for <code>DOMContentLoaded </code>increases measured latencies</p>



<h3 class="wp-block-heading">Waiting for <code>DOMContentLoaded</code> VS waiting for <code>load</code> event</h3>



<p class="wp-block-paragraph">Having 2 experiments we can compare them too. Since <code>load</code> event comes always after <code>DOMContentLoaded</code> it&#8217;s expected to see bigger latencies which is reflected in measurements</p>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1300" height="278" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-wait-for-load.png" alt="" class="wp-image-129" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-wait-for-load.png 1300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-wait-for-load-300x64.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-wait-for-load-1024x219.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-wait-for-load-768x164.png 768w" sizes="(max-width: 1300px) 100vw, 1300px" /></figure>



<p class="wp-block-paragraph"></p>



<h2 class="wp-block-heading">Based on measurements, what are undesired effects of waiting for <code>DOMContentLoad</code> or <code>load</code> event?</h2>



<ul class="wp-block-list">
<li>To understand better following part take a look at difference between accuracy and precision</li>
</ul>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="764" height="794" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-Accuracy-vs-Precision.png" alt="" class="wp-image-130" style="width:507px;height:auto" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-Accuracy-vs-Precision.png 764w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-Accuracy-vs-Precision-289x300.png 289w" sizes="(max-width: 764px) 100vw, 764px" /></figure>



<ul class="wp-block-list">
<li>The undesired effects</li>



<li>Measuring something neither reflecting user experience nor relevant to user experience. User sees a button and clicks a button regardless of events in the browser
<ul class="wp-block-list">
<li>No user ever thought &#8220;<em>I politely wish this site had dom completion faster</em>&#8220;. User thinks &#8220;<em>Why the f^%@ is this button not working yet?!!??</em>&#8220;</li>
</ul>
</li>



<li>(much) longer benchmarks
<ul class="wp-block-list">
<li>for ones that aim for measurement count, e.g. we want to have 200 samples</li>
</ul>
</li>



<li>Less precise benchmarks
<ul class="wp-block-list">
<li>for ones that aim for duration, e.g. running for 10 minutes</li>



<li>because you will get less samples</li>
</ul>
</li>



<li>Less accurate benchmarks
<ul class="wp-block-list">
<li>because you&#8217;re not measuring what user experiencies</li>



<li>As a result system under benchmark might actually meet some acceptance criteria, while measurements show it&#8217;s not</li>
</ul>
</li>



<li>Disturbs calculating correlations and figuring out frontend latency causation</li>
</ul>



<h2 class="wp-block-heading">Side effects of not waiting for <code>DOMContentLoaded</code> or <code>loaded</code> event</h2>



<p class="wp-block-paragraph">User can click a visible button, that does not react on clicks yet. Which actually is a reality <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p class="wp-block-paragraph">However that complicates measurements. Frameworks do not check if a button has event listeners. It often requires polling for a desired button state. e.g. await condition click and check if expected message is visible</p>



<h2 class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">I&#8217;ve taken thousands of measurements of a few different sites, not only github. Systems behave the same. Waiting for browser events makes measured latencies inaccurate. It might be 10% away from real user experience, it might be 530%.</p>



<p class="wp-block-paragraph">It&#8217;s non-zero, reproducible and deterministic. So if you already made an effort to make synthetic performance benchmarks, I&#8217;d recommend putting an effort also in increasing their accuracy.</p>



<h3 class="wp-block-heading">Consider changing default options for browser automation</h3>



<p class="wp-block-paragraph">For playwright and selenium (and whatever browser automation tools there are) you should opt out from waiting for any browser events if you&#8217;re using those tools for performance measurements.</p>



<figure class="wp-block-table"><table><tbody><tr><td>Option</td><td>Selenium</td><td>Playwright</td></tr><tr><td>wait for response received (<strong>best for performance benchmarks</strong>)</td><td>NONE</td><td>COMMIT</td></tr><tr><td>wait for load event (<strong>default</strong>)</td><td>NORMAL</td><td>LOAD</td></tr><tr><td>wait for DOMContentLoaded event (another option)</td><td>EAGER</td><td>DOMCONTENTLOADED</td></tr></tbody></table></figure>



<p class="wp-block-paragraph"></p><p>The post <a href="http://deploymenteveryday.com/how-to-make-performance-benchmark-530-worse-by-relying-on-web-app-automation-defaults/">How to make performance benchmark 530% worse by relying on web app automation defaults?</a> first appeared on <a href="http://deploymenteveryday.com">Deployment Every Day</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>http://deploymenteveryday.com/how-to-make-performance-benchmark-530-worse-by-relying-on-web-app-automation-defaults/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		<enclosure url="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-load-event-p95.webm" length="43031" type="video/webm" />
<enclosure url="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-no-wait-p95.webm" length="41413" type="video/webm" />
<enclosure url="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-DOMContentLoaded-p95.webm" length="41614" type="video/webm" />

			</item>
		<item>
		<title>How to use percentile rank to measure performance</title>
		<link>http://deploymenteveryday.com/how-to-use-percentile-rank-to-measure-performance/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-use-percentile-rank-to-measure-performance</link>
					<comments>http://deploymenteveryday.com/how-to-use-percentile-rank-to-measure-performance/#respond</comments>
		
		<dc:creator><![CDATA[Mikolaj Grzaslewicz]]></dc:creator>
		<pubDate>Sat, 20 Apr 2024 13:52:04 +0000</pubDate>
				<category><![CDATA[performance]]></category>
		<category><![CDATA[estimator]]></category>
		<category><![CDATA[ui-benchmark]]></category>
		<guid isPermaLink="false">https://deploymenteveryday.com/?p=104</guid>

					<description><![CDATA[<p>If you want to have a simple yet meaningful criteria that answers the question Is my product fast enough? this blog post is for you. First things first. What&#8217;s the goal of a UI measurement? Who consumes UI? Humans from planet Earth, of course. They get annoyed, frustrated and want to throw away your website [&#8230;]</p>
<p>The post <a href="http://deploymenteveryday.com/how-to-use-percentile-rank-to-measure-performance/">How to use percentile rank to measure performance</a> first appeared on <a href="http://deploymenteveryday.com">Deployment Every Day</a>.</p>]]></description>
										<content:encoded><![CDATA[<h1 class="wp-block-heading">If you want to have a simple yet meaningful criteria that answers the question <code>Is my product fast enough?</code> this blog post is for you.</h1>



<h1 class="wp-block-heading">First things first. What&#8217;s the goal of a UI measurement?</h1>



<p class="wp-block-paragraph">Who consumes UI? Humans from planet Earth, of course. They get annoyed, frustrated and want to throw away your website out of the window when it&#8217;s too slow. They also think very neat things about your product when they are waiting too long.</p>



<p class="wp-block-paragraph">So the ultimate goal of measuring UI is to know if/what parts of the system should be improved in order to keep users happy.</p>



<h1 class="wp-block-heading">What question should performance pass / fail criteria answer?</h1>



<p class="wp-block-paragraph">It should answer the question: &#8220;<em>Are users happy using my product?</em>&#8220;.</p>



<h2 class="wp-block-heading">Why happy user is that important? And what are consequences of having frustrated users because of poor performance?</h2>



<p class="wp-block-paragraph">You&#8217;d better keep users happy by keeping your site fast. Not only you users suffer. Your business suffers. The longer you&#8217;re making users to wait, the more</p>



<ul class="wp-block-list">
<li>bounce rate goes up</li>



<li>you&#8217;re losing traffic from organic search as search engine prefers faster sites
<ul class="wp-block-list">
<li>It&#8217;s a real deal. I already spoke to people having this particular problem. Google changed the rules (web vitals) and whole team needed to work on performance to bring back lost organic traffic</li>
</ul>
</li>



<li>and for systems that companies use internally daily
<ul class="wp-block-list">
<li>users will start to look for alternative solutions that frustrate them less</li>



<li>or have a grumpy-cat face all day</li>
</ul>
</li>
</ul>



<p class="wp-block-paragraph">When system passes acceptance criteria it means particular part of your system is fast enough. It means there aren&#8217;t too many frustrated users. Product does not need to be fixed in this area.</p>



<p class="wp-block-paragraph">When system does not pass acceptance criteria it means it&#8217;s too slow. Which means there are too many frustrated users. Product needs to be fixed in this area.</p>



<h1 class="wp-block-heading">What&#8217;s the perfect threshold for UI pass / fail acceptance criteria?</h1>



<p class="wp-block-paragraph">0 ms so no one waits for anything <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Jokes aside, let&#8217;s see what research says about latencies.</p>



<h2 class="wp-block-heading">What research says about latency and user frustration</h2>



<ul class="wp-block-list">
<li>https://hpbn.co/primer-on-web-performance/#speed-performance-and-human-perception</li>



<li>Latency longer than 1s causes likely mental context switch. And bounce rate goes up</li>



<li>And that might be our frustration point</li>
</ul>



<p class="wp-block-paragraph">Even if a particular part of your UI is far away from latency 1s, it might be a good, ambitious goal that you can track. Don&#8217;t lower the expectations just because system will not pass the criteria. You can always measure the gap to passing the criteria and progress on closing the gap.</p>



<h1 class="wp-block-heading">Measuring user happiness and frustration is nothing new and it works well</h1>



<p class="wp-block-paragraph">There already are well thought concepts. For example</p>



<h2 class="wp-block-heading"><code>APDEX</code></h2>



<ul class="wp-block-list">
<li>https://docs.newrelic.com/docs/apm/new-relic-apm/apdex/apdex-measure-user-satisfaction/</li>



<li>User satisfaction based on T, below which user is satisfied
<ul class="wp-block-list">
<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f603.png" alt="😃" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Satisfied, latency <code>&lt;0, T]</code></li>



<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f610.png" alt="😐" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Tolerated, latency <code>&lt;T, 4T]</code></li>



<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f621.png" alt="😡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Frustrated, latency <code>&lt;4T, infinity)</code> or <code>error</code></li>
</ul>
</li>



<li>Score [0, 1]
<ul class="wp-block-list">
<li>0 &#8211; all frustrated <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f621.png" alt="😡" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>



<li>1 &#8211; all satisfied <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f603.png" alt="😃" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>



<li>(number of requests within satisfied range + (number of requests within tolerated range) / 2) / total number of requests</li>
</ul>
</li>
</ul>



<p class="wp-block-paragraph"><code>APDEX</code> makes sense. However I will explain percentile rank as even simpler and as powerful concept.</p>



<p class="wp-block-paragraph">You might have also heard about percentiles, so let&#8217;s compare percentiles and percentile rank in practical terms.</p>



<h2 class="wp-block-heading">Percentiles. Usually P90, P95 or P99</h2>



<p class="wp-block-paragraph">What&#8217;s the idea?</p>



<p class="wp-block-paragraph">Sort the measured latencies array ascending. P90 is index in this array at 90th %.</p>



<p class="wp-block-paragraph">P90 = latencies[latencies.size * 0.9]</p>



<p class="wp-block-paragraph">P95 = latencies[latencies.size * 0.95]</p>



<p class="wp-block-paragraph">etc</p>



<p class="wp-block-paragraph">Using high percentiles (P90-P99.9) you can find out the slowest latencies user expierence.</p>



<p class="wp-block-paragraph">Calculating percentiles requires whole population (latencies sorted ascending). It&#8217;s not applicable for live monitoring of systems as you have to store every single measured latency.</p>



<h2 class="wp-block-heading">Percentile rank</h2>



<p class="wp-block-paragraph">Statistical meaning for PR(n)</p>



<ul class="wp-block-list">
<li>How many % of other values are lower than <em>n</em></li>



<li>Llike on exam: how many % of other students you&#8217;ve beaten with your score</li>
</ul>



<p class="wp-block-paragraph">It doesn&#8217;t require whole population to calculate, you can count it even on stream. Just increment counter of the passed threshold(s).</p>



<p class="wp-block-paragraph">Implementation is simple</p>



<pre class="wp-block-code"><code>val badCount = 0
val goodCount = 0
val threshold = 1000
latencies.forEach {
 if (it &amp;lt; threshold) goodCount++ else badCount++
}
val percentileRankPercent = goodCount * 100.0 / (badCount + goodCount)
</code></pre>



<h1 class="wp-block-heading">Why not just use percentiles and P90?</h1>



<p class="wp-block-paragraph">P90 means <code>what's the value at 90%th index of sorted array?</code></p>



<p class="wp-block-paragraph">PR(1s) means <code>how many latencies are below 1s?</code>. Not <code>below or equal</code> and that makes the difference.</p>



<p class="wp-block-paragraph">P90 = 1s doesn&#8217;t mean PR(1s) = 90%</p>



<p class="wp-block-paragraph">Take a look at the example. Given following, sorted array of latencies [s], 10 elements for simplicity</p>



<ul class="wp-block-list">
<li><code>0.25, 0.3, 0.6, 0.6, 0.7, 0.8, 0.85, 0.9, 1.0, 1.4</code>
<ul class="wp-block-list">
<li>P90 = 1.0</li>



<li>PR(1) = 80%</li>



<li>PR(1.1) = 90%</li>



<li>PR(1.5) = 100%</li>
</ul>
</li>
</ul>



<p class="wp-block-paragraph">Let&#8217;s change the example. Latencies got worse, but P90 stays the same</p>



<ul class="wp-block-list">
<li><code>0.25, 0.3, 0.6, 0.6, 0.7, 1.0, 1.0, 1.0, 1.0, 1.4</code>
<ul class="wp-block-list">
<li>P90 = 1.0</li>



<li>PR(1) = 50%</li>
</ul>
</li>



<li>As you can see, a single percentile does not say how many samples</li>
</ul>



<p class="wp-block-paragraph">Let&#8217;s change the example. Latencies got even worse, P90 stays the same</p>



<ul class="wp-block-list">
<li><code>1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.4</code>
<ul class="wp-block-list">
<li>P90 = 1.0</li>



<li>PR(1) = 0%</li>
</ul>
</li>
</ul>



<p class="wp-block-paragraph">As you can see, relying on a single percentile brings a problem. For P90 anything between [0, 90%] of latencies might be as bad as P90.</p>



<p class="wp-block-paragraph">Percentiles are not designed to answer question <strong>how good/bad this latency is?</strong></p>



<h1 class="wp-block-heading">Let&#8217;s construct the pass / fail acceptance criteria based on percentile rank</h1>



<p class="wp-block-paragraph">Based on previously mentioned research and 1s as a user frustration point, let&#8217;s construct the acceptance criteria.</p>



<p class="wp-block-paragraph"><code>PR(1s) is expected to be at least 99%</code></p>



<ul class="wp-block-list">
<li>Which means &#8220;<em>At least 99% of website interactions user should experience latency below 1s</em>&#8220;</li>



<li>Which also means &#8220;<em>User can be frustrated by max 1% of website interactions</em>&#8220;</li>
</ul>



<p class="wp-block-paragraph">What if after a benchmark, criteria is NOT met?</p>



<p class="wp-block-paragraph">e.g. <code>PR(1s) = 80% and is expected to be at least 99%</code></p>



<p class="wp-block-paragraph">It means 19% of the responses are too slow. 19% of interactions needs to be fixed.</p>



<h1 class="wp-block-heading">How to measure improvement if you start with PR(1s) = 0%</h1>



<p class="wp-block-paragraph">You might be in a situation that all latencies are &gt;= 1s. 100% frustrated user. You want to improve the product by chaning something.</p>



<p class="wp-block-paragraph"><strong>Before change</strong></p>



<ul class="wp-block-list">
<li>PR(1s) = 0%</li>



<li>All measured latencies are be above 2s</li>
</ul>



<p class="wp-block-paragraph"><strong>After change, new commits added to the product</strong></p>



<ul class="wp-block-list">
<li>PR(1s) = 0%</li>



<li>All of measured latencies are above 1.5s. It is an improvement, but percentile rank hasn&#8217;t changed.</li>
</ul>



<p class="wp-block-paragraph">And you can&#8217;t just say &#8220;<em>this improvement doesn&#8217;t matter, user is still frustrated because criteria measuring user&#8217;s frustration is till unmet</em>&#8220;. Some improvements require a lot of iterative work and you need a devloop in which you notice the improvement. Even if it&#8217;s still far away from having happy user.</p>



<p class="wp-block-paragraph">To compare if there is an improvement you could use comparison benchmark with Hodges-Lehmann estimator.</p>



<ul class="wp-block-list">
<li>That requires access to every single latency from baseline and experiment</li>
</ul>



<p class="wp-block-paragraph">You could also use a simple measure which I call &#8216;wasted user time&#8217; which you want to keep at 0.</p>



<pre class="wp-block-code"><code>val thresholdMillis = 1000
val allowedDurationMillis = latencies.size * thresholdMillis
val totalDurationMillis = latencies.sumOf { it.toMillis() }
val excessiveDurationMillis = totalDurationMillis - allowedDurationMillis
val wastedUserTimePercent = excessiveDurationMillis * 100.0 / totalDurationMillis
</code></pre>



<p class="wp-block-paragraph">This way even if percentile rank hasn&#8217;t changed you can still measure the improvement.</p>



<p class="wp-block-paragraph">And that&#8217;s the exact methodology I&#8217;m using to measure webpages performance and answer questions &#8220;how to improve the system?&#8221;</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="531" src="https://deploymenteveryday.com/wp-content/uploads/2024/04/image-1024x531.png" alt="" class="wp-image-108" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/04/image-1024x531.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/04/image-300x155.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/04/image-768x398.png 768w, http://deploymenteveryday.com/wp-content/uploads/2024/04/image.png 1318w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Real life example of Percentile Rank used to check if a website is fast enough</figcaption></figure><p>The post <a href="http://deploymenteveryday.com/how-to-use-percentile-rank-to-measure-performance/">How to use percentile rank to measure performance</a> first appeared on <a href="http://deploymenteveryday.com">Deployment Every Day</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>http://deploymenteveryday.com/how-to-use-percentile-rank-to-measure-performance/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
