
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Wed, 08 Apr 2026 23:30:18 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Performance measurements… and the people who love them]]></title>
            <link>https://blog.cloudflare.com/loving-performance-measurements/</link>
            <pubDate>Tue, 20 May 2025 13:00:00 GMT</pubDate>
            <description><![CDATA[ Developers have a gut-felt understanding for performance, but that intuition breaks down when systems reach Cloudflare’s scale. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>⚠️ WARNING ⚠️ This blog post contains graphic depictions of probability. Reader discretion is advised.</p><p>Measuring performance is tricky. You have to think about accuracy and precision. Are your sampling rates high enough? Could they be too high?? How much metadata does each recording need??? Even after all that, all you have is raw data. Eventually for all this raw performance information to be useful, it has to be aggregated and communicated. Whether it's in the form of a dashboard, customer report, or a paged alert, performance measurements are only useful if someone can see and understand them.</p><p>This post is a collection of things I've learned working on customer performance escalations within Cloudflare and analyzing existing tools (both internal and commercial) that we use when evaluating our own performance.  A lot of this information also comes from Gil Tene's talk, <a href="https://youtu.be/lJ8ydIuPFeU"><u>How NOT to Measure Latency</u></a>. You should definitely watch that too (but maybe after reading this, so you don't spoil the ending). I was surprised by my own blind spots and which assumptions turned out to be wrong, even though they seemed "obviously true" at the start. I expect I am not alone in these regards. For that reason this journey starts by establishing fundamental definitions and ends with some new tools and techniques that we will be sharing as well as the surprising results that those tools uncovered.</p>
    <div>
      <h2>Check your verbiage</h2>
      <a href="#check-your-verbiage">
        
      </a>
    </div>
    <p>So ... what is performance? Alright, let's start with something easy: definitions. "Performance" is not a very precise term because it gets used in too many contexts. Most of us as nerds and engineers have a gut understanding of what it means, without a real definition. We can't <i>really</i> measure it because how "good" something is depends on what makes that thing good. "Latency" is better ... but not as much as you might think. Latency does at least have an implicit time unit, so we <i>can</i> measure it. But ... <a href="https://www.cloudflare.com/learning/performance/glossary/what-is-latency/">what is latency</a>? There are lots of good, specific examples of measurements of latency, but we are going to use a general definition. Someone starts something, and then it finishes — the elapsed time between is the latency.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1r4blwH5oeloUdXoizuLB4/f58014b1b4b3715f54400e6b03c60ea7/image7.png" />
          </figure><p>This seems a bit reductive, but it’s a surprisingly useful definition because it gives us a key insight. This fundamental definition of latency is based around the client's perspective. Indeed, when we look at our internal measurements of latency for health checks and monitoring, they all have this one-sided caller/callee relationship. There is the latency of the caching layer from the point of view of the ingress proxy. There’s the latency of the origin from the cache’s point of view. Each component can measure the latency of its upstream counterparts, but not the other way around. </p><p>This one-sided nature of latency observation is a real problem for us because Cloudflare <i>only</i> exists on the server side. This makes all of our internal measurements of latency purely estimations. Even if we did have full visibility into a client’s request timing, the start-to-finish latency of a request to Cloudflare isn’t a great measure of Cloudflare’s latency. The process of making an HTTP request has lots of steps, only a subset of which are affected by us. Time spent on things like DNS lookup, local computation for TLS, or resource contention <i>do</i> affect the client’s experience of latency, but only serve as sources of noise when we are considering our own performance.</p><p>There is a very useful and common metric that is used to measure web requests, and I’m sure lots of you have been screaming it in your brains from the second you read the title of this post. ✨Time to first byte✨. Clearly this is the answer, right?!  But ... what is “Time to first byte”?</p>
    <div>
      <h2>TTFB mine</h2>
      <a href="#ttfb-mine">
        
      </a>
    </div>
    <p>Time to first byte (TTFB) on its face is simple. The name implies that it's the time it takes (on the client's side) to receive the first byte of the response from the server, but unfortunately, that only describes when the timer should end. It doesn't say when the timer should start. This ambiguity is just one factor that leads to inconsistencies when trying to compare TTFB across different measurement platforms ... or even across a single platform because there is no <i>one</i> definition of TTFB. Similar to “performance”, it is used in too many places to have a single definition. That being said, TTFB is a very useful concept, so in order to measure it and report it in an unambiguous way, we need to pick a definition that’s already in use.</p><p>We have mentioned TTFB in other blog posts, but <a href="https://blog.cloudflare.com/ttfb-is-not-what-it-used-to-be/"><u>this one</u></a> sums up the problem best with “Time to first byte isn’t what it used to be.” You should read that article too, but the gist is that one popular TTFB definition used by browsers was changed in a confusing way with the introduction of <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/103"><u>early hints</u></a> in June 2022. That post and <a href="https://blog.cloudflare.com/tag/ttfb/"><u>others</u></a> make the point that while TTFB is useful, it isn’t the best direct measurement for web performance. Later on in this post we will derive why that’s the case.</p><p>One common place <i>we</i> see TTFB used is our customers’ analysis comparing Cloudflare's performance to our competitors through <a href="https://www.catchpoint.com/"><u>Catchpoint</u></a>. Customers, as you might imagine, have a vested interest in measuring our latency, as it affects theirs. Catchpoint provides several tools built on their global Internet probe network for measuring HTTP request latency (among other things) and visualizing it in their web interface. In an effort to align better with our customers, we decided to adopt Catchpoint’s terminology for talking about latency, both internally and externally.</p>
    <div>
      <h2>Catchpoint catch-up</h2>
      <a href="#catchpoint-catch-up">
        
      </a>
    </div>
    <p>While Catchpoint makes things like TTFB easy to plot over time, the visualization tool doesn't give a definition of what TTFB is, but after going through all of their technical blog posts and combing through thousands of lines of raw data, we were able to get functional definitions for TTFB and other composite metrics. This was an important step because these metrics are how our customers are viewing our performance, so we all need to be able to understand exactly what they signify! The final report for this is internal (and long and dry), so in this post, I'll give you the highlights in the form of colorful diagrams, starting with this one.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5bB3HmSrIIhQ2AzVpheJWa/8d2b73f3f2f0602217daaf7fea847e11/image6.png" />
          </figure><p>This diagram shows our customers' most commonly viewed client metrics on Catchpoint and how they fit together into the processing of a request from the server side. Notice that some are directly measured, and some are calculated based on the direct measurements. Right in the middle is TTFB, which Catchpoint calculates as the sum of the DNS, Connect, TLS, and Wait times. It’s worth noting again that this is not <i>the</i> definition of TTFB, this is just Catchpoint’s definition, and now ours.</p><p>This breakdown of HTTPS phases is not the only one commonly used. Browsers themselves have a standard for measuring the stages of a request. The diagram below shows how most browsers are reporting request metrics. Luckily (and maybe unsurprisingly) these phases match Catchpoint's very closely.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1ZouyuBQV7XgER2kqhMy8r/04f750eef44ba12bb6915a06eac532ca/image1.png" />
          </figure><p>There are some differences beyond the inclusion of things like <a href="https://html.spec.whatwg.org/#applicationcache"><u>AppCache</u></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Redirections"><u>Redirects</u></a> (which are not directly impacted by Cloudflare's latency). Browser timing metrics are based on timestamps instead of durations. The diagram subtly calls this out with gaps between the different phases indicating that there is the potential for the computer running the browser to do things that are not part of any phase. We can line up these timestamps with Catchpoint's metrics like so:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4TvwOuTxWvMBxKGQQTUfZc/a8105d77725a9fa0d3e5bf6a115a13a5/Screenshot_2025-05-15_at_11.31.46.png" />
          </figure><p>Now that we, our customers, and our browsers (with data coming from <a href="https://en.wikipedia.org/wiki/Real_user_monitoring"><u>RUM</u></a>) have a common and well-defined language to talk about the phases of a request, we can start to measure, visualize, and compare the components that make up the network latency of a request. </p>
    <div>
      <h2>Visual basics</h2>
      <a href="#visual-basics">
        
      </a>
    </div>
    <p>Now that we have defined what our key values for latency are, we can record numbers and put them in a chart and watch them roll by ... except not directly. In most cases, the systems we use to record the data actively prevent us from seeing the recorded data in its raw form. Tools like <a href="https://prometheus.io/"><u>Prometheus</u></a> are designed to collect pre-aggregated data, not individual samples, and for a good reason. Storing every recorded metric (even compacted) would be an enormous amount of data. Even worse, the data loses its value exponentially over time, since the most recent data is the most actionable.</p><p>The unavoidable conclusion is that some aggregation has to be done before performance data can be visualized. In most cases, the aggregation means looking at a series of windowed percentiles over time. The most common are 50th percentile (median), 75th, 90th, and 99th if you're really lucky. Here is an example of a latency visualization from one of our own internal dashboards.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/lvjAR41mTJf2d5Vdg5SwT/19ff931587790b1fb7fbcc317ab83a5e/image8.png" />
          </figure><p>It clearly shows a spike in latency around 14:40 UTC. Was it an incident? The p99 jumped by 1300% (500ms to 6500</p><p>ms) for multiple minutes while the p50 jumped by more than 13600% (4.4ms to 600ms). It is a clear signal, so something must have happened, but what was it? Let me keep you in suspense for a second while we talk about statistics and probability.</p>
    <div>
      <h2>Uncooked math</h2>
      <a href="#uncooked-math">
        
      </a>
    </div>
    <p>Let me start with a quote from my dear, close, personal friend <a href="https://www.youtube.com/watch?v=xV4rLfpidIk"><u>@ThePrimeagen</u></a>:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/I8VbrcSjVSKY1i7fbVEMl/8108e25e78c1ee5356bbd080c467c056/Screenshot_2025-05-15_at_11.33.40.png" />
          </figure><p>It's a good reminder that while statistics is a great tool for providing a simplified and generalized representation of a complex system, it can also obscure important subtleties of that system. A good way to think of statistical modeling is like lossy compression. In the latency visualization above (which is a plot of TTFB over time), we are compressing the entire spectrum of latency metrics into 4 percentile bands, and because we are only considering up to the 99th percentile, there's an entire 1% of samples left over that we are ignoring! </p><p>"What?" I hear you asking. "P99 is already well into perfection territory. We're not trying to be perfectionists. Maybe we should get our p50s down first". Let's put things in perspective. This zone (<a href="http://www.cloudflare.com/"><u>www.cloudflare.com</u></a>) is getting about 30,000 req/s and the 99th percentile latency is 500 ms. (Here we are defining latency as “Edge TTFB”, a server-side approximation of our now official definition.) So there are 300 req/s that are taking longer than half a second to complete, and that's just the portion of the request that <i>we</i> can see. How much worse than 500 ms are those requests in the top 1%? If we look at the 100th percentile (the max), we get a much different vibe from our Edge TTFB plot.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/NDvJObDLjy5D8bKIEhsjS/10f1c40940ba41aae308100c7f374836/image12.png" />
          </figure><p>Viewed like this, the spike in latency no longer looks so remarkable. Without seeing more of the picture, we could easily believe something was wrong when in reality, even if something is wrong, it is not localized to that moment. In this case, it's like we are using our own statistics to lie to ourselves. </p>
    <div>
      <h2>The top 1% of requests have 99% of the latency</h2>
      <a href="#the-top-1-of-requests-have-99-of-the-latency">
        
      </a>
    </div>
    <p>Maybe you're still not convinced. It feels more intuitive to focus on the median because the latency experienced by 50 out of 100 people seems more important to focus on than that of 1 in 100. I would argue that is a totally true statement, but notice I said "people"<sup> </sup>and not "requests." A person visiting a website is not likely to be doing it one request at a time.</p><p>Taking <a href="http://www.cloudflare.com/"><u>www.cloudflare.com</u></a> as an example again, when a user opens that page, their browser makes more than <b>70</b> requests. It sounds big, but in the world of user-facing websites, it’s not that bad. In contrast, <a href="http://www.amazon.com/"><u>www.amazon.com</u></a> issues more than <b>400</b> requests! It's worth noting that not all those requests need to complete before a web page or application becomes usable. That's why more advanced and browser-focused metrics exist, but I will leave a discussion of those for later blog posts. I am more interested in how making that many requests changes the probability calculations for expected latency on a per-user basis. </p><p>Here's a brief primer on combining probabilities that covers everything you need to know to understand this section.</p><ul><li><p>The probability of two things happening is the probability of the first happening multiplied by the probability of the second thing happening. $$P(X\cap Y )=P(X) \times P (Y)$$</p></li><li><p>The probability of something in the $X^{th}$ percentile happening is $X\%$. $$P(pX) = X\%$$</p></li></ul><p>Let's define $P( pX_{N} )$ as the probability that someone on a website with $N$ requests experiences no latencies &gt;= the $X^{th}$ percentile. For example, $P(p50_{2})$ would be the probability of getting no latencies greater than the median on a page with 2 requests. This is equivalent to the probability of one request having a latency less than the $p50$ and the other request having a latency less than the $p50$. We can use the first identities above. </p><p>$$\begin{align}
P( p50_{2}) &amp;= P\left ( p50 \cap p50 \right ) \\
   &amp;= P( p50) \times P\left ( p50 \right ) \\
   &amp;= 50\%^{2} \\
   &amp;= 25\%
\end{align}$$</p><p>We can generalize this for any percentile and any number of requests. $$P( pX_{N}) = X\%^{N}$$</p><p>For <a href="http://www.cloudflare.com/"><u>www.cloudflare.com</u></a> and its 70ish requests, the percentage of visitors that won't experience a latency above the median is </p><p>$$\begin{align} 
P( p50_{70}) &amp;= 50\%^{70} \\
  &amp;\approx 0.000000000000000000001\%
\end{align}$$</p><p>This vanishingly small number should make you question why we would value the $p50$ latency so highly at all when effectively no one experiences it as their worst case latency.</p><p>So now the question is, what request latency percentile <i>should</i> we be looking at? Let's go back to the statement at the beginning of this section. What does the median person experience on <a href="http://www.cloudflare.com./"><u>www.cloudflare.com</u></a>? We can use a little algebra to solve for that.</p><p>$$\begin{align} 
P( pX_{70}) &amp;= 50\% \\
X^{70}  &amp;= 50\% \\
X &amp;= e^{ \frac{ln\left ( 50\% \right )}{70}} \\
X &amp;\approx  99\%
\end{align}$$</p><p>This seems a little too perfect, but I am not making this up. For <a href="http://www.cloudflare.com/"><u>www.cloudflare.com</u></a>, if you want to capture a value that's representative of what the median user can expect, you need to look at $p99$ request latency. Extending this even further, if you want a value that's representative of what 99% of users will experience, you need to look at the <b>99.99th</b> <b>percentile</b>!</p>
    <div>
      <h2>Spherical latency in a vacuum</h2>
      <a href="#spherical-latency-in-a-vacuum">
        
      </a>
    </div>
    <p>Okay, this is where we bring everything together, so stay with me. So far, we have only talked about measuring the performance of a single system. This gives us absolute numbers to look at internally for monitoring, but if you’ll recall, the goal of this post was to be able to clearly communicate about performance outside the company. Often this communication takes the form of comparing Cloudflare’s performance against other providers. How are these comparisons done? By plotting a percentile request "latency" over time and eyeballing the difference.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/x9j5kstMS1kXdsb1PaIbu/837398e0da4758743155595f4f570340/image2.png" />
          </figure><p>With everything we have discussed in this post, it seems like we can devise a better method for doing this comparison. We saw how exposing more of the percentile spectrum can provide a new perspective on existing data, and how impactful higher percentile statistics can be when looking at a more complete user experience. Let me close this post with an example of how putting those two concepts together yields some intriguing results.</p>
    <div>
      <h2>One last thing</h2>
      <a href="#one-last-thing">
        
      </a>
    </div>
    <p>Below is a comparison of the latency (defined here as the sum of the TLS, Connect, and Wait times or the equivalent of TTFB - DNS lookup time) for the customer when viewed through Cloudflare and a competing provider. This is the same data represented in the chart immediately above (containing 90,000 samples for each provider), just in a different form called a <a href="https://en.wikipedia.org/wiki/Cumulative_distribution_function"><u>CDF plot</u></a>, which is one of a few ways we are making it easier to visualize the entire percentile range. The chart shows the percentiles on the y-axis and latency measurements on the x-axis, so to see the latency value for a given percentile, you go up to the percentile you want and then over to the curve. Interpreting these charts is as easy as finding which curve is farther to the left for any given percentile. That curve will have the lower latency.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/53sRk6UCoflU2bGcXypgEQ/f435bbdf43e1646cf2afb56d2aca26be/image4.png" />
          </figure><p>It's pretty clear that for nearly the entire percentile range, the other provider has the lower latency by as much as 30ms. That is, until you get to the very top of the chart. There's a little bit of blue that's above (and therefore to the left) of the green. In order to see what's going on there more clearly, we can use a different kind of visualization. This one is called a <a href="https://en.wikipedia.org/wiki/Q%E2%80%93Q_plot"><u>QQ-Plot</u></a>, or quantile-quantile plot. This shows the same information as the CDF plot, but now each point on the curve represents a specific quantile, and the 2 axes are the latency values of the two providers at that percentile.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/jeYkDomZjnqhrCIIUJBqj/ebb4533c6982b0f8b9f5f491aa1549fb/image9.png" />
          </figure><p>This chart looks complicated, but interpreting it is similar to the CDF plot. The blue is a dividing marker that shows where the latency of both providers is equal. Points below the line indicate percentiles where the other provider has a lower latency than Cloudflare, and points above the line indicate percentiles where Cloudflare is faster. We see again that for most of the percentile range, the other provider is faster, but for percentiles above 99, Cloudflare is significantly faster. </p><p>This is not so compelling by itself, but what if we take into account the number of requests this page issues ... which is over 180. Using the same math from above, and only considering <i>half</i> the requests to be required for the page to be considered loaded, yields this new effective QQ plot.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/S0lLIZfVyVM7KjWUawcNg/967417729939f454bacd0d4c12b0c0e2/image3.png" />
          </figure><p>Taking multiple requests into account, we see that the median latency is close to even for both Cloudflare and the other provider, but the stories above and below that point are very different. A user has about an even chance of an experience where Cloudflare is significantly faster and one where Cloudflare is slightly slower than the other provider. We can show the impact of this shift in perspective more directly by calculating the <a href="https://en.wikipedia.org/wiki/Expected_value#Arbitrary_real-valued_random_variables"><u>expected value</u></a> for request and experienced latency.</p><table><tr><td><p><b>Latency Kind</b></p></td><td><p><b>Cloudflare </b>(ms)</p></td><td><p><b>Other CDN</b> (ms)</p></td><td><p><b>Difference</b> (ms)</p></td></tr><tr><td><p>Expected Request Latency</p></td><td><p>141.9</p></td><td><p>129.9</p></td><td><p><b>+12.0</b></p></td></tr><tr><td><p>Expected Experienced Latency </p><p>Based on 90 Requests </p></td><td><p>207.9</p></td><td><p>281.8</p></td><td><p><b>-71.9</b></p></td></tr></table><p>Shifting the focus from individual request latency to user latency we see that Cloudflare is 70 ms faster than the other provider. This is where our obsession with reliability and tail latency becomes a win for our customers, but without a large volume of raw data, knowledge, and tools, this win would be totally hidden. That is why in the near future we are going to be making this tool and others available to our customers so that we can all get a more accurate and clear picture of our users’ experiences with latency. Keep an eye out for more announcements to come later in 2025.</p> ]]></content:encoded>
            <category><![CDATA[Internet Performance]]></category>
            <category><![CDATA[Latency]]></category>
            <category><![CDATA[Open Source]]></category>
            <category><![CDATA[Observability]]></category>
            <category><![CDATA[TTFB]]></category>
            <guid isPermaLink="false">6R3IB3ISH3fXyycnjNPyZC</guid>
            <dc:creator>Kevin Guthrie</dc:creator>
        </item>
        <item>
            <title><![CDATA[Cloudflare Stream Low-Latency HLS support now in Open Beta]]></title>
            <link>https://blog.cloudflare.com/cloudflare-stream-low-latency-hls-open-beta/</link>
            <pubDate>Mon, 25 Sep 2023 13:00:29 GMT</pubDate>
            <description><![CDATA[ Cloudflare Stream’s LL-HLS support enters open beta today. You can deliver video to your audience faster, reducing the latency a viewer may experience on their player to as little as 3 seconds ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Stream Live lets users easily scale their live-streaming apps and websites to millions of creators and concurrent viewers while focusing on the content rather than the infrastructure — Stream manages codecs, protocols, and bit rate automatically.</p><p>For <a href="/recapping-speed-week-2023/">Speed Week</a> this year, we introduced a <a href="/low-latency-hls-support-for-cloudflare-stream/">closed beta of Low-Latency HTTP Live Streaming</a> (LL-HLS), which builds upon the high-quality, feature-rich HTTP Live Streaming (HLS) protocol. Lower latency brings creators even closer to their viewers, empowering customers to build more interactive features like chat and enabling the use of live-streaming in more time-sensitive applications like live e-learning, sports, gaming, and events.</p><p>Today, in celebration of Birthday Week, we’re opening this beta to all customers with even lower latency. With LL-HLS, you can deliver video to your audience faster, reducing the latency a viewer may experience on their player to as little as three seconds. <a href="https://www.cloudflare.com/developer-platform/solutions/live-streaming/">Low Latency streaming</a> is priced the same way, too: $1 per 1,000 minutes delivered, with zero extra charges for encoding or bandwidth.</p>
    <div>
      <h3>Broadcast with latency as low as three seconds.</h3>
      <a href="#broadcast-with-latency-as-low-as-three-seconds">
        
      </a>
    </div>
    <p>LL-HLS is an extension of the <a href="https://www.cloudflare.com/learning/video/what-is-http-live-streaming/">HLS standard</a> that allows us to reduce glass-to-glass latency — the time between something happening on the broadcast end and a user seeing it on their screen. That includes factors like network conditions and transcoding for HLS and adaptive bitrates. We also include client-side buffering in our understanding of latency because we know the experience is driven by what a user sees, not when a byte is delivered into a buffer. Depending on encoder and player settings, broadcasters' content can be playing on viewers' screens in less than three seconds.</p><div>
  
</div><p><i>On the left,</i> <a href="https://obsproject.com/"><i>OBS Studio</i></a> <i>broadcasting from my personal computer to Cloudflare Stream. On the right, watching this livestream using our own built-in player playing LL-HLS with three second latency!</i></p>
    <div>
      <h3>Same pricing, lower latency. Encoding is always free.</h3>
      <a href="#same-pricing-lower-latency-encoding-is-always-free">
        
      </a>
    </div>
    <p>Our addition of LL-HLS support builds on all the best parts of Stream including simple, predictable pricing. You never have to pay for ingress (broadcasting to us), compute (encoding), or egress. This allows you to stream with peace of mind, knowing there are no surprise fees and no need to trade quality for cost. Regardless of bitrate or resolution, Stream costs \$1 per 1,000 minutes of video delivered and \$5 per 1,000 minutes of video stored, billed monthly.</p><p>Stream also provides both a built-in web player or HLS/DASH manifests to use in a compatible player of your choosing. This enables you or your users to go live using the same protocols and tools that broadcasters big and small use to go live to YouTube or Twitch, but gives you full control over access and presentation of live streams. We also provide access control with signed URLs and hotlinking prevention measures to protect your content.</p>
    <div>
      <h3>Powered by the strength of the network</h3>
      <a href="#powered-by-the-strength-of-the-network">
        
      </a>
    </div>
    <p>And of course, Stream is powered by Cloudflare's global network for fast delivery worldwide, with points of presence within 50ms of 95% of the Internet connected population, a key factor in our quest to slash latency. We ingest live video close to broadcasters and move it rapidly through Cloudflare’s network. We run encoders on-demand and generate player manifests as close to viewers as possible.</p>
    <div>
      <h3>Getting started with LL-HLS</h3>
      <a href="#getting-started-with-ll-hls">
        
      </a>
    </div>
    <p>Getting started with Stream Live only takes a few minutes, and by using Live <i>Outputs</i> for restreaming, you can even test it without changing your existing infrastructure. First, create or update a Live Input in the Cloudflare dashboard. While in beta, Live Inputs will have an option to enable LL-HLS called “Low-Latency HLS Support.” Activate this toggle to enable the new pipeline.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3LHUfSgkCln8UFZy12CtHF/17d72972159b165364d5db31c069702f/image1-9.png" />
            
            </figure><p>Stream will automatically provide the RTMPS and SRT endpoints to broadcast your feed to us, just as before. For the best results, we recommend the following broadcast settings:</p><ul><li><p>Codec: h264</p></li><li><p>GOP size / keyframe interval: 1 second</p></li></ul><p>Optionally, configure a Live Output to point to your existing video ingest endpoint via RTMPS or SRT to test Stream while rebroadcasting to an existing workflow or infrastructure.</p><p>Stream will automatically provide RTMPS and SRT endpoints to broadcast your feed to us as well as an HTML embed for our built-in player.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1zFDLgJ0iwEuh0Brat0TXz/da52d22ae28234ecd4390cf4dc518f4d/image3-7-1.png" />
            
            </figure><p>This connection information can be added easily to a broadcast application like OBS to start streaming immediately:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4Gn4GlXNvamzD1qnN5vEi2/c503a9c7280c6bc8f88a1bfadf43b876/image2-7.png" />
            
            </figure><p>During the beta, our built-in player will automatically attempt to use low-latency for any enabled Live Input, falling back to regular HLS otherwise. If LL-HLS is being used, you’ll see “Low Latency” noted in the player.</p><p>During this phase of the beta, we are most closely focused on using <a href="https://obsproject.com/">OBS</a> to broadcast and Stream’s built-in player to watch. However, you may test the LL-HLS manifest in a player of your own by appending <code>?protocol=llhls</code> to the end of the HLS manifest URL. This flag may change in the future and is not yet ready for production usage; <a href="https://developers.cloudflare.com/stream/changelog/">watch for changes in DevDocs</a>.</p>
    <div>
      <h3>Sign up today</h3>
      <a href="#sign-up-today">
        
      </a>
    </div>
    <p>Low-Latency HLS is Stream Live’s latest tool to bring your creators and audiences together. All new and existing Stream subscriptions are eligible for the LL-HLS open beta today, with no pricing changes or contract requirements --- all part of building the fastest, simplest serverless live-streaming platform. Join our beta to start test-driving Low-Latency HLS!</p> ]]></content:encoded>
            <category><![CDATA[Birthday Week]]></category>
            <category><![CDATA[Cloudflare Stream]]></category>
            <category><![CDATA[Live Streaming]]></category>
            <category><![CDATA[Restreaming]]></category>
            <category><![CDATA[Video]]></category>
            <category><![CDATA[Latency]]></category>
            <category><![CDATA[Product News]]></category>
            <guid isPermaLink="false">4s1ozw7fIyXQZnMYrnEnX4</guid>
            <dc:creator>Taylor Smith</dc:creator>
        </item>
        <item>
            <title><![CDATA[Reduce latency and increase cache hits with Regional Tiered Cache]]></title>
            <link>https://blog.cloudflare.com/introducing-regional-tiered-cache/</link>
            <pubDate>Thu, 01 Jun 2023 13:00:27 GMT</pubDate>
            <description><![CDATA[ Regional Tiered Cache provides an additional layer of caching for Enterprise customers who have a global traffic footprint and want to serve content faster by avoiding network latency when there is a cache miss in a lower-tier, resulting in an upper-tier fetch in a data center located far away ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/A8lQLOj0zrB1oUjv2YTtX/c7982b28eb35d74286e2ca4e130ae1e7/image5-14.png" />
            
            </figure><p>Today we’re excited to announce an update to our <a href="https://developers.cloudflare.com/cache/about/tiered-cache">Tiered Cache</a> offering: Regional Tiered Cache.</p><p>Tiered Cache allows customers to organize Cloudflare data centers into tiers so that only some “<a href="https://developers.cloudflare.com/cache/about/tiered-cache/#:~:text=upper%2Dtier%20does%20not%20have%20the%20content%2C%20only%20the%20upper%2Dtier%20can%20ask%20the%20origin%20for%20content">upper-tier</a>” data centers can request content from an origin server, and then send content to “<a href="https://developers.cloudflare.com/cache/about/tiered-cache/#:~:text=lower%2Dtier%20data%20centers%20(generally%20the%20ones%20closest%20to%20a%20visitor)">lower-tiers</a>” closer to visitors. Tiered Cache helps content load faster for visitors, makes it cheaper to serve, and <a href="https://developers.cloudflare.com/cache/about/tiered-cache#:~:text=Tiered%20Cache%20concentrates%20connections%20to%20origin%20servers%20so%20they%20come%20from%20a%20small%20number%20of%20data%20centers%20rather%20than%20the%20full%20set%20of%20network%20locations.%20This%20results%20in%20fewer%20open%20connections%20using%20server%20resources.">reduces</a> origin resource consumption.</p><p>Regional Tiered Cache provides an additional layer of caching for Enterprise customers who have a global traffic footprint and want to serve content faster by avoiding <a href="https://www.cloudflare.com/learning/performance/glossary/what-is-latency/">network latency</a> when there is a cache miss in a lower-tier, resulting in an upper-tier fetch in a data center located far away. In our trials, customers who have enabled Regional Tiered Cache have seen a 50-100ms improvement in tail <a href="https://developers.cloudflare.com/cache/about/default-cache-behavior/#cloudflare-cache-responses">cache hit</a> response times from Cloudflare’s CDN.</p>
    <div>
      <h2>What problem does Tiered Cache help solve?</h2>
      <a href="#what-problem-does-tiered-cache-help-solve">
        
      </a>
    </div>
    <p>First, a quick refresher on <a href="https://www.cloudflare.com/learning/cdn/what-is-caching/">caching</a>: a request for content is initiated from a visitor on their phone or computer. This request is generally routed to the closest Cloudflare data center. When the request arrives, we look to see if we have the content cached to respond to that request with. If it’s not in cache (it’s a miss), Cloudflare data centers must contact the <a href="https://www.cloudflare.com/learning/cdn/glossary/origin-server/">origin server</a> to get a new copy of the content.</p><p>Getting content from an origin server suffers from two issues: latency and increased origin egress and load.</p>
    <div>
      <h3>Latency</h3>
      <a href="#latency">
        
      </a>
    </div>
    <p>Origin servers, where content is hosted, can be far away from visitors. This is especially true the more global of an audience a particular piece of content has relative to where the origin is located. This means that content hosted in New York can be served in dramatically different amounts of time for visitors in London, Tokyo, and Cape Town. The farther away from New York a visitor is, the longer they must wait before the content is returned. Serving content from cache helps provide a uniform experience to all of these visitors because the content is served from a data center that’s close.</p>
    <div>
      <h3>Origin load</h3>
      <a href="#origin-load">
        
      </a>
    </div>
    <p>Even when using a <a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/">CDN</a>, many different visitors can be interacting with different data centers around the world and each data center, without the content visitors are requesting, will need to reach out to the origin for a copy. This can cost customers money because of egress fees origins charge for sending traffic to Cloudflare, and it places needless load on the origin by opening multiple connections for the same content, just headed to different data centers.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5E2xM6zZw1uUJld0wbCn97/b7c2e5723ad36fe15e538aaa89dec94d/download-19.png" />
            
            </figure><p>When Tiered Cache is not enabled, all data centers in Cloudflare’s network can reach out to the origin in the event of a cache miss.</p><p>Performance improvements and origin load reductions are the promise of tiered cache.</p><p>Tiered Caching means that instead of every data center reaching out to the origin when there is a cache miss, the lower-tier data center that is closest to the visitor will reach out to a larger upper-tier data center to see if it has the requested content cached before the upper-tier asks the origin for the content. Organizing Cloudflare’s data centers into tiers means that fewer requests will make it back to the origin for the same content, preserving origin resources, reducing load, and saving the customer money in egress fees.</p>
    <div>
      <h2>What options are there to maximize the benefits of tiered caching?</h2>
      <a href="#what-options-are-there-to-maximize-the-benefits-of-tiered-caching">
        
      </a>
    </div>
    <p>Cloudflare customers are given access to different Tiered Cache topologies based on their plan level. There are currently two predefined Tiered Cache topologies to select from – Smart and Generic Global. If either of those don’t work for a particular customer’s traffic profile, Enterprise customers can also work with us to define a custom topology.</p><p>In <a href="/introducing-smarter-tiered-cache-topology-generation/">2021,</a> we announced that we’d allow all plans to access Smart Tiered Cache. Smart Tiered Cache dynamically finds the single closest data center to a customer’s origin server and chooses that as the upper-tier that all lower-tier data centers reach out to in the event of a cache miss. All other data centers go through that single upper-tier for content and that data center is the only one that can reach out to the origin. This helps to drastically boost cache hit ratios and reduces the connections to the origin. However, this topology can come at the cost of increased latency for visitors that are farther away from that single upper-tier.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4GmpStKUSWMPfYBgk2dAvb/976cee64422a296ed1e776557fe087cc/download--1--13.png" />
            
            </figure><p>When Smart Tiered Cache is enabled, a single upper tier data center can communicate with the origin, helping to conserve origin resources**.**</p><p>Enterprise customers may select additional tiered cache topologies like the Generic Global topology which allows all of Cloudflare’s large data centers on our network (about 40 data centers) to serve as upper-tiers. While this topology may help reduce the long tail latencies for far-away visitors, it does so at the cost of increased connections and load on a customer's origin.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2PMcMuw7DkwI0GxxtLSzYF/616a53624e1392129f2716d40fb2a48a/download--2--11.png" />
            
            </figure><p>When Generic Global Tiered Cache is enabled, lower-tier data centers are mapped to all upper-tier data centers in Cloudflare’s network which can all reach out to the origin in the event of a cache miss. </p><p>To describe the latency problem with Smart Tiered Cache in more detail let’s use an example. Suppose the upper-tier data center is selected to be in New York using Smart Tiered Cache. The traffic profile for the website with the New York upper-tier is relatively global. Visitors are coming from London, Tokyo, and Cape Town. For every cache miss in a lower-tier it will need to reach out to the New York upper-tier for content. This means these requests from Tokyo will need to traverse the Pacific Ocean and most of the continental United States to check the New York upper-tier cache. Then turn around and go all the way back to Tokyo. This is a giant performance hit for visitors outside the US for the sake of improving origin resource load.</p>
    <div>
      <h2>Regional Tiered Cache brings the best of both worlds</h2>
      <a href="#regional-tiered-cache-brings-the-best-of-both-worlds">
        
      </a>
    </div>
    <p>With Regional Tiered Cache we introduce a middle-tier in each region around the world. When a lower-tier fetches on a cache miss it tries the regional-tier first if the upper-tier is in a different region. If the regional-tier does not have the asset then it asks the upper-tier for it. On the response the regional-tier writes to its cache so other lower-tiers in the same region benefit.</p><p>By putting an additional tier in the same region as the lower-tier, there’s an increased chance that the content will be available in the region before heading to a far-away upper-tier. This can drastically improve the performance of assets while still reducing the number of connections that will eventually need to be made to the customer’s origin.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7MIfujQ3l4iMBIQ5ZWdQiU/b8d70abe5ee256c4d95311665b840aca/download--3--7.png" />
            
            </figure><p>When Regional Tiered Cache is enabled, all lower-tier data centers will reach out to a regional tier close to them in the event of a cache miss. If the regional tier doesn’t have the content, the regional tier will then ask an upper-tier out of region for the content. This can help improve latency for Smart and Custom Tiered Cache topologies.</p>
    <div>
      <h2>Who will benefit from regional tiered cache?</h2>
      <a href="#who-will-benefit-from-regional-tiered-cache">
        
      </a>
    </div>
    <p>Regional Tiered Cache helps customers with Smart Tiered Cache or a Custom Tiered Cache topology with upper-tiers in one or two regions. Regional Tiered Cache is not beneficial for customers with many upper-tiers in many regions like Generic Global Tiered Cache .</p>
    <div>
      <h2>How to enable Regional Tiered Cache</h2>
      <a href="#how-to-enable-regional-tiered-cache">
        
      </a>
    </div>
    <p>Enterprise customers can enable Regional Tiered Cache via the Cloudflare Dashboard or the API:</p>
    <div>
      <h3>UI</h3>
      <a href="#ui">
        
      </a>
    </div>
    <ul><li><p>To enable Regional Tiered Cache, simply sign in to your account and select your website</p></li><li><p>Navigate to the Cache Tab of the dashboard, and select the Tiered Cache Section</p></li><li><p>If you have Smart or Custom Tiered Cache Topology Selected, you should have the ability to choose Regional Tiered Cache</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1wj1OVOGbTWsvDy1rHavci/783d587871b8e0dc423e4f23bf18cb02/download--4--7.png" />
            
            </figure>
    <div>
      <h3>API</h3>
      <a href="#api">
        
      </a>
    </div>
    <p>Please see the <a href="https://developers.cloudflare.com/api/operations/zone-cache-settings-get-regional-tiered-cache-setting">documentation</a> for detailed information about how to configure Regional Tiered Cache from the API.</p><p><b>GET</b></p>
            <pre><code>curl --request GET \
 --url https://api.cloudflare.com/client/v4/zones/zone_identifier/cache/regional_tiered_cache \
 --header 'Content-Type: application/json' \
 --header 'X-Auth-Email: '</code></pre>
            <p><b>PATCH</b></p>
            <pre><code>curl --request PATCH \
 --url https://api.cloudflare.com/client/v4/zones/zone_identifier/cache/regional_tiered_cache \
 --header 'Content-Type: application/json' \
 --header 'X-Auth-Email: ' \
 --data '{
 "value": "on"
}'</code></pre>
            
    <div>
      <h2>Try Regional Tiered Cache out today!</h2>
      <a href="#try-regional-tiered-cache-out-today">
        
      </a>
    </div>
    <p>Regional Tiered Cache is the first of many planned improvements to Cloudflare’s Tiered Cache offering which are currently in development. We look forward to hearing what you think about Regional Tiered Cache, and if you’re interested in helping us improve our CDN, <a href="https://www.cloudflare.com/careers/jobs/?department=Engineering">we’re hiring</a>.</p> ]]></content:encoded>
            <category><![CDATA[Tiered Cache]]></category>
            <category><![CDATA[Cache]]></category>
            <category><![CDATA[Latency]]></category>
            <guid isPermaLink="false">6s5rGN6B1pWPNhxDwZ3IRL</guid>
            <dc:creator>Alex Krivit</dc:creator>
            <dc:creator>Andrew Hauck</dc:creator>
        </item>
        <item>
            <title><![CDATA[Optimizing TCP for high WAN throughput while preserving low latency]]></title>
            <link>https://blog.cloudflare.com/optimizing-tcp-for-high-throughput-and-low-latency/</link>
            <pubDate>Fri, 01 Jul 2022 13:00:01 GMT</pubDate>
            <description><![CDATA[ In this post, we describe how we modified the Linux kernel to optimize for both low latency and high throughput concurrently ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Here at Cloudflare we're constantly working on improving our service. Our engineers are looking at hundreds of parameters of our traffic, making sure that we get better all the time.</p><p>One of the core numbers we keep a close eye on is HTTP request latency, which is important for many of our products. We regard latency spikes as bugs to be fixed. One example is the 2017 story of <a href="/the-sad-state-of-linux-socket-balancing/">"Why does one NGINX worker take all the load?"</a>, where we optimized our TCP Accept queues to improve overall latency of TCP sockets waiting for accept().</p><p>Performance tuning is a holistic endeavor, and we monitor and continuously improve a range of other performance metrics as well, including throughput. Sometimes, tradeoffs have to be made. Such a case occurred in 2015, when a latency spike was discovered in our processing of HTTP requests. The solution at the time was to set tcp_rmem to 4 MiB, which minimizes the amount of time the kernel spends on TCP collapse processing. It was this collapse processing that was causing the latency spikes. Later in this post we discuss TCP collapse processing in more detail.</p><p>The tradeoff is that using a low value for tcp_rmem limits TCP throughput over high latency links. The following graph shows the maximum throughput as a function of network latency for a window size of 2 MiB. Note that the 2 MiB corresponds to a tcp_rmem value of 4 MiB due to the tcp_adv_win_scale setting in effect at the time.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1mRCX3pD7U0Yzn1QeHDDhL/cef49d60e7e32c23d364cbbc65dd50e7/image10-5.png" />
            
            </figure><p>For the Cloudflare products then in existence, this was not a major problem, as connections terminate and content is served from nearby servers due to our BGP anycast routing.</p><p>Since then, we have added new products, such as Magic WAN, WARP, Spectrum, Gateway, and others. These represent new types of use cases and traffic flows.</p><p>For example, imagine you're a typical Magic WAN customer. You have connected all of your worldwide offices together using the Cloudflare global network. While Time to First Byte still matters, Magic WAN office-to-office traffic also needs good throughput. For example, a lot of traffic over these corporate connections will be file sharing using protocols such as SMB. These are <a href="https://en.wikipedia.org/wiki/Elephant_flow">elephant flows</a> over <a href="https://datatracker.ietf.org/doc/html/rfc1072">long fat networks</a>. Throughput is the metric every eyeball watches as they are downloading files.</p><p>We need to continue to provide world-class low latency while simultaneously providing high throughput over high-latency connections.</p><p>Before we begin, let’s introduce the players in our game.</p><p><b>TCP receive window</b> is the maximum number of unacknowledged user payload bytes the sender should transmit (bytes-in-flight) at any point in time. The size of the receive window can and does go up and down during the course of a TCP session. It is a mechanism whereby the receiver can tell the sender to stop sending if the sent packets cannot be successfully received because the receive buffers are full. It is this receive window that often limits throughput over high-latency networks.</p><p><b>net.ipv4.tcp_adv_win_scale</b> is a (non-intuitive) number used to account for the overhead needed by Linux to process packets. The receive window is specified in terms of user payload bytes. Linux needs additional memory beyond that to track other data associated with packets it is processing.</p><p>The value of the receive window changes during the lifetime of a TCP session, depending on a number of factors. The maximum value that the receive window can be is limited by the amount of free memory available in the receive buffer, according to this table:</p><table><tr><td><p>tcp_adv_win_scale</p></td><td><p>TCP window size</p></td></tr><tr><td><p>4</p></td><td><p>15/16 * available memory in receive buffer</p></td></tr><tr><td><p>3</p></td><td><p>⅞ * available memory in receive buffer</p></td></tr><tr><td><p>2</p></td><td><p>¾ * available memory in receive buffer</p></td></tr><tr><td><p>1</p></td><td><p>½ * available memory in receive buffer</p></td></tr><tr><td><p>0</p></td><td><p>available memory in receive buffer</p></td></tr><tr><td><p>-1</p></td><td><p>½ * available memory in receive buffer</p></td></tr><tr><td><p>-2</p></td><td><p>¼ * available memory in receive buffer</p></td></tr><tr><td><p>-3</p></td><td><p>⅛ * available memory in receive buffer</p></td></tr></table><p>We can intuitively (and correctly) understand that the amount of available memory in the receive buffer is the difference between the used memory and the maximum limit. But what is the maximum size a receive buffer can be? The answer is sk_rcvbuf.</p><p><b>sk_rcvbuf</b> is a per-socket field that specifies the maximum amount of memory that a receive buffer can allocate. This can be set programmatically with the socket option SO_RCVBUF. This can sometimes be useful to do, for localhost TCP sessions, for example, but in general the use of SO_RCVBUF is not recommended.</p><p>So how is sk_rcvbuf set? The most appropriate value for that depends on the latency of the TCP session and other factors. This makes it difficult for L7 applications to know how to set these values correctly, as they will be different for every TCP session. The solution to this problem is Linux autotuning.</p>
    <div>
      <h2>Linux autotuning</h2>
      <a href="#linux-autotuning">
        
      </a>
    </div>
    <p>Linux autotuning is logic in the Linux kernel that adjusts the buffer size limits and the receive window based on actual packet processing. It takes into consideration a number of things including TCP session <a href="https://www.cloudflare.com/learning/cdn/glossary/round-trip-time-rtt/">RTT</a>, L7 read rates, and the amount of available host memory.</p><p>Autotuning can sometimes seem mysterious, but it is actually fairly straightforward.</p><p>The central idea is that Linux can track the rate at which the local application is reading data off of the receive queue. It also knows the session RTT. Because Linux knows these things, it can automatically increase the buffers and receive window until it reaches the point at which the application layer or network bottleneck links are the constraint on throughput (and not host buffer settings). At the same time, autotuning prevents slow local readers from having excessively large receive queues. The way autotuning does that is by limiting the receive window and its corresponding receive buffer to an appropriate size for each socket.</p><p>The values set by autotuning can be seen via the Linux “<code>ss</code>” command from the <code>iproute</code> package (e.g. “<code>ss -tmi</code>”).  The relevant output fields from that command are:</p><p><b>Recv-Q</b> is the number of user payload bytes not yet read by the local application.</p><p><b>rcv_ssthresh</b> is the window clamp, a.k.a. the maximum receive window size. This value is not known to the sender. The sender receives only the current window size, via the TCP header field. A closely-related field in the kernel, tp-&gt;window_clamp, is the maximum window size allowable based on the amount of available memory. rcv_ssthresh is the receiver-side slow-start threshold value.</p><p><b>skmem_r</b> is the actual amount of memory that is allocated, which includes not only user payload (Recv-Q) but also additional memory needed by Linux to process the packet (packet metadata). This is known within the kernel as sk_rmem_alloc.</p><p>Note that there are other buffers associated with a socket, so skmem_r does not represent the total memory that a socket might have allocated. Those other buffers are not involved in the issues presented in this post.</p><p><b>skmem_rb</b> is the maximum amount of memory that could be allocated by the socket for the receive buffer. This is higher than rcv_ssthresh to account for memory needed for packet processing that is not packet data. Autotuning can increase this value (up to tcp_rmem max) based on how fast the L7 application is able to read data from the socket and the RTT of the session. This is known within the kernel as sk_rcvbuf.</p><p><b>rcv_space</b> is the high water mark of the rate of the local application reading from the receive buffer during any RTT. This is used internally within the kernel to adjust sk_rcvbuf.</p><p>Earlier we mentioned a setting called tcp_rmem. <b>net.ipv4.tcp_rmem</b> consists of three values, but in this document we are always referring to the third value (except where noted). It is a global setting that specifies the maximum amount of memory that any TCP receive buffer can allocate, i.e. the maximum permissible value that autotuning can use for sk_rcvbuf. This is essentially just a failsafe for autotuning, and under normal circumstances should play only a minor role in TCP memory management.</p><p>It’s worth mentioning that receive buffer memory is not preallocated. Memory is allocated based on actual packets arriving and sitting in the receive queue. It’s also important to realize that filling up a receive queue is not one of the criteria that autotuning uses to increase sk_rcvbuf. Indeed, preventing this type of excessive buffering (<a href="https://en.wikipedia.org/wiki/Bufferbloat">bufferbloat</a>) is one of the benefits of autotuning.</p>
    <div>
      <h2>What’s the problem?</h2>
      <a href="#whats-the-problem">
        
      </a>
    </div>
    <p>The problem is that we must have a large TCP receive window for high <a href="https://en.wikipedia.org/wiki/Bandwidth-delay_product">BDP</a> sessions. This is directly at odds with the latency spike problem mentioned above.</p><p>Something has to give. The laws of physics (speed of light in glass, etc.) dictate that we must use large window sizes. There is no way to get around that. So we are forced to solve the latency spikes differently.</p>
    <div>
      <h2>A brief recap of the latency spike problem</h2>
      <a href="#a-brief-recap-of-the-latency-spike-problem">
        
      </a>
    </div>
    <p>Sometimes a TCP session will fill up its receive buffers. When that happens, the Linux kernel will attempt to reduce the amount of memory the receive queue is using by performing what amounts to a “defragmentation” of memory. This is called collapsing the queue. Collapsing the queue takes time, which is what drives up HTTP request latency.</p><p>We do not want to spend time collapsing TCP queues.</p><p>Why do receive queues fill up to the point where they hit the maximum memory limit? The usual situation is when the local application starts out reading data from the receive queue at one rate (triggering autotuning to raise the max receive window), followed by the local application slowing down its reading from the receive queue. This is valid behavior, and we need to handle it correctly.</p>
    <div>
      <h2>Selecting sysctl values</h2>
      <a href="#selecting-sysctl-values">
        
      </a>
    </div>
    <p>Before exploring solutions, let’s first decide what we need as the maximum TCP window size.</p><p>As we have seen above in the discussion about BDP, the window size is determined based upon the RTT and desired throughput of the connection.</p><p>Because Linux autotuning will adjust correctly for sessions with lower RTTs and bottleneck links with lower throughput, all we need to be concerned about are the maximums.</p><p>For latency, we have chosen 300 ms as the maximum expected latency, as that is the measured latency between our Zurich and Sydney facilities. It seems reasonable enough as a worst-case latency under normal circumstances.</p><p>For throughput, although we have very fast and modern hardware on the Cloudflare global network, we don’t expect a single TCP session to saturate the hardware. We have arbitrarily chosen 3500 mbps as the highest supported throughput for our highest latency TCP sessions.</p><p>The calculation for those numbers results in a BDP of 131MB, which we round to the more aesthetic value of 128 MiB.</p><p>Recall that allocation of TCP memory includes metadata overhead in addition to packet data. The ratio of actual amount of memory allocated to user payload size varies, depending on NIC driver settings, packet size, and other factors. For full-sized packets on some of our hardware, we have measured average allocations up to 3 times the packet data size. In order to reduce the frequency of TCP collapse on our servers, we set tcp_adv_win_scale to -2. From the table above, we know that the max window size will be ¼ of the max buffer space.</p><p>We end up with the following sysctl values:</p>
            <pre><code>net.ipv4.tcp_rmem = 8192 262144 536870912
net.ipv4.tcp_wmem = 4096 16384 536870912
net.ipv4.tcp_adv_win_scale = -2</code></pre>
            <p>A tcp_rmem of 512MiB and tcp_adv_win_scale of -2 results in a maximum window size that autotuning can set of 128 MiB, our desired value.</p>
    <div>
      <h2>Disabling TCP collapse</h2>
      <a href="#disabling-tcp-collapse">
        
      </a>
    </div>
    <p>Patient: Doctor, it hurts when we collapse the TCP receive queue.</p><p>Doctor: Then don’t do that!</p><p>Generally speaking, when a packet arrives at a buffer when the buffer is full, the packet gets dropped. In the case of these receive buffers, Linux tries to “save the packet” when the buffer is full by collapsing the receive queue. Frequently this is successful, but it is not guaranteed to be, and it takes time.</p><p>There are no problems created by immediately just dropping the packet instead of trying to save it. The receive queue is full anyway, so the local receiver application still has data to read. The sender’s congestion control will notice the drop and/or ZeroWindow and will respond appropriately. Everything will continue working as designed.</p><p>At present, there is no setting provided by Linux to disable the TCP collapse. We developed an in-house patch to the kernel to disable the TCP collapse logic.</p>
    <div>
      <h2>Kernel patch – Attempt #1</h2>
      <a href="#kernel-patch-attempt-1">
        
      </a>
    </div>
    <p>The kernel patch for our first attempt was straightforward. At the top of tcp_try_rmem_schedule(), if the memory allocation fails, we simply return (after pred_flag = 0 and tcp_sack_reset()), thus completely skipping the tcp_collapse and related logic.</p><p>It didn’t work.</p><p>Although we eliminated the latency spikes while using large buffer limits, we did not observe the throughput we expected.</p><p>One of the realizations we made as we investigated the situation was that standard network benchmarking tools such as iperf3 and similar do not expose the problem we are trying to solve. iperf3 does not fill the receive queue. Linux autotuning does not open the TCP window large enough. Autotuning is working perfectly for our well-behaved benchmarking program.</p><p>We need application-layer software that is slightly less well-behaved, one that exercises the autotuning logic under test. So we wrote one.</p>
    <div>
      <h2>A new benchmarking tool</h2>
      <a href="#a-new-benchmarking-tool">
        
      </a>
    </div>
    <p>Anomalies were seen during our “Attempt #1” that negatively impacted throughput. The anomalies were seen only under certain specific conditions, and we realized we needed a better benchmarking tool to detect and measure the performance impact of those anomalies.</p><p>This tool has turned into an invaluable resource during the development of this patch and raised confidence in our solution.</p><p>It consists of two Python programs. The reader opens a TCP session to the daemon, at which point the daemon starts sending user payload as fast as it can, and never stops sending.</p><p>The reader, on the other hand, starts and stops reading in a way to open up the TCP receive window wide open and then repeatedly causes the buffers to fill up completely. More specifically, the reader implemented this logic:</p><ol><li><p>reads as fast as it can, for five seconds</p><ul><li><p>this is called fast mode</p></li><li><p>opens up the window</p></li></ul></li><li><p>calculates 5% of the high watermark of the bytes reader during any previous one second</p></li><li><p>for each second of the next 15 seconds:</p><ul><li><p>this is called slow mode</p></li><li><p>reads that 5% number of bytes, then stops reading</p></li><li><p>sleeps for the remainder of that particular second</p></li><li><p>most of the second consists of no reading at all</p></li></ul></li><li><p>steps 1-3 are repeated in a loop three times, so the entire run is 60 seconds</p></li></ol><p>This has the effect of highlighting any issues in the handling of packets when the buffers repeatedly hit the limit.</p>
    <div>
      <h2>Revisiting default Linux behavior</h2>
      <a href="#revisiting-default-linux-behavior">
        
      </a>
    </div>
    <p>Taking a step back, let’s look at the default Linux behavior. The following is kernel v5.15.16.</p><table><tr><td><p>NIC speed (mbps)</p></td><td><p>RTT (ms)</p></td><td><p>tcp_rmem (MiB)</p></td><td><p>tcp_adv_win_scale</p></td><td><p>tcp_disable_collapse</p></td><td><p>TCP window (MiB)</p></td><td><p>buffer metadata to user payload ratio</p></td><td><p>Prune Called</p></td><td><p>RcvCollapsed</p></td><td><p>RcvQDrop</p></td><td><p>OFODrop</p></td><td><p>Test Result</p></td></tr><tr><td><p>1000</p></td><td><p>300</p></td><td><p>512</p></td><td><p>-2</p></td><td><p>0</p></td><td><p>128</p></td><td><p>4</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>GOOD</p></td></tr><tr><td><p>1000</p></td><td><p>300</p></td><td><p>256</p></td><td><p>1</p></td><td><p>0</p></td><td><p>128</p></td><td><p>2</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>GOOD</p></td></tr><tr><td><p>1000</p></td><td><p>300</p></td><td><p>170</p></td><td><p>2</p></td><td><p>0</p></td><td><p>128</p></td><td><p>1.33</p></td><td><p>24</p></td><td><p>490K</p></td><td><p>0</p></td><td><p>0</p></td><td><p>GOOD</p></td></tr><tr><td><p>1000</p></td><td><p>300</p></td><td><p>146</p></td><td><p>3</p></td><td><p>0</p></td><td><p>128</p></td><td><p>1.14</p></td><td><p>57</p></td><td><p>616K</p></td><td><p>0</p></td><td><p>0</p></td><td><p>GOOD</p></td></tr><tr><td><p>1000</p></td><td><p>300</p></td><td><p>137</p></td><td><p>4</p></td><td><p>0</p></td><td><p>128</p></td><td><p>1.07</p></td><td><p>74</p></td><td><p>803K</p></td><td><p>0</p></td><td><p>0</p></td><td><p>GOOD</p></td></tr></table><p>The Linux kernel is effective at freeing up space in order to make room for incoming packets when the receive buffer memory limit is hit. As documented previously, the cost for saving these packets (i.e. not dropping them) is latency.</p><p>However, the latency spikes, in <i>milliseconds</i>, for tcp_try_rmem_schedule(), are:</p><p>tcp_rmem 170 MiB, tcp_adv_win_scale +2 (170p2):</p>
            <pre><code>@ms:
[0]       27093 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
[1]           0 |
[2, 4)        0 |
[4, 8)        0 |
[8, 16)       0 |
[16, 32)      0 |
[32, 64)     16 |</code></pre>
            <p>tcp_rmem 146 MiB, tcp_adv_win_scale +3 (146p3):</p>
            <pre><code>@ms:
(..., 16)  25984 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
[16, 20)       0 |
[20, 24)       0 |
[24, 28)       0 |
[28, 32)       0 |
[32, 36)       0 |
[36, 40)       0 |
[40, 44)       1 |
[44, 48)       6 |
[48, 52)       6 |
[52, 56)       3 |</code></pre>
            <p>tcp_rmem 137 MiB, tcp_adv_win_scale +4 (137p4):</p>
            <pre><code>@ms:
(..., 16)  37222 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
[16, 20)       0 |
[20, 24)       0 |
[24, 28)       0 |
[28, 32)       0 |
[32, 36)       0 |
[36, 40)       1 |
[40, 44)       8 |
[44, 48)       2 |</code></pre>
            <p>These are the latency spikes we cannot have on the Cloudflare global network.</p>
    <div>
      <h2>Kernel patch – Attempt #2</h2>
      <a href="#kernel-patch-attempt-2">
        
      </a>
    </div>
    <p>So the “something” that was not working in Attempt #1 was that the receive queue memory limit was hit early on as the flow was just ramping up (when the values for sk_rmem_alloc and sk_rcvbuf were small, ~800KB). This occurred at about the two second mark for 137p4 test (about 2.25 seconds for 170p2).</p><p>In hindsight, we should have noticed that tcp_prune_queue() actually raises sk_rcvbuf when it can. So we modified the patch in response to that, added a guard to allow the collapse to execute when sk_rmem_alloc is less than the threshold value.</p><p><code>net.ipv4.tcp_collapse_max_bytes = 6291456</code></p><p>The next section discusses how we arrived at this value for tcp_collapse_max_bytes.</p><p>The patch is available <a href="https://github.com/cloudflare/linux/blob/master/patches/0014-add-a-sysctl-to-enable-disable-tcp_collapse-logic.patch">here</a>.</p><p>The results with the new patch are as follows:</p><p>oscil – 300ms tests</p><table><tr><td><p>Test</p></td><td><p>RTT (ms)</p></td><td><p>tcp_rmem (MiB)</p></td><td><p>tcp_adv_win_scale</p></td><td><p>tcp_disable_collapse (MiB)</p></td><td><p>NIC speed (mbps)</p></td><td><p>TCP window (MiB)</p></td><td><p>real buffer metadata to user payload ratio</p></td><td><p>RcvCollapsed</p></td><td><p>RcvQDrop</p></td><td><p>OFODrop</p></td><td><p>max latency (us)</p><p>
</p></td><td><p>Test Result</p></td></tr><tr><td><p>oscil reader</p></td><td><p>300</p></td><td><p>512</p></td><td><p>-2</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>4</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>12</p></td><td><p>1-941</p><p>941</p><p>941</p></td></tr><tr><td><p>oscil reader</p></td><td><p>300</p></td><td><p>256</p></td><td><p>1</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>2</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>11</p></td><td><p>1-941</p><p>941</p><p>941</p></td></tr><tr><td><p>oscil reader</p></td><td><p>300</p></td><td><p>170</p></td><td><p>2</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.33</p></td><td><p>0</p></td><td><p>9</p></td><td><p>86</p></td><td><p>11</p></td><td><p>1-941</p><p>36-605</p><p>1-298</p></td></tr><tr><td><p>oscil reader</p></td><td><p>300</p></td><td><p>146</p></td><td><p>3</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.14</p></td><td><p>0</p></td><td><p>7</p></td><td><p>1550</p></td><td><p>16</p></td><td><p>1-940</p><p>2-82</p><p>292-395</p></td></tr><tr><td><p>oscil reader</p></td><td><p>300</p></td><td><p>137</p></td><td><p>4</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.07</p></td><td><p>0</p></td><td><p>10</p></td><td><p>3020</p></td><td><p>9</p></td><td><p>1-940</p><p>2-13</p><p>13-33</p></td></tr></table><p>oscil – 20ms tests</p><table><tr><td><p>Test</p></td><td><p>RTT (ms)</p></td><td><p>tcp_rmem (MiB)</p></td><td><p>tcp_adv_win_scale</p></td><td><p>tcp_disable_collapse (MiB)</p></td><td><p>NIC speed (mbps)</p></td><td><p>TCP window (MiB)</p></td><td><p>real buffer metadata to user payload ratio</p></td><td><p>RcvCollapsed</p></td><td><p>RcvQDrop</p></td><td><p>OFODrop</p></td><td><p>max latency (us)</p><p>
</p></td><td><p>Test Result</p></td></tr><tr><td><p>oscil reader</p></td><td><p>20</p></td><td><p>512</p></td><td><p>-2</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>4</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>13</p></td><td><p>795-941</p><p>941</p><p>941</p></td></tr><tr><td><p>oscil reader</p></td><td><p>20</p></td><td><p>256</p></td><td><p>1</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>2</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>13</p></td><td><p>795-941</p><p>941</p><p>941</p></td></tr><tr><td><p>oscil reader</p></td><td><p>20</p></td><td><p>170</p></td><td><p>2</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.33</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>8</p></td><td><p>795-941</p><p>941</p><p>941</p></td></tr><tr><td><p>oscil reader</p></td><td><p>20</p></td><td><p>146</p></td><td><p>3</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.14</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>7</p></td><td><p>795-941</p><p>941</p><p>941</p></td></tr><tr><td><p>oscil reader</p></td><td><p>20</p></td><td><p>137</p></td><td><p>4</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.07</p></td><td><p>0</p></td><td><p>4</p></td><td><p>196</p></td><td><p>12</p></td><td><p>795-941</p><p>13-941</p><p>941</p></td></tr></table><p>oscil – 0ms tests</p><table><tr><td><p>Test</p></td><td><p>RTT (ms)</p></td><td><p>tcp_rmem (MiB)</p></td><td><p>tcp_adv_win_scale</p></td><td><p>tcp_disable_collapse (MiB)</p></td><td><p>NIC speed (mbps)</p></td><td><p>TCP window (MiB)</p></td><td><p>real buffer metadata to user payload ratio</p></td><td><p>RcvCollapsed</p></td><td><p>RcvQDrop</p></td><td><p>OFODrop</p></td><td><p>max latency (us)</p><p>
</p></td><td><p>Test Result</p></td></tr><tr><td><p>oscil reader</p></td><td><p>0.3</p></td><td><p>512</p></td><td><p>-2</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>4</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>9</p></td><td><p>941</p><p>941</p><p>941</p></td></tr><tr><td><p>oscil reader</p></td><td><p>0.3</p></td><td><p>256</p></td><td><p>1</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>2</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>22</p></td><td><p>941</p><p>941</p><p>941</p></td></tr><tr><td><p>oscil reader</p></td><td><p>0.3</p></td><td><p>170</p></td><td><p>2</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.33</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>8</p></td><td><p>941</p><p>941</p><p>941</p></td></tr><tr><td><p>oscil reader</p></td><td><p>0.3</p></td><td><p>146</p></td><td><p>3</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.14</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>10</p></td><td><p>941</p><p>941</p><p>941</p></td></tr><tr><td><p>oscil reader</p></td><td><p>0.3</p></td><td><p>137</p></td><td><p>4</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.07</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>10</p></td><td><p>941</p><p>941</p><p>941</p></td></tr></table><p>iperf3 – 300 ms tests</p><table><tr><td><p>Test</p></td><td><p>RTT (ms)</p></td><td><p>tcp_rmem (MiB)</p></td><td><p>tcp_adv_win_scale</p></td><td><p>tcp_disable_collapse (MiB)</p></td><td><p>NIC speed (mbps)</p></td><td><p>TCP window (MiB)</p></td><td><p>real buffer metadata to user payload ratio</p></td><td><p>RcvCollapsed</p></td><td><p>RcvQDrop</p></td><td><p>OFODrop</p></td><td><p>max latency (us)</p><p>
</p></td><td><p>Test Result</p></td></tr><tr><td><p>iperf3</p></td><td><p>300</p></td><td><p>512</p></td><td><p>-2</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>4</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>7</p></td><td><p>941</p></td></tr><tr><td><p>iperf3</p></td><td><p>300</p></td><td><p>256</p></td><td><p>1</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>2</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>6</p></td><td><p>941</p></td></tr><tr><td><p>iperf3</p></td><td><p>300</p></td><td><p>170</p></td><td><p>2</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.33</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>9</p></td><td><p>941</p></td></tr><tr><td><p>iperf3</p></td><td><p>300</p></td><td><p>146</p></td><td><p>3</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.14</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>11</p></td><td><p>941</p></td></tr><tr><td><p>iperf3</p></td><td><p>300</p></td><td><p>137</p></td><td><p>4</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.07</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>7</p></td><td><p>941</p></td></tr></table><p>iperf3 – 20 ms tests</p><table><tr><td><p>Test</p></td><td><p>RTT (ms)</p></td><td><p>tcp_rmem (MiB)</p></td><td><p>tcp_adv_win_scale</p></td><td><p>tcp_disable_collapse (MiB)</p></td><td><p>NIC speed (mbps)</p></td><td><p>TCP window (MiB)</p></td><td><p>real buffer metadata to user payload ratio</p></td><td><p>RcvCollapsed</p></td><td><p>RcvQDrop</p></td><td><p>OFODrop</p></td><td><p>max latency (us)</p><p>
</p></td><td><p>Test Result</p></td></tr><tr><td><p>iperf3</p></td><td><p>20</p></td><td><p>512</p></td><td><p>-2</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>4</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>7</p></td><td><p>941</p></td></tr><tr><td><p>iperf3</p></td><td><p>20</p></td><td><p>256</p></td><td><p>1</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>2</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>15</p></td><td><p>941</p></td></tr><tr><td><p>iperf3</p></td><td><p>20</p></td><td><p>170</p></td><td><p>2</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.33</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>7</p></td><td><p>941</p></td></tr><tr><td><p>iperf3</p></td><td><p>20</p></td><td><p>146</p></td><td><p>3</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.14</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>7</p></td><td><p>941</p></td></tr><tr><td><p>iperf3</p></td><td><p>20</p></td><td><p>137</p></td><td><p>4</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.07</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>6</p></td><td><p>941</p></td></tr></table><p>iperf3 – 0ms tests</p><table><tr><td><p>Test</p></td><td><p>RTT (ms)</p></td><td><p>tcp_rmem (MiB)</p></td><td><p>tcp_adv_win_scale</p></td><td><p>tcp_disable_collapse (MiB)</p></td><td><p>NIC speed (mbps)</p></td><td><p>TCP window (MiB)</p></td><td><p>real buffer metadata to user payload ratio</p></td><td><p>RcvCollapsed</p></td><td><p>RcvQDrop</p></td><td><p>OFODrop</p></td><td><p>max latency (us)</p><p>
</p></td><td><p>Test Result</p></td></tr><tr><td><p>iperf3</p></td><td><p>0.3</p></td><td><p>512</p></td><td><p>-2</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>4</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>6</p></td><td><p>941</p></td></tr><tr><td><p>iperf3</p></td><td><p>0.3</p></td><td><p>256</p></td><td><p>1</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>2</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>14</p></td><td><p>941</p></td></tr><tr><td><p>iperf3</p></td><td><p>0.3</p></td><td><p>170</p></td><td><p>2</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.33</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>6</p></td><td><p>941</p></td></tr><tr><td><p>iperf3</p></td><td><p>0.3</p></td><td><p>146</p></td><td><p>3</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.14</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>7</p></td><td><p>941</p></td></tr><tr><td><p>iperf3</p></td><td><p>0.3</p></td><td><p>137</p></td><td><p>4</p></td><td><p>6</p></td><td><p>1000</p></td><td><p>128</p></td><td><p>1.07</p></td><td><p>0</p></td><td><p>0</p></td><td><p>0</p></td><td><p>6</p></td><td><p>941</p></td></tr></table><p>All tests are successful.</p>
    <div>
      <h2>Setting tcp_collapse_max_bytes</h2>
      <a href="#setting-tcp_collapse_max_bytes">
        
      </a>
    </div>
    <p>In order to determine this setting, we need to understand what the biggest queue we <i>can</i> collapse without incurring unacceptable latency.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6PfP9U02g39dedYXSqz0H2/8b61121e3ffe7f102fea0d682d902c56/image8-12.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/14e9NHQT0aciu7bJvpLyIM/5df55a86e2a24c7c7501c2c80a38e847/image7-13.png" />
            
            </figure><p>Using 6 MiB should result in a maximum latency of no more than 2 ms.</p>
    <div>
      <h2>Cloudflare production network results</h2>
      <a href="#cloudflare-production-network-results">
        
      </a>
    </div>
    
    <div>
      <h3>Current production settings (“Old”)</h3>
      <a href="#current-production-settings-old">
        
      </a>
    </div>
    
            <pre><code>net.ipv4.tcp_rmem = 8192 2097152 16777216
net.ipv4.tcp_wmem = 4096 16384 33554432
net.ipv4.tcp_adv_win_scale = -2
net.ipv4.tcp_collapse_max_bytes = 0
net.ipv4.tcp_notsent_lowat = 4294967295</code></pre>
            <p>tcp_collapse_max_bytes of 0 means that the custom feature is disabled and that the vanilla kernel logic is used for TCP collapse processing.</p>
    <div>
      <h3>New settings under test (“New”)</h3>
      <a href="#new-settings-under-test-new">
        
      </a>
    </div>
    
            <pre><code>net.ipv4.tcp_rmem = 8192 262144 536870912
net.ipv4.tcp_wmem = 4096 16384 536870912
net.ipv4.tcp_adv_win_scale = -2
net.ipv4.tcp_collapse_max_bytes = 6291456
net.ipv4.tcp_notsent_lowat = 131072</code></pre>
            <p>The tcp_notsent_lowat setting is discussed in the last section of this post.</p><p>The middle value of tcp_rmem was changed as a result of separate work that found that Linux autotuning was setting receive buffers too high for localhost sessions. This updated setting reduces TCP memory usage for those sessions, but does not change anything about the type of TCP sessions that is the focus of this post.</p><p>For the following benchmarks, we used non-Cloudflare host machines in Iowa, US, and Melbourne, Australia performing data transfers to the Cloudflare data center in Marseille, France. In Marseille, we have some hosts configured with the existing production settings, and others with the system settings described in this post. Software used is perf3 version 3.9, kernel 5.15.32.</p>
    <div>
      <h3>Throughput results</h3>
      <a href="#throughput-results">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3GkgLi4X0vuXMl7rOEWvZK/e64146a2a197174ab5e563b34b8ba9e8/image3-36.png" />
            
            </figure><table><tr><td><p>
</p></td><td><p>RTT</p><p>(ms)</p></td><td><p>Throughput with Current Settings</p><p>(mbps)</p></td><td><p>Throughput with</p><p>New Settings</p><p>(mbps)</p></td><td><p>Increase</p><p>Factor</p></td></tr><tr><td><p>Iowa to</p><p>Marseille</p></td><td><p>121 </p></td><td><p>276</p></td><td><p>6600</p></td><td><p>24x</p></td></tr><tr><td><p>Melbourne to Marseille</p></td><td><p>282</p></td><td><p>120</p></td><td><p>3800</p></td><td><p>32x</p></td></tr></table><p><b>Iowa-Marseille throughput</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2WvQmKxKD8buJU17GU15Kw/798330bea25ad3dedfe5d1beb8578363/image6-16.png" />
            
            </figure><p><b>Iowa-Marseille receive window and bytes-in-flight</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2V2iFmp0s1h8vH4SyrzFQS/5b1118af5ff1ee2116aacfe7c9d60927/image2-51.png" />
            
            </figure><p><b>Melbourne-Marseille throughput</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/PnqeHPMJbmCuyGvt9UjtA/037328ab56686611fce568409e0c8336/image9-10.png" />
            
            </figure><p><b>Melbourne-Marseille receive window and bytes-in-flight</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4H5cZs3oyG7KiTKL9m4b7R/ff9563e9d1a69da0adb653587dd1f336/image5-21.png" />
            
            </figure><p>Even with the new settings in place, the Melbourne to Marseille performance is limited by the receive window on the Cloudflare host. This means that further adjustments to these settings yield even higher throughput.</p>
    <div>
      <h3>Latency results</h3>
      <a href="#latency-results">
        
      </a>
    </div>
    <p>The Y-axis on these charts are the 99th percentile time for TCP collapse in seconds.</p><p><b>Cloudflare hosts in Marseille running the current production settings</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1jxkFikBYH12eNxfHcHwXR/c8c3e183a28b84a4f93bc725309d5eff/image11-4.png" />
            
            </figure><p><b>Cloudflare hosts in Marseille running the new settings</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6dqk8mtvEpghINGK0bfKUz/cec115db36d4a927ca4764bfe022cad8/image1-59.png" />
            
            </figure><p>The takeaway in looking at these graphs is that maximum TCP collapse time for the new settings is no worse than with the current production settings. This is the desired result.</p>
    <div>
      <h3>Send Buffers</h3>
      <a href="#send-buffers">
        
      </a>
    </div>
    <p>What we have shown so far is that the receiver side seems to be working well, but what about the sender side?</p><p>As part of this work, we are setting tcp_wmem max to 512 MiB. For oscillating reader flows, this can cause the send buffer to become quite large. This represents bufferbloat and wasted kernel memory, both things that nobody likes or wants.</p><p>Fortunately, there is already a solution: <b>tcp_notsent_lowat</b>. This setting limits the size of unsent bytes in the write queue. More details can be found at <a href="https://lwn.net/Articles/560082/">https://lwn.net/Articles/560082</a>.</p><p>The results are significant:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3yXWQ4mWrhKWV8Tzv0bUCb/75339710d08b943a8cb884a68377d4ec/image4-29.png" />
            
            </figure><p>The RTT for these tests was 466ms. Throughput is not negatively affected. Throughput is at full wire speed in all cases (1 Gbps). Memory usage is as reported by /proc/net/sockstat, TCP mem.</p><p>Our web servers already set tcp_notsent_lowat to 131072 for its sockets. All other senders are using 4 GiB, the default value. We are changing the sysctl so that 131072 is in effect for all senders running on the server.</p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>The goal of this work is to open the throughput floodgates for high BDP connections while simultaneously ensuring very low HTTP request latency.</p><p>We have accomplished that goal.</p><p>...<i>We protect </i><a href="https://www.cloudflare.com/network-services/"><i>entire corporate networks</i></a><i>, help customers build </i><a href="https://workers.cloudflare.com/"><i>Internet-scale applications efficiently</i></a><i>, accelerate any </i><a href="https://www.cloudflare.com/performance/accelerate-internet-applications/"><i>website or Internet application</i></a><i>, ward off </i><a href="https://www.cloudflare.com/ddos/"><i>DDoS attacks</i></a><i>, keep </i><a href="https://www.cloudflare.com/application-security/"><i>hackers at bay</i></a><i>, and can help you on </i><a href="https://www.cloudflare.com/products/zero-trust/"><i>your journey to Zero Trust</i></a><i>.</i></p><p><i>Visit </i><a href="https://1.1.1.1/"><i>1.1.1.1</i></a><i> from any device to get started with our free app that makes your Internet faster and safer.To learn more about our mission to help build a better Internet, start </i><a href="https://www.cloudflare.com/learning/what-is-cloudflare/"><i>here</i></a><i>. If you’re looking for a new career direction, check out </i><a href="http://cloudflare.com/careers"><i>our open positions</i></a><i>.</i></p> ]]></content:encoded>
            <category><![CDATA[Deep Dive]]></category>
            <category><![CDATA[TCP]]></category>
            <category><![CDATA[Latency]]></category>
            <category><![CDATA[Optimization]]></category>
            <guid isPermaLink="false">BcOgofewzZGwenQrFsVMq</guid>
            <dc:creator>Mike Freemon</dc:creator>
        </item>
        <item>
            <title><![CDATA[Test your home network performance]]></title>
            <link>https://blog.cloudflare.com/test-your-home-network-performance/</link>
            <pubDate>Tue, 26 May 2020 17:00:59 GMT</pubDate>
            <description><![CDATA[ Cloudflare launches speed.cloudflare.com, a tool that allows you to gain in-depth insights into the quality of your network uplink, including throughput, latency and jitter. ]]></description>
            <content:encoded><![CDATA[ <p>With many people being forced to work from home, there’s <a href="/recent-trends-in-internet-traffic/">increased load on consumer ISPs</a>. You may be asking yourself: how well is my ISP performing with even more traffic? Today we’re announcing the general availability of speed.cloudflare.com, a way to gain meaningful insights into exactly how well your network is performing.</p><p>We’ve seen a massive shift from users accessing the Internet from <a href="/covid-19-impacts-on-internet-traffic-seattle-italy-and-south-korea/">busy office districts to spread out urban areas</a>.</p><p>Although there are a slew of speed testing tools out there, none of them give you precise insights into how they came to those measurements and how they map to real-world performance. With <a href="https://speed.cloudflare.com">speed.cloudflare.com</a>, we give you insights into what we’re measuring and how exactly we calculate the scores for your network connection. Best of all, you can easily download the measurements from right inside the tool if you’d like to perform your own analysis.</p><p>We also know you care about privacy. We believe that you should know what happens with the results generated by this tool. Many other tools sell the data to third parties. Cloudflare does not sell your data. Performance data is collected and anonymized and is governed by the terms of our <a href="https://www.cloudflare.com/privacypolicy/">Privacy Policy</a>. The data is used anonymously to determine how we can improve our network, both in terms of capacity as well as to help us determine which Internet Service Providers to peer with.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2XmV7Z2R39XCeitSokM2tH/c14af6877c0e7dc4d87e96be78a43aef/image1-10.png" />
            
            </figure><p>The test has three main components: download, upload and a latency test. Each measures  different aspects of your network connection.</p>
    <div>
      <h3>Down</h3>
      <a href="#down">
        
      </a>
    </div>
    <p>For starters we run you through a basic download test. We start off downloading small files and progressively move up to larger and larger files until the test has saturated your Internet downlink. Small files (we start off with 10KB, then 100KB and so on) are a good representation of how websites will load, as these typically encompass many small files such as images, CSS stylesheets and JSON blobs.</p><p>For each file size, we show you the measurements inside a table, allowing you to drill down. Each dot in the bar graph represents one of the measurements, with the thin line delineating the range of speeds we've measured. The slightly thicker block represents the set of measurements between the 25th and 75th percentile.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4AHcHPWM661ZMzgT6vvSPK/a8ee84fa21f13641439a7bb0342ecb2b/image2-10.png" />
            
            </figure><p>Getting up to the larger file sizes we can see true maximum throughput: how much bandwidth do you really have? You may be wondering why we have to use progressively larger files. The reason is that download speeds start off slow (this is aptly called <a href="https://en.wikipedia.org/wiki/TCP_congestion_control#Slow_start">slow start</a>) and then progressively gets faster. If we were to use only small files we would never get to the maximum throughput that your network provider supports, which should be close to the Internet speed your ISP quoted you when you signed up for service.</p><p>The maximum throughput on larger files will be indicative of how fast you can download large files such as games (<a href="https://store.steampowered.com/app/271590/Grand_Theft_Auto_V/">GTA V</a> is almost 100 GB to download!) or the maximum quality that you can stream video on (lower download speed means you have to use a lower resolution to get continuous playback). We only increase download file sizes up to the absolute minimum required to get accurate measurements: no wasted bandwidth.</p>
    <div>
      <h3>Up</h3>
      <a href="#up">
        
      </a>
    </div>
    <p>Upload is the opposite of download: we send data <i>from</i> your browser <i>to</i> the Internet. This metric is more important nowadays with many people working from home: it directly affects <a href="https://www.cloudflare.com/developer-platform/solutions/live-streaming/">live video conferencing</a>. A faster upload speed means your microphone and video feed can be of higher quality, meaning people can see and hear you more clearly on videoconferences.</p><p>Measurements for upload operate in the same manner: we progressively try to upload larger and larger files up until the point we notice your connection is saturated.</p><p>Speed measurements are never 100% consistent, which is why we repeat them. An easy way for us to report your speed would be to simply report the fastest speed we see. The problem is that this will not be representative of your real-world experience: latency and packet loss constantly fluctuates, meaning you can't expect to see your maximum measured performance all the time.</p><p>To compensate for this, we take the 90th percentile of measurements, or p90 and report that instead of the absolute maximum speed that we measured. Taking the 90th percentile is a more accurate representation in that it discounts peak outliers, which is a much closer approximation of what you can expect in terms of speeds in the real world.</p>
    <div>
      <h3>Latency and Jitter</h3>
      <a href="#latency-and-jitter">
        
      </a>
    </div>
    <p>Download and upload are important metrics but don't paint the entire picture of the quality of your Internet connection. Many of us find ourselves interacting with work and friends over videoconferencing software more than ever. Although speeds matter, video is also very sensitive to the <i>latency</i> of your Internet connection. Latency represents the time an IP <i>packet</i> needs to travel from your device to the service you're using on the Internet and back. High latency means that when you're talking on a video conference, it will take longer for the other party to hear your voice.</p><p>But, latency only paints half the picture. Imagine yourself in a conversation where you have some delay before you hear what the other person says. That may be annoying but after a while you get used to it. What would be even worse is if the delay <i>differed</i> constantly: sometimes the audio is almost in sync and sometimes it has a delay of a few seconds. You can imagine how often this would result into two people starting to talk at the same time. This is directly related to how <i>stable</i> your latency is and is represented by the jitter metric. Jitter is the average variation found in consecutive latency measurements. A lower number means that the latencies measured are more consistent, meaning your media streams will have the same delay throughout the session.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4J8jZSYw2a59lXQZLn3Dtf/9704dd82d47bedfab3a8669b8c3f8a61/image4-5.png" />
            
            </figure><p>We've designed speed.cloudflare.com to be as transparent as possible: you can click into any of the measurements to see the average, median, minimum, maximum measurements, and more. If you're interested in playing around with the numbers, there's a download button that will give you the raw results we measured.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2AvTQZVVkOnGyXWDijbUkr/a138ef20812bfc813b8e3e7ee0333f91/image3-9.png" />
            
            </figure><p>The entire speed.cloudflare.com backend runs on Workers, meaning all logic runs entirely on the Cloudflare edge and your browser, no server necessary! If you're interested in seeing how the benchmarks take place, we've open-sourced the code, feel free to take a peek on our <a href="https://github.com/cloudflare/worker-speedtest-template">Github</a> repository.</p><p>We hope you'll enjoy adding this tool to your set of network debugging tools. We love being transparent and our tools reflect this: your network performance is more than just one number. Give it a <a href="https://speed.cloudflare.com">whirl</a> and let us know what you think.</p> ]]></content:encoded>
            <category><![CDATA[Performance]]></category>
            <category><![CDATA[Speed]]></category>
            <category><![CDATA[Latency]]></category>
            <category><![CDATA[COVID-19]]></category>
            <category><![CDATA[Insights]]></category>
            <guid isPermaLink="false">HF2StgkKQVmu9H1vJZ3dp</guid>
            <dc:creator>Achiel van der Mandele</dc:creator>
        </item>
    </channel>
</rss>