
<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>Tue, 14 Apr 2026 22:45:01 GMT</lastBuildDate>
        <item>
            <title><![CDATA[New standards for a faster and more private Internet]]></title>
            <link>https://blog.cloudflare.com/new-standards/</link>
            <pubDate>Wed, 25 Sep 2024 13:00:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare's customers can now take advantage of Zstandard (zstd) compression, offering 42% faster compression than Brotli and 11.3% more efficiency than GZIP. We're further optimizing performance for our customers with HTTP/3 prioritization and BBR congestion control, and enhancing privacy through Encrypted Client Hello (ECH). ]]></description>
            <content:encoded><![CDATA[ <p>As the Internet grows, so do the demands for speed and security. At Cloudflare, we’ve spent the last 14 years simplifying the adoption of the latest web technologies, ensuring that our users stay ahead without the complexity. From being the first to offer <a href="https://www.cloudflare.com/application-services/products/ssl/">free SSL certificates</a> through <a href="https://blog.cloudflare.com/introducing-universal-ssl/"><u>Universal SSL</u></a> to quickly supporting innovations like <a href="https://blog.cloudflare.com/introducing-tls-1-3"><u>TLS 1.3</u></a>, <a href="https://blog.cloudflare.com/introducing-cloudflares-automatic-ipv6-gatewa/"><u>IPv6</u></a>, and <a href="https://blog.cloudflare.com/http-3-from-root-to-tip/"><u>HTTP/3</u></a>, we've consistently made it easy for everyone to harness cutting-edge advancements.</p><p>One of the most exciting recent developments in web performance is Zstandard (zstd) — a new compression algorithm that we have found compresses data 42% faster than Brotli while maintaining almost the same compression levels. Not only that, but Zstandard reduces file sizes by 11.3% compared to GZIP, all while maintaining comparable speeds. As compression speed and efficiency directly impact latency, this is a game changer for improving user experiences across the web.</p><p>We’re also re-starting the <a href="https://blog.cloudflare.com/announcing-encrypted-client-hello/"><u>rollout of Encrypted Client Hello (ECH)</u></a>, a <a href="https://datatracker.ietf.org/doc/draft-ietf-tls-esni/"><u>new proposed standard </u></a>that prevents networks from snooping on which websites a user is visiting. <a href="https://blog.cloudflare.com/encrypted-client-hello/"><u>Encrypted Client Hello (ECH) is a successor to ESNI</u></a> and masks the <a href="https://www.cloudflare.com/en-gb/learning/ssl/what-is-sni/"><u>Server Name Indication (SNI)</u></a> that is used to negotiate a TLS handshake. This means that whenever a user visits a website on Cloudflare that has ECH enabled, no one except for the user, Cloudflare, and the website owner will be able to determine which website was visited. Cloudflare is a big proponent of privacy for everyone and is excited about the prospects of bringing this technology to life.</p><p>In this post, we also further explore our work measuring the impact of HTTP/3 prioritization, and the development of Bottleneck Bandwidth and Round-trip propagation time (BBR) congestion control to further optimize network performance.</p>
    <div>
      <h2>Introducing Zstandard compression</h2>
      <a href="#introducing-zstandard-compression">
        
      </a>
    </div>
    <p><a href="https://github.com/facebook/zstd"><u>Zstandard</u></a>, an advanced compression algorithm, was developed by <a href="https://engineering.fb.com/2018/12/19/core-infra/zstandard/"><u>Yann Collet at Facebook</u></a> and open sourced in August 2016 to manage large-scale data processing.  It has gained popularity in recent years due to its impressive compression ratios and speed. The protocol was included in <a href="https://chromestatus.com/feature/6186023867908096"><u>Chromium-based browsers</u></a> and <a href="https://connect.mozilla.org/t5/ideas/add-support-for-zstd-compression/idi-p/52155"><u>Firefox</u></a> in March 2024 as a <a href="https://caniuse.com/zstd"><u>supported</u></a> compression algorithm. </p><p>Today, we are excited to announce that Zstandard compression between Cloudflare and browsers is now available to everyone. </p><p>Our testing shows that Zstandard compresses data up to 42% faster than <a href="https://github.com/google/brotli"><u>Brotli</u></a> while achieving nearly equivalent data compression. Additionally, Zstandard outperforms <a href="https://datatracker.ietf.org/doc/html/rfc1952"><u>GZIP</u></a> by approximately 11.3% in compression efficiency, all while maintaining similar compression speeds. This means Zstandard can compress files to the same size as Brotli but in nearly half the time, speeding up your website without sacrificing performance.

This is exciting because compression speed and file size directly impacts latency. When a browser requests a resource from the origin server, the server needs time to compress the data before it’s sent over the network. A faster compression algorithm, like Zstandard, reduces this initial processing time. By also reducing the size of files transmitted over the Internet, better compression means downloads take less time to complete, websites load quicker, and users ultimately get a better experience.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6HSjbMJGtBI4GJlBp2Jf35/e2f971157f078636c6702f40f2c03a70/image2.png" />
            
            </figure>
    <div>
      <h3>Why is compression so important?</h3>
      <a href="#why-is-compression-so-important">
        
      </a>
    </div>
    <p>Website performance is crucial to the success of online businesses. <a href="https://www2.deloitte.com/content/dam/Deloitte/ie/Documents/Consulting/Milliseconds_Make_Millions_report.pdf"><u>Study</u></a> after <a href="https://www.thinkwithgoogle.com/_qs/documents/4290/c676a_Google_MobileSiteSpeed_Playbook_v2.1_digital_4JWkGQT.pdf"><u>study</u></a> has shown that an increased load time <a href="https://www.cloudflare.com/learning/performance/more/website-performance-conversion-rates/"><u>directly affects sales</u></a>. In highly competitive markets, the performance of a website is crucial for success. Just like a physical shop situated in a remote area faces challenges in attracting customers, a slow website encounters similar difficulties in attracting traffic. 

Think about buying a piece of flat pack furniture such as a bookshelf. Instead of receiving the bookshelf fully assembled, which would be expensive and cumbersome to transport, you receive it in a compact, flat box with all the components neatly organized, ready for assembly. The parts are carefully arranged to take up the least amount of space, making the package much smaller and easier to handle. When you get the item, you simply follow the instructions to assemble it to its proper state. </p><p>This is similar to how data compression works. The data is “disassembled” and packed tightly to reduce its size before being transmitted. Once it reaches its destination, it’s “reassembled” to its original form. This compression process reduces the amount of data that needs to be sent, saving bandwidth, reducing costs, and speeding up the transfer, just like how flat pack furniture reduces shipping costs and simplifies delivery logistics.</p><p>However, with compression, there is a tradeoff: time to compress versus the overall compression ratio. A compression ratio is a measure of how much a file's size is reduced during compression. For example, a 10:1 compression ratio means that the compressed file is one-tenth the size of the original. Just like assembling flat-pack furniture takes time and effort, achieving higher compression ratios often requires more processing time. While a higher compression ratio significantly reduces file size — making data transmission faster and more efficient — it may take longer to compress and decompress the data. Conversely, quicker compression methods might produce larger files, leading to faster processing but at the cost of greater bandwidth usage. Balancing these factors is key to optimizing performance in data transmission.</p><p><a href="https://w3techs.com/technologies/details/ce-compression"><u>W3 Technologies</u></a> reports that as of September 12, 2024, 88.6% of websites rely on compression to optimize speed and reduce bandwidth usage. <a href="https://datatracker.ietf.org/doc/html/rfc1952"><u>GZIP</u></a>, introduced in 1996, remains the default algorithm for many, used by 57.0% of sites due to its reasonable compression ratios and fast compression speeds. <a href="https://datatracker.ietf.org/doc/html/rfc7932"><u>Brotli</u></a>, released by Google in 2016, delivers better <a href="https://blog.cloudflare.com/results-experimenting-brotli/"><u>compression ratios</u></a>, leading to smaller file sizes, especially for static assets like JavaScript and CSS, and is used by 45.5% of websites. However, this also means that 11.4% of websites still operate without any compression, missing out on crucial performance improvements.</p><p>As the Internet and its supporting infrastructure have evolved, so have user demands for faster, more efficient performance. This growing need for higher efficiency without compromising speed is where Zstandard comes into play.</p>
    <div>
      <h3>Enter Zstandard</h3>
      <a href="#enter-zstandard">
        
      </a>
    </div>
    <p>Zstandard offers higher compression ratios comparable to GZIP, but with significantly faster compression and decompression speeds than Brotli. This makes it ideal for real-time applications that require both speed and relatively high compression ratios.</p><p>To understand Zstandard's advantages, it's helpful to know about <a href="https://blog.cloudflare.com/cloudflare-fights-cancer/"><u>Zlib</u></a>. Zlib was developed in the mid-1990s based on the <a href="https://en.wikipedia.org/wiki/DEFLATE"><u>DEFLATE</u></a> compression algorithm, which combines <a href="https://www.cloudflare.com/en-gb/learning/performance/glossary/what-is-image-compression/"><u>LZ77 and Huffman coding</u></a> to reduce file sizes. While Zlib has been a compression standard since the mid-1990s and is used in Cloudflare’s <a href="https://blog.cloudflare.com/cloudflare-fights-cancer/"><u>open-source</u></a> GZIP implementation, its design is limited by a 32 KB sliding window — a constraint from the memory limitations of that era. This makes Zlib less efficient on modern hardware, which can access far more memory.</p><p>Zstandard enhances Zlib by leveraging modern innovations and hardware capabilities. Unlike Zlib’s fixed 32 KB window, Zstandard has no strict memory constraints and can theoretically address terabytes of memory. However,  in practice, it typically uses much less, around 1 MB at lower compression levels. This flexibility allows Zstandard to buffer large amounts of data, enabling it to identify and compress repeating patterns more effectively. Zstandard also employs <a href="https://engineering.fb.com/2016/08/31/core-infra/smaller-and-faster-data-compression-with-zstandard/#:~:text=Repcode%20modeling,within%20zlib/gzip."><u>repcode modeling</u></a> to efficiently compress structured data with repetitive sequences, further reducing file sizes and enhancing its suitability for modern compression needs.</p><p>Zstandard is optimized for modern CPUs, which can execute multiple tasks simultaneously using multiple Arithmetic Logic Units (ALUs) that are used to perform mathematical tasks. Zstandard achieves this by processing data in parallel streams, dividing it into multiple parts that are processed concurrently. <a href="https://chromium.googlesource.com/external/github.com/klauspost/compress/+/refs/heads/master/huff0/"><u>The Huffman decoder, Huff0</u></a>, can decode multiple symbols in parallel on a single CPU core, and when combined with multi-threading, this leads to substantial speed improvements during both compression and decompression.</p><p>Zstandard’s branchless design is a crucial innovation that enhances CPU efficiency, especially in modern processors. To understand its significance, consider how CPUs execute instructions.</p><p>Modern CPUs use pipelining, where different stages of an instruction are processed simultaneously—like a production line—keeping all parts of the processor busy. However, when CPUs encounter a branch, such as an 'if-else' decision, they must make a <a href="https://blog.cloudflare.com/branch-predictor/"><u>branch prediction</u></a> to guess the next step. If the prediction is wrong, the pipeline must be cleared and restarted, causing slowdowns.</p><p>Zstandard avoids this issue by eliminating conditional branching. Without relying on branch predictions, it ensures the CPU can execute instructions continuously, keeping the pipeline full and avoiding performance bottlenecks.</p><p>A key feature of Zstandard is its use of <a href="https://www.rfc-editor.org/rfc/rfc8478.html#section-4.1"><u>Finite State Entropy (FSE)</u></a>, an advanced compression method that encodes data more efficiently based on probability. FSE, built on the <a href="https://en.wikipedia.org/wiki/Asymmetric_numeral_systems"><u>Asymmetric Numeral System (ANS)</u></a>, allows Zstandard to use fractional bits for encoding, unlike traditional Huffman coding, which only uses whole bits. This allows heavily repeated data to be compressed more tightly without sacrificing efficiency.</p>
    <div>
      <h3>Zstandard findings</h3>
      <a href="#zstandard-findings">
        
      </a>
    </div>
    <p>In the third quarter of 2024, we conducted extensive tests on our new Zstandard compression module, focusing on a 24-hour period where we switched the default compression algorithm from Brotli to Zstandard across our Free plan traffic. This experiment spanned billions of requests, covering a wide range of file types and sizes, including HTML, CSS, and JavaScript. The results were very promising, with significant improvements in both compression speed and file size reduction, leading to faster load times and more efficient bandwidth usage.</p>
    <div>
      <h4>Compression ratios</h4>
      <a href="#compression-ratios">
        
      </a>
    </div>
    <p>In terms of compression efficiency, Zstandard delivers impressive results. Below are the average compression ratios we observed during our testing.</p><table><tr><td><p><b>Compression Algorithm</b></p></td><td><p><b>Average Compression Ratio</b></p></td></tr><tr><td><p>GZIP</p></td><td><p>2.56</p></td></tr><tr><td><p>Zstandard</p></td><td><p>2.86</p></td></tr><tr><td><p>Brotli</p></td><td><p>3.08</p></td></tr></table>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2EA8KkP7M3j4KiEArzmXVT/2a93b972f531f02f6b253e231f73ff40/image5.png" />
            
            </figure><p>As the table shows, Zstandard achieves an average compression ratio of <b>2.86:1</b>, which is notably higher than gzip's <b>2.56:1</b> and close to Brotli’s <b>3.08:1</b>. While Brotli slightly edges out Zstandard in terms of pure compression ratio, what is particularly exciting is that we are only using Zstandard’s default compression level of 3 (out of 22) on our traffic. In the fourth quarter of 2024, we plan to experiment with higher compression levels and multithreading capabilities to further enhance Zstandard’s performance and optimize results even more.</p>
    <div>
      <h4>Compression speeds</h4>
      <a href="#compression-speeds">
        
      </a>
    </div>
    <p>What truly sets Zstandard apart is its speed. Below are the average times to compress data from our traffic-based tests measured in milliseconds:</p><table><tr><td><p><b>Compression Algorithm</b></p></td><td><p><b>Average Time to Compress (ms)</b></p></td></tr><tr><td><p>GZIP</p></td><td><p>0.872</p></td></tr><tr><td><p>Zstandard</p></td><td><p>0.848</p></td></tr><tr><td><p>Brotli</p></td><td><p>1.544</p></td></tr></table>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/15AxPyO1PmyV6hDRRjBznu/9cf16cfdc146afddbf9a3332da29629a/image10.png" />
            
            </figure><p>Zstandard not only compresses data efficiently, but it also does so <b>42% faster</b> than Brotli, with an average compression time of <b>0.848 ms</b> compared to Brotli’s <b>1.544 ms</b>. It even outperforms gzip, which compresses at <b>0.872 ms</b> on average.</p><p>From our results, we have found Zstandard strikes an excellent balance between achieving a high compression ratio and maintaining fast compression speed, making it particularly well-suited for dynamic content such as HTML and non-cacheable sensitive data. Zstandard can compress these responses from the origin quickly and efficiently, saving time compared to Brotli while providing better compression ratios than GZIP.</p>
    <div>
      <h3>Implementing Zstandard at Cloudflare</h3>
      <a href="#implementing-zstandard-at-cloudflare">
        
      </a>
    </div>
    <p>To implement Zstandard compression at Cloudflare, we needed to build it into our Nginx-based service which already handles GZIP and Brotli compression. Nginx is modular by design, with each module performing a specific function, such as compressing a response. Our custom Nginx module leverages Nginx's function 'hooks' — specifically, the header filter and body filter — to implement Zstandard compression.</p>
    <div>
      <h4>Header filter</h4>
      <a href="#header-filter">
        
      </a>
    </div>
    <p>The header filter allows us to access and modify response headers. For example, Cloudflare only compresses responses above a certain size (50 bytes for Zstandard), which is enforced with this code:</p>
            <pre><code>if (r-&gt;headers_out.content_length_n != -1 &amp;&amp;
    r-&gt;headers_out.content_length_n &lt; conf-&gt;min_length) {
    return ngx_http_next_header_filter(r);
}</code></pre>
            <p>Here, we check the "Content-Length" header. If the content length is less than our minimum threshold, we skip compression and let Nginx execute the next module.</p><p>We also need to ensure the content is not already compressed by checking the "Content-Encoding" header:</p>
            <pre><code>if (r-&gt;headers_out.content_encoding &amp;&amp;
    r-&gt;headers_out.content_encoding-&gt;value.len) {
    return ngx_http_next_header_filter(r);
}</code></pre>
            <p>If the content is already compressed, the module is bypassed, and Nginx proceeds to the next header filter.</p>
    <div>
      <h4>Body filter</h4>
      <a href="#body-filter">
        
      </a>
    </div>
    <p>The body filter hook is where the actual processing of the response body occurs. In our case, this involves compressing the data with the Zstandard encoder and streaming the compressed data back to the client. Since responses can be very large, it's not feasible to buffer the entire response in memory, so we manage internal memory buffers carefully to avoid running out of memory.</p><p>The Zstandard library is well-suited for streaming compression and provides the <code>ZSTD_compressStream2</code> function:</p>
            <pre><code>ZSTDLIB_API size_t ZSTD_compressStream2(ZSTD_CCtx* cctx,
                                        ZSTD_outBuffer* output,
                                        ZSTD_inBuffer* input,
                                        ZSTD_EndDirective endOp);</code></pre>
            <p>This function can be called repeatedly with chunks of input data to be compressed. It accepts input and output buffers and an "operation" parameter (<code>ZSTD_EndDirective endOp</code>) that controls whether to continue feeding data, flush the data, or finalize the compression process.</p><p>Nginx uses a "flush" flag on memory buffers to indicate when data can be sent. Our module uses this flag to set the appropriate Zstandard operation:</p>
            <pre><code>switch (zstd_operation) {
    case ZSTD_e_continue: {
        if (flush) {
            zstd_operation = ZSTD_e_flush;
        }
    }
}
</code></pre>
            <p>This logic allows us to switch from the "ZSTD_e_continue" operation, which feeds more input data into the encoder, to "ZSTD_e_flush", which extracts compressed data from the encoder.</p>
    <div>
      <h4>Compression cycle</h4>
      <a href="#compression-cycle">
        
      </a>
    </div>
    <p>The compression module operates in the following cycle:</p><ol><li><p>Receive uncompressed data.</p></li><li><p>Locate an internal buffer to store compressed data.</p></li><li><p>Compress the data with Zstandard.</p></li><li><p>Send the compressed data back to the client.</p></li></ol><p>Once a buffer is filled with compressed data, it’s passed to the next Nginx module and eventually sent to the client. When the buffer is no longer in use, it can be recycled, avoiding unnecessary memory allocation. This process is managed as follows:</p>
            <pre><code>if (free) {
    // A free buffer is available, so use it
    buffer = free;
} else if (buffers_used &lt; maximum_buffers) {
    // No free buffers, but we're under the limit, so allocate a new one
    buffer = create_buf();
} else {
    // No free buffers and can't allocate more
    err = no_memory;
}
</code></pre>
            
    <div>
      <h4>Handling backpressure</h4>
      <a href="#handling-backpressure">
        
      </a>
    </div>
    <p>If no buffers are available, it can lead to backpressure — a situation where the Zstandard module generates compressed data faster than the client can receive it. This causes data to become "stuck" inside Nginx, halting further compression due to memory constraints. In such cases, we stop compression and send an empty buffer to the next Nginx module, allowing Nginx to attempt to send the data to the client again. When successful, this frees up memory buffers that our module can reuse, enabling continued streaming of the compressed response without buffering the entire response in memory.</p>
    <div>
      <h3>What's next? Compression dictionaries</h3>
      <a href="#whats-next-compression-dictionaries">
        
      </a>
    </div>
    <p>The future of Internet compression lies in the use of <a href="https://datatracker.ietf.org/doc/draft-ietf-httpbis-compression-dictionary/"><u>compression dictionaries</u></a>. Both Brotli and Zstandard support dictionaries, offering up to <a href="https://developer.chrome.com/blog/shared-dictionary-compression"><u>90% improvement</u></a> on compression levels compared to using static dictionaries. </p><p>Compression dictionaries store common patterns or sequences of data, allowing algorithms to compress information more efficiently by referencing these patterns rather than repeating them. This concept is akin to how an iPhone's predictive text feature works. For example, if you frequently use the phrase "On My Way," you can customize your iPhone’s dictionary to recognize the abbreviation "OMW" and automatically expand it to "On My Way" when you type it, saving the user from typing six extra letters.</p><table><tr><td><p>O</p></td><td><p>M</p></td><td><p>W</p></td><td><p></p></td><td><p></p></td><td><p></p></td><td><p></p></td><td><p></p></td><td><p></p></td></tr><tr><td><p>O</p></td><td><p>n</p></td><td><p>
</p></td><td><p>M</p></td><td><p>y</p></td><td><p>
</p></td><td><p>W</p></td><td><p>a</p></td><td><p>y</p></td></tr></table><p>Traditionally, compression algorithms use a static dictionary defined by its RFC that is shared between clients and origin servers. This static dictionary is designed to be broadly applicable, balancing size and compression effectiveness for general use. However, Zstandard and Brotli support custom dictionaries, tailored specifically to the content being sent to the client. For example, Cloudflare could create a specialized dictionary that focuses on frequently used terms like “Cloudflare”. This custom dictionary would compress these terms more efficiently, and a browser using the same dictionary could decode them accurately, leading to significant improvements in compression and performance.</p><p>In the future, we will enable users to leverage origin-generated dictionaries for Zstandard and Brotli to enhance compression. Another exciting area we're exploring is the use of AI to create these dictionaries dynamically without them needing to be generated at the origin. By analyzing data streams in real-time, Cloudflare could develop context-aware dictionaries tailored to the specific characteristics of the data being processed. This approach would allow users to significantly improve both compression ratios and processing speed for their applications.</p>
    <div>
      <h3>Compression Rules for everyone</h3>
      <a href="#compression-rules-for-everyone">
        
      </a>
    </div>
    <p>Today we’re also excited to announce the introduction of <a href="https://developers.cloudflare.com/rules/compression-rules/"><u>Compression Rules</u></a> for all our customers. By default, Cloudflare will automatically compress certain content types based on their <a href="https://developers.cloudflare.com/speed/optimization/content/brotli/content-compression"><u>Content-Type headers</u></a>. Customers can use compression rules to optimize how and what Cloudflare compresses. This feature was previously exclusive to our Enterprise plans.

Compression Rules is built on the same robust framework as our other rules products, such as Origin Rules, Custom Firewall Rules, and Cache Rules, with additional fields for Media Type and Extension Type. This allows you to easily specify the content you wish to compress, providing granular control over your site’s performance optimization.</p><p>Compression rules are now available on all our pay-as-you-go plans and will be added to free plans in October 2024. This feature was previously exclusive to our Enterprise customers. In the table below, you’ll find the updated limits, including an increase to 125 Compression Rules for Enterprise plans, aligning with our other rule products' quotas.</p><table><tr><td><p><b>Plan Type</b></p></td><td><p><b>Free*</b></p></td><td><p><b>Pro</b></p></td><td><p><b>Business</b></p></td><td><p><b>Enterprise</b></p></td></tr><tr><td><p>Available Compression Rules</p></td><td><p>10</p></td><td><p>25</p></td><td><p>50</p></td><td><p>125</p></td></tr></table>
    <div>
      <h3>Using Compression Rules to enable Zstandard</h3>
      <a href="#using-compression-rules-to-enable-zstandard">
        
      </a>
    </div>
    <p>To integrate our Zstandard module into our platform, we also added support for it within our Compression Rules framework. This means that customers can now specify Zstandard as their preferred compression method, and our systems will automatically enable the Zstandard module in Nginx, disabling other compression modules when necessary.</p><p>The <code>Accept-Encoding</code> header determines which compression algorithms a client supports. If a browser supports Zstandard (<code>zstd</code>), and both Cloudflare and the zone have enabled the feature, then Cloudflare will return a Zstandard compressed response.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/tRIwu8JItGU0zyVmeTp2e/232af3ea43893022e5879e8361ed42b7/image4.png" />
            
            </figure><p>If the client does not support Zstandard, then Cloudflare will automatically fall back to Brotli, GZIP, or serve the content uncompressed where no compression algorithm is supported, ensuring compatibility. 

To enable Zstandard for your entire site or specifically filter on certain file types, all Cloudflare users can deploy a simple compression rule.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2WiodFBfD02mlPASLABkz5/e9a66b552ba46e423f352e502792b398/image3.png" />
            
            </figure><p>Further details and examples of what can be accomplished with Compression Rules can be found in our <a href="https://developers.cloudflare.com/rules/compression-rules/"><u>developer documentation</u></a>.</p><p>Currently, we support Zstandard, Brotli, and GZIP as compression algorithms for traffic sent to clients, and support GZIP and Brotli (since <a href="https://blog.cloudflare.com/this-is-brotli-from-origin/"><u>2023</u></a>) compressed data from the origin. We plan to implement full end-to-end support for Zstandard in 2025, offering customers another effective way to reduce their egress costs.</p><p>Once Zstandard is enabled, you can view your browser’s <a href="https://developer.chrome.com/docs/devtools/network"><u>Network Activity</u></a> log to check the content-encoding headers of the response.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1CujaUsXiEFee79Ny27Zks/1f7ef23910d4bad47c203ad311866951/image11.png" />
            
            </figure><p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5JIvK4tNjlxd7uFlWyEJoc/e426053d2c895c980f4c1370379c7b2e/image1.png" />
            
            </figure>
    <div>
      <h3>Enable Zstandard now!</h3>
      <a href="#enable-zstandard-now">
        
      </a>
    </div>
    <p>Zstandard is now available to all Cloudflare customers through <a href="https://dash.cloudflare.com/?to=/:account/:zone/rules/compression-rules"><u>Compression Rules</u></a> on our Enterprise and pay as you go plans, with free plans gaining access in October 2024. Whether you're optimizing for speed or aiming to reduce bandwidth, <a href="https://dash.cloudflare.com/?to=/:account/:zone/rules/compression-rules"><u>Compression Rules</u></a> give all customers granular control over their site's performance.</p>
    <div>
      <h2>Encrypted Client Hello (ECH)</h2>
      <a href="#encrypted-client-hello-ech">
        
      </a>
    </div>
    
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4BFVDlJK6Mxn0P1yk5UdHK/e348084642480eeaec9861a76e32d5f2/image9.png" />
          </figure><p>While performance is crucial for delivering a fast user experience, ensuring privacy is equally important in today’s Internet landscape. As we optimize for speed with Zstandard, Cloudflare is also working to protect users' sensitive information from being exposed during data transmission. With web traffic growing more complex and interconnected, it's critical to keep both performance and privacy in balance. This is where technologies like Encrypted Client Hello (ECH) come into play, securing connections without sacrificing speed.</p><p>Ten years ago, we embarked on a mission to create a more secure and encrypted web. At the time, much of the Internet remained unencrypted, leaving user data vulnerable to interception. On September 27, 2014, we took a major step forward by enabling HTTPS for free for all Cloudflare customers. Overnight, we doubled the size of the encrypted web. This set the stage for a more secure Internet, ensuring that encryption was not a privilege limited by budget but a right accessible to everyone.</p><p>Since then, both Cloudflare and the broader community have helped encrypt more of the Internet. Projects like <a href="https://letsencrypt.org/"><u>Let's Encrypt</u></a> launched to make certificates free for everyone. Cloudflare invested to encrypt more of the connection, and future-proof that encryption from coming technologies like <a href="https://blog.cloudflare.com/post-quantum-for-all/"><u>quantum computers</u></a>. We've always believed that it was everyone's right, regardless of your budget, to have an encrypted Internet at no cost.</p><p>One of the last major challenges has been securing the SNI (Server Name Identifier), which remains exposed in plaintext during the TLS handshake. This is where Encrypted Client Hello (ECH) comes in, and today, we are proud to announce that we're closing that gap. </p><p>Cloudflare announced support for <a href="https://blog.cloudflare.com/announcing-encrypted-client-hello/"><u>Encrypted Client Hello (ECH)</u></a> in 2023 and has continued to enhance its implementation in collaboration with our Internet browser partners. During a TLS handshake, one of the key pieces of information exchanged is the <a href="https://www.cloudflare.com/learning/ssl/what-is-sni/"><u>Server Name Indication (SNI)</u></a>, which is used to initiate a secure connection. Unfortunately, the SNI is sent in plaintext, meaning anyone can read it. Imagine hand-delivering a letter — anyone following you can see where you're delivering it, even if they don’t know the contents. With ECH, it is like sending the same confidential letter to a P.O. Box. You place your sensitive letter in a sealed inner envelope with the actual address. Then, you put that envelope into a larger, standard envelope addressed to a public P.O. Box, trusted to securely forward your intended recipient. The larger envelope containing the non-sensitive information is visible to everyone, while the inner envelope holds the confidential details, such as the actual address and recipient. Just as the P.O. Box maintains the anonymity of the true recipient’s address, ECH ensures that the SNI remains protected. </p><p>While encrypting the SNI is a primary motivation for ECH, its benefits extend further. ECH encrypts the entire Client Hello, ensuring user privacy and enabling TLS to evolve without exposing sensitive connection data. By securing the full handshake, ECH allows for flexible, future-proof encryption designs that safeguard privacy as the Internet continues to grow.</p>
    <div>
      <h3>How ECH works</h3>
      <a href="#how-ech-works">
        
      </a>
    </div>
    <p>Encrypted Client Hello (ECH) introduces a layer of privacy by dividing the ClientHello message into two distinct parts: a ClientHelloOuter and a ClientHelloInner. </p><ul><li><p><b>ClientHelloOuter</b>: This part remains unencrypted and contains innocuous values for sensitive TLS extensions. It sets the SNI to Cloudflare’s public name, currently set to cloudflare-ech.com. Cloudflare manages this domain and possesses the necessary certificates to handle TLS negotiations for it.</p></li><li><p><b>ClientHelloInner</b>: This part is encrypted with a public key and includes the actual server name the client wants to visit, along with other sensitive TLS extensions. The encryption scheme ensures that this sensitive data can only be decrypted by the client-facing server, which in our case is Cloudflare.</p></li></ul><p>During the TLS handshake, the ClientHelloOuter reveals only the public name (e.g., cloudflare-ech.com), while the encrypted ClientHelloInner carries the real server name. As a result, intermediaries observing the traffic will only see cloudflare-ech.com in plaintext, concealing the actual destination.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5C7EUMmlYp3gmvfvnbeKul/ea62a3c648cd859bbb46bb6fe0761645/image13.png" />
          </figure><p>The design of ECH effectively addresses many challenges in securely deploying handshake encryption, thanks to the collaborative efforts within the <a href="https://datatracker.ietf.org/doc/draft-ietf-tls-esni/"><u>IETF community</u></a>. The key to ECH’s success is its integration with other IETF standards, including the new <a href="https://datatracker.ietf.org/doc/html/rfc9460"><u>HTTPS DNS resource record</u></a>, which enables HTTPS endpoints to advertise different TLS capabilities and simplifies key distribution. By using <a href="https://blog.cloudflare.com/dns-encryption-explained/"><u>Encrypted DNS</u></a> methods, browsers and clients can anonymously query these HTTPS records. These records contain the ECH parameters needed to initiate a secure connection. </p><p>ECH leverages the <a href="https://blog.cloudflare.com/hybrid-public-key-encryption/"><u>Hybrid Public Key Encryption (HPKE)</u></a> standard, which streamlines the handshake encryption process, making it more secure and easier to implement. Before initiating a layer 4 connection, the user’s browser makes a DNS request for an HTTPS record, and zones with ECH enabled will include an ECH configuration in the HTTPS record containing an encryption public key and some associated metadata. For example, looking at the zone cloudflare-ech.com, you can see the following record returned:</p>
            <pre><code>dig cloudflare-ech.com https +short


1 . alpn="h3,h2" ipv4hint=104.18.10.118,104.18.11.118 ech=AEX+DQBB2gAgACD1W1B+GxY3nZ53Rigpsp0xlL6+80qcvZtgwjsIs4YoOwAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA= ipv6hint=2606:4700::6812:a76,2606:4700::6812:b76</code></pre>
            <p>Aside from the public key used by the client to encrypt ClientHelloInner and other <a href="https://www.ietf.org/archive/id/draft-ietf-tls-esni-20.html#name-encrypted-clienthello-confi"><u>parameters</u></a> that specify the ECH configuration, the configured public name is also present.</p>
            <pre><code>Y2xvdWRmbGFyZS1lY2guY29t</code></pre>
            <p>When the string is decoded it reveals:</p>
            <pre><code>cloudflare-ech.com</code></pre>
            <p>This public name is then used by the client in the ClientHelloOuter.</p>
    <div>
      <h3>Practical implications</h3>
      <a href="#practical-implications">
        
      </a>
    </div>
    <p>With ECH, any observer monitoring the traffic between the client and Cloudflare will see only uniform TLS handshakes that appear to be directed towards <code>cloudflare-ech.com</code>, regardless of the actual website being accessed. For instance, if a user visits <code>example.com</code>, intermediaries will not discern this specific destination but will only see <code>cloudflare-ech.com</code> in the visible handshake data. </p>
    <div>
      <h3>The problem with middleboxes</h3>
      <a href="#the-problem-with-middleboxes">
        
      </a>
    </div>
    <p>In a basic HTTPS connection, a browser (client) establishes a TLS connection directly with an origin server to send requests and download content. However, many connections on the Internet do not go directly from a browser to the server but instead pass through some form of proxy or middlebox (often referred to as a "monster-in-the-middle" or MITM). This routing through intermediaries can occur for various reasons, both benign and malicious.</p><p>One common type of HTTPS interceptor is the TLS-terminating forward proxy. This proxy sits between the client and the destination server, transparently forwarding and potentially modifying traffic. To perform this task, the proxy terminates the TLS connection from the client, decrypts the traffic, and then re-encrypts and forwards it to the destination server over a new TLS connection. To avoid browser certificate validation errors, these forward proxies typically require users to install a root certificate on their devices. This root certificate allows the proxy to generate and present a trusted certificate for the destination server, a process often managed by network administrators in corporate environments, as seen with <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/"><u>Cloudflare WARP</u></a>. These services can help prevent sensitive company data from being transmitted to unauthorized destinations, safeguarding confidentiality.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2xfA7PB61KxnbZ96XRLahh/975153cfcd831f67f47f638ec9578cdb/image8.png" />
          </figure><p>However, TLS-terminating forward proxies may not be equipped to handle Encrypted Client Hello (ECH) correctly, especially if the MITM proxy and the client facing ECH server belong to different entities.  Because the MITM proxy will terminate the TLS connection without being ECH aware, it may provide a valid certificate for the public name (in our case, cloudflare-ech.com) without being able to decrypt the ClientHelloInner or provide a new public key for the client to use. In this case, the client considers ECH to be disabled, which means you lose out on both ECH and pay the cost of an extra round trip. </p><p>We also observed that specific Cloudflare setups, such as <a href="https://developers.cloudflare.com/dns/cname-flattening/"><u>CNAME Flattening</u></a> and <a href="https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/how-it-works/"><u>Orange-to-Orange configurations</u></a>, could cause ECH to break. This issue arose because the end destination for these connections did not support TLS 1.3, preventing ECH from being processed correctly. Fortunately, in close collaboration with our browser partners, we implemented a fallback in our <a href="https://boringssl.googlesource.com/boringssl/+/d274b1bacdca36f3941bf78e43dc38acf676a1a8"><u>BoringSSL</u></a> implementation that handles TLS terminations. This fallback allows browsers to retry connections over TLS 1.2 without ECH, ensuring that a connection can be established and not break.</p><p>As a result of these improvements, we have enabled ECH by default for all Free plans, while all other plan types can manually enable it through their <a href="https://dash.cloudflare.com/?to=/:account/:zone/ssl-tls/edge-certificates#ech-card"><u>Cloudflare dashboard</u></a> or via the API. We are excited to support ECH at scale, enhancing the privacy and security of users' browsing activities. ECH plays a crucial role in safeguarding online interactions from potential eavesdroppers and maintaining the confidentiality of web activities.</p>
    <div>
      <h2>HTTP/3 Prioritization and QUIC congestion control</h2>
      <a href="#http-3-prioritization-and-quic-congestion-control">
        
      </a>
    </div>
    <p>Two other areas we are investing in to improve performance for all our customers are <a href="https://blog.cloudflare.com/better-http-3-prioritization-for-a-faster-web/"><u>HTTP/3 Prioritization</u></a> and QUIC congestion control. </p><p>HTTP/3 Prioritization focuses on efficiently managing the order in which web assets are loaded, thereby improving web performance by ensuring critical assets are delivered faster. HTTP/3 Prioritization uses Extensible Priorities to simplify prioritization with two parameters: urgency (ranging from 0-7) and a true/false value indicating whether the resource can be processed progressively. This allows resources like HTML, CSS, and images to be prioritized based on importance.</p><p>On the other hand, QUIC congestion control aims to optimize the flow of data, preventing network bottlenecks and ensuring smooth, reliable transmission even under heavy traffic conditions. </p><p>Both of these improvements significantly impact how Cloudflare’s network serves requests to clients. Before deploying these technologies across our global network, which handles peak traffic volumes of over 80 million requests per second, we first developed a reliable method to measure their impact through rigorous experimentation.</p>
    <div>
      <h3>Measuring impact</h3>
      <a href="#measuring-impact">
        
      </a>
    </div>
    <p>Accurately measuring the impact of features implemented by Cloudflare for our customers is crucial for several reasons. These measurements ensure that optimizations related to performance, security, or reliability deliver the intended benefits without introducing new issues. Precise measurement validates the effectiveness of these changes, allowing Cloudflare to assess improvements in metrics such as load times, user experience, and overall site security. One of the best ways to measure performance changes is through aggregated real-world data.</p><p><a href="https://developers.cloudflare.com/pages/how-to/web-analytics/"><u>Cloudflare Web Analytics</u></a> offers free, privacy-first analytics for your website, helping you understand the performance of your web pages as experienced by your visitors. Real User Metrics (RUM) is a vital tool in web performance optimization, capturing data from real users interacting with a website, providing insights into site performance under real-world conditions. RUM tracks various metrics directly from the user's device, including load times, resource usage, and user interactions. This data is essential for understanding the actual user experience, as it reflects the diverse environments and conditions under which the site is accessed.</p><p>A key performance indicator measured through RUM is <a href="https://web.dev/articles/vitals#core-web-vitals"><u>Core Web Vitals (CWV)</u></a>, a set of metrics defined by Google that quantify crucial aspects of user experience on the web. CWV focuses on three main areas: loading performance, interactivity, and visual stability. The specific metrics include Largest Contentful Paint (LCP), which measures loading performance; First Input Delay (FID), which gauges interactivity; and Cumulative Layout Shift (CLS), which assesses visual stability. By using the CWV measurement in RUM, developers can monitor and optimize their applications to ensure a smoother, faster, and more stable user experience and track the impact of any changes they release.</p><p>Over the last three months we have developed the capability to include valuable information in Server-Timing response headers. When a page that uses Cloudflare Web Analytics is loaded in a browser, the privacy-first client-side script from Web Analytics collects browser metrics and server-timing headers, then sends back this performance data. This data is ingested, aggregated, and made available for querying. The server-timing header includes Layer 4 information, such as Round-Trip Time (RTT) and protocol type (TCP or QUIC). Combined with Core Web Vitals data, this allows us to determine whether an optimization has positively impacted a request compared to a control sample. This capability enables us to release large-scale changes such as HTTP/3 Prioritization or BBR with a clear understanding of their impact across our global network.</p><p>An example of this header contains several key properties that provide valuable information about the network performance as observed by the server:</p>
            <pre><code>server-timing: cfL4;desc="?proto=TCP&amp;rtt=7337&amp;sent=8&amp;recv=8&amp;lost=0&amp;retrans=0&amp;sent_bytes=3419&amp;recv_bytes=832&amp;delivery_rate=548023&amp;cwnd=25&amp;unsent_bytes=0&amp;cid=94dae6b578f91145&amp;ts=225</code></pre>
            <ul><li><p><b>proto</b>: Indicates the transport protocol used</p></li><li><p><b>rtt</b>: Round-Trip Time (RTT), representing the duration of the network round trip as measured by the layer 4 connection using a smoothing algorithm.</p></li><li><p><b>sent</b>: Number of packets sent.</p></li><li><p><b>recv</b>: Number of packets received.</p></li><li><p><b>lost</b>: Number of packets lost.</p></li><li><p><b>retrans</b>: Number of retransmitted packets.</p></li><li><p><b>sent_bytes</b>: Total number of bytes sent.</p></li><li><p><b>recv_bytes</b>: Total number of bytes received.</p></li><li><p><b>delivery_rate</b>: Rate of data delivery, an instantaneous measurement in bytes per second.</p></li><li><p><b>cwnd</b>: Congestion Window, an instantaneous measurement of packet or byte count depending on the protocol.</p></li><li><p><b>unsent_bytes</b>: Number of bytes not yet sent.</p></li><li><p><b>cid</b>: A 16-byte hexadecimal opaque connection ID.</p></li><li><p><b>ts</b>: Timestamp in milliseconds, representing when the data was captured.</p></li></ul><p>This real-time collection of performance data via RUM and Server-Timing headers allows Cloudflare to make data-driven decisions that directly enhance user experience. By continuously analyzing these detailed network and performance insights, we can ensure that future optimizations, such as HTTP/3 Prioritization or BBR deployment, are delivering tangible benefits for our customers.</p>
    <div>
      <h3>Enabling HTTP/3 Prioritization for all plans</h3>
      <a href="#enabling-http-3-prioritization-for-all-plans">
        
      </a>
    </div>
    <p>As part of our focus on improving <a href="https://www.cloudflare.com/learning/performance/what-is-observability/">observability</a> through the integration of the server-timing header, we implemented several minor changes to optimize QUIC handshakes. Notably, we observed positive improvements in our telemetry due to the Layer 4 observability enhancements provided by the server-timing header. These internal findings coincided with third-party measurements, which showed similar improvements in handshake performance.</p><p>In the fourth quarter of 2024, we will apply the same experimental methodology to the HTTP/3 Prioritization support announced during Speed Week 2023. <a href="https://blog.cloudflare.com/better-http-3-prioritization-for-a-faster-web/"><u>HTTP/3 Prioritization</u></a> is designed to enhance the efficiency and speed of loading web pages by intelligently managing the order in which web assets are delivered to users. This is crucial because modern web pages are composed of numerous elements — such as images, scripts, and stylesheets — that vary in importance. Proper prioritization ensures that critical elements, like primary content and layout, load first, delivering a faster and more seamless browsing experience.</p><p>We will use this testing framework to measure performance improvements before enabling the feature across all plan types. This process allows us not only to quantify the benefits but, most importantly, to ensure there are no performance regressions.</p>
    <div>
      <h3>Congestion control</h3>
      <a href="#congestion-control">
        
      </a>
    </div>
    <p>Following the completion of the HTTP/3 Prioritization experiments we will then begin testing different congestion control algorithms, specifically focusing on <a href="https://cloud.google.com/blog/products/networking/tcp-bbr-congestion-control-comes-to-gcp-your-internet-just-got-faster"><u>BBR</u></a> (Bottleneck Bandwidth and Round-trip propagation time) version 3. Congestion control is a crucial mechanism in network communication that aims to optimize data transfer rates while avoiding network congestion. When too much data is sent too quickly over a network, it can lead to congestion, causing packet loss, delays, and reduced overall performance. Think of a busy highway during rush hour. If too many cars (data packets) flood the highway at once, traffic jams occur, slowing everyone down.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1kR4Ekkg2eUPzO4Lj8CrjY/d002875b8f77f782d13bc0ef199ba931/image6.png" />
          </figure><p>Congestion control algorithms act like traffic managers, regulating the flow of data to prevent these “traffic jams,” ensuring that data moves smoothly and efficiently across the network. Each side of a connection runs an algorithm in real time, dynamically adjusting the flow of data based on the current and predicted network conditions.

BBR is an advanced congestion control algorithm, initially developed by Google. BBR seeks to estimate the actual available bandwidth and the minimum round-trip time (RTT) to determine the optimal data flow. This approach allows BBR to maintain high throughput while minimizing latency, leading to more efficient and stable network performance.</p><p><a href="https://github.com/google/bbr/blob/v3/README.md"><u>BBR v3</u></a>, the latest iteration, builds on the strengths of its predecessors BBRv1 and BBRv2 by further refining its bandwidth estimation techniques and enhancing its adaptability to varying network conditions. We found BBR v3 to be faster in several cases compared to our previous implementation of <a href="https://datatracker.ietf.org/doc/html/rfc8312"><u>CUBIC</u></a>. Most importantly, it reduced loss and retransmission rates in our <a href="https://blog.cloudflare.com/introducing-oxy/"><u>Oxy</u></a> proxy implementation.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/61VpPQXJWTHnrnlb7dz2um/f1aecca8fea1bbeced3074d46389c3db/image7.png" />
            
            </figure><p>With these promising results, we are excited to test various congestion control algorithms including BBRv3 for <a href="https://github.com/cloudflare/quiche"><u>quiche</u></a>, our QUIC implementation, across our HTTP/3 traffic. Combining the layer 4 server-timing information with experiments in this area will enable us to explicitly control and measure the impact on real-world metrics.</p>
    <div>
      <h2>The future</h2>
      <a href="#the-future">
        
      </a>
    </div>
    <p>The future of the Internet relies on continuous innovation to meet the growing demands for speed, security, and scalability. Technologies like Zstandard for compression, BBR for congestion control, HTTP/3 prioritization, and Encrypted Client Hello are setting new standards for performance and privacy. By implementing these protocols, web services can achieve faster page load times, more efficient bandwidth usage, and stronger protections for user data.</p><p>These advancements don't just offer incremental improvements, they provide a significant leap forward in optimizing the user experience and safeguarding online interactions. At Cloudflare, we are committed to making these technologies accessible to everyone, empowering businesses to deliver better, faster, and more secure services. </p><p>Stay tuned for more developments as we continue to push the boundaries of what's possible on the web and if you’re passionate about building and implementing the latest Internet innovations, we’re <a href="https://www.cloudflare.com/careers/jobs/"><u>hiring</u></a>!</p> ]]></content:encoded>
            <category><![CDATA[Birthday Week]]></category>
            <category><![CDATA[Privacy]]></category>
            <category><![CDATA[Compression]]></category>
            <category><![CDATA[TLS]]></category>
            <guid isPermaLink="false">6YLU1FUKD4lioSrbpOnb5r</guid>
            <dc:creator>Matt Bullock</dc:creator>
            <dc:creator>Maciej Lechowski</dc:creator>
            <dc:creator>Rushil Mehra</dc:creator>
        </item>
        <item>
            <title><![CDATA[All the way up to 11: Serve Brotli from origin and Introducing Compression Rules]]></title>
            <link>https://blog.cloudflare.com/this-is-brotli-from-origin/</link>
            <pubDate>Fri, 23 Jun 2023 13:01:00 GMT</pubDate>
            <description><![CDATA[ Today, we're enhancing our support for Brotli compression, enabling end-to-end Brotli compression for web content. Compression plays a vital role in reducing bytes during transfers, ensuring quicker downloads and seamless browsing ]]></description>
            <content:encoded><![CDATA[ <p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5eudI9Wu9FybnliXMNdIib/eacd7d29901473846554ed5c678e7e8c/image8-2.png" />
            
            </figure><p>Throughout Speed Week, we have talked about the importance of <a href="https://www.cloudflare.com/learning/performance/speed-up-a-website/">optimizing performance</a>. Compression plays a crucial role by reducing file sizes transmitted over the Internet. Smaller file sizes lead to faster downloads, quicker website loading, and an improved user experience.</p><p>Take household cleaning products as a real world example. It is <a href="https://ellenmacarthurfoundation.org/circular-examples/replenish">estimated</a> “a typical bottle of cleaner is 90% water and less than 10% actual valuable ingredients”. Removing 90% of a typical 500ml bottle of household cleaner reduces the weight from 600g to 60g. This reduction means only a 60g parcel, with instructions to rehydrate on receipt, needs to be sent. Extrapolated into the gallons, this weight reduction soon becomes a huge shipping saving for businesses. Not to mention the environmental impact.</p><p>This is how compression works. The sender compresses the file to its smallest possible size, and then sends the smaller file with instructions on how to handle it when received. By reducing the size of the files sent, compression ensures the amount of bandwidth needed to send files over the Internet is a lot less. Where files are stored in <a href="/aws-egregious-egress/">expensive cloud providers like AWS</a>, reducing the size of files sent can directly equate to significant cost savings on bandwidth.</p><p>Smaller file sizes are also particularly beneficial for end users with limited Internet connections, such as mobile devices on cellular networks or users in areas with slow network speeds.</p><p>Cloudflare has always supported compression in the form of Gzip. Gzip is a widely used compression algorithm that has been around since 1992 and provides file compression for all Cloudflare users. However, in 2013 Google introduced Brotli which supports higher compression levels and better performance overall. Switching from gzip to Brotli results in smaller file sizes and faster load times for web pages. We have supported Brotli since 2017 for the connection between Cloudflare and client browsers. Today we are announcing end-to-end Brotli support for web content: support for Brotli compression, at the highest possible levels, from the origin server to the client.</p><p>If your origin server supports Brotli, turn it on, crank up the compression level, and enjoy the performance boost.</p>
    <div>
      <h3>Brotli compression to 11</h3>
      <a href="#brotli-compression-to-11">
        
      </a>
    </div>
    <p>Brotli has 12 levels of compression ranging from 0 to 11, with 0 providing the fastest compression speed but the lowest compression ratio, and 11 offering the highest compression ratio but requiring more computational resources and time. During our initial implementation of Brotli five years ago, we identified that <a href="/results-experimenting-brotli/">compression level 4</a> offered the balance between bytes saved and compression time without compromising performance.</p><p>Since 2017, Cloudflare has been using a maximum compression of Brotli level 4 for all compressible assets based on the end user's "accept-encoding" header. However, one issue was that Cloudflare only requested Gzip compression from the origin, even if the origin supported Brotli. Furthermore, Cloudflare would always decompress the content received from the origin before compressing and sending it to the end user, resulting in additional processing time. As a result, customers were unable to fully leverage the benefits offered by Brotli compression.</p><p><b>Old world</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4oE6hwo4gOyCbDmcN9yd9P/18d7e83b965bbb83190039aa2013f480/Flow_how-CF-compresses_1.png" />
            
            </figure><p>With Cloudflare now fully supporting Brotli end to end, customers will start seeing our updated accept-encoding header arriving at their origins. Once available customers can transfer, cache and serve heavily compressed Brotli files directly to us, all the way up to the maximum level of 11. This will help reduce <a href="https://www.cloudflare.com/learning/performance/glossary/what-is-latency/">latency</a> and bandwidth consumption. If the end user device does not support Brotli compression, we will automatically decompress the file and serve it either in its decompressed format or as a Gzip-compressed file, depending on the Accept-Encoding header.</p><p><b>Full end-to-end Brotli compression support</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7pQQ1tDxSU00iu1BARptlf/a448f89051e2480f2b11fe51b02d669a/Flow_how-CF-compresses_2.png" />
            
            </figure><p><b>End user cannot support Brotli compression</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4YLEb3iqcTCqQu3GdUIwzo/3913ea436fb7b81ababe03f07c82cd6d/Flow_how-CF-compresses_3.png" />
            
            </figure><p>Customers can implement Brotli compression at their origin by referring to the appropriate online materials. For example, customers that are using NGINX, can implement Brotli by following this <a href="https://github.com/google/ngx_brotli#installation">tutorial</a> and setting compression at level 11 within the <code>nginx.conf</code> configuration file as follows:</p>
            <pre><code>brotli on;
brotli_comp_level 11;
brotli_static on;
brotli_types text/plain text/css application/javascript application/x-javascript text/xml 
application/xml application/xml+rss text/javascript image/x-icon 
image/vnd.microsoft.icon image/bmp image/svg+xml;</code></pre>
            <p>Cloudflare will then serve these assets to the client at the exact same compression level (11) for the matching file brotli_types. This means any SVG or BMP images will be sent to the client compressed at Brotli level 11.</p>
    <div>
      <h3>Testing</h3>
      <a href="#testing">
        
      </a>
    </div>
    <p>We applied compression against a simple CSS file, measuring the impact of various compression algorithms and levels. Our goal was to identify potential improvements that users could experience by optimizing compression techniques. These results can be seen in the following table:</p>
<table>
<thead>
  <tr>
    <th><span>Test</span></th>
    <th><span>Size (bytes)</span></th>
    <th><span>% Reduction of original file (Higher % better)</span></th>
  </tr>
</thead>
<tbody>
  <tr>
    <td><span>Uncompressed response (no compression used)</span></td>
    <td><span>2,747</span></td>
    <td><span>-</span></td>
  </tr>
  <tr>
    <td><span>Cloudflare default Gzip compression (level 8)</span></td>
    <td><span>1,121</span></td>
    <td><span>59.21%</span></td>
  </tr>
  <tr>
    <td><span>Cloudflare default Brotli compression (level 4)</span></td>
    <td><span>1,110</span></td>
    <td><span>59.58%</span></td>
  </tr>
  <tr>
    <td><span>Compressed with max Gzip level (level 9)</span></td>
    <td><span>1,121</span></td>
    <td><span>59.21%</span></td>
  </tr>
  <tr>
    <td><span>Compressed with max Brotli level (level 11)</span></td>
    <td><span>909</span></td>
    <td><span>66.94%</span></td>
  </tr>
</tbody>
</table><p>By compressing Brotli at level 11 users are able to reduce their file sizes by 19% compared to the best Gzip compression level. Additionally, the strongest Brotli compression level is around 18% smaller than the default level used by Cloudflare. This highlights a significant size reduction achieved by utilizing Brotli compression, particularly at its highest levels, which can lead to improved website performance, faster page load times and an overall reduction in egress fees.</p><p>To take advantage of higher end to end compression rates the following Cloudflare proxy features need to be disabled.</p><ul><li><p>Email Obfuscation</p></li><li><p>Rocket Loader</p></li><li><p>Server Side Excludes (SSE)</p></li><li><p>Mirage</p></li><li><p>HTML Minification - JavaScript and CSS can be left enabled.</p></li><li><p>Automatic HTTPS Rewrites</p></li></ul><p>This is due to Cloudflare needing to decompress and access the body to apply the requested settings. Alternatively a customer can disable these features for specific paths using Configuration Rules.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/JgawIZ58Bp2hB133zV09d/3c4d5d9cc6b7fc6a558393032d124c3c/pasted-image-0-3.png" />
            
            </figure><p>If any of these rewrite features are enabled, your origin can still send Brotli compression at higher levels. However, we will decompress, apply the Cloudflare feature(s) enabled, and recompress on the fly using Cloudflare’s default Brotli level 4 or Gzip level 8 depending on the user's accept-encoding header.</p><p>For browsers that do not accept Brotli compression, we will continue to decompress and send Gzipped responses or uncompressed.</p>
    <div>
      <h3>Implementation</h3>
      <a href="#implementation">
        
      </a>
    </div>
    <p>The initial step towards implementing Brotli from the origin involved constructing a decompression module that could be integrated into Cloudflare software stack. It allows us to efficiently convert the compressed bits received from the origin into the original, uncompressed file. This step was crucial as numerous <a href="/rust-nginx-module/">features</a> such as Email Obfuscation and Cloudflare Workers Customers, rely on accessing the body of a response to apply customizations.</p><p>We integrated the decompressor into  the core <a href="https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/">reverse web proxy</a> of Cloudflare. This integration ensured that all Cloudflare products and features could access Brotli decompression effortlessly. This also allowed our Cloudflare Workers team to incorporate Brotli Directly into Cloudflare Workers allowing our Workers customers to be able to interact with responses returned in Brotli or pass through to the end user unmodified.</p>
    <div>
      <h3>Introducing Compression rules - Granular control of compression to end users</h3>
      <a href="#introducing-compression-rules-granular-control-of-compression-to-end-users">
        
      </a>
    </div>
    <p>By default Cloudflare compresses <a href="https://developers.cloudflare.com/support/speed/optimization-file-size/what-will-cloudflare-compress/">certain content types</a> based on the Content-Type header of the file. Today we are also announcing Compression Rules for our Enterprise Customers to allow you even more control on how and what Cloudflare will compress.</p><p>Today we are also announcing the introduction of Compression Rules for our Enterprise Customers. With Compression Rules, you gain enhanced control over Cloudflare's compression capabilities, enabling you to customize how and which content Cloudflare compresses to <a href="https://www.cloudflare.com/learning/performance/why-site-speed-matters/">optimize your website's performance</a>.</p><p>For example, by using Cloudflare's Compression Rules for .ktx files, customers can optimize the delivery of textures in webGL applications, enhancing the overall user experience. Enabling compression minimizes the bandwidth usage and ensures that webGL applications load quickly and smoothly, even when dealing with large and detailed textures.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/KkfvWdXDQauzqaRSxLInu/e97a02af6927b7c2a405184b7204d7dc/pasted-image-0--1--2.png" />
            
            </figure><p>Alternatively customers can disable compression or specify a preference of how we compress. Another example could be an Infrastructure company only wanting to support Gzip for their IoT devices but allow Brotli compression for all other hostnames.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4SHkVVBlGpGDOstaY6r0wQ/c9aabf11c022b67402b8fdcaa4304636/pasted-image-0--2--1.png" />
            
            </figure><p>Compression rules use the filters that our other <a href="https://developers.cloudflare.com/rules/">rules</a> products are built on top of with the added fields of Media Type and Extension type. Allowing users to easily specify the content you wish to compress.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7K6o1RMDHunR0xDyl61sdI/1f51fa7fcc17d70da034831b5461988d/pasted-image-0--3--1.png" />
            
            </figure>
    <div>
      <h3>Deprecating the Brotli toggle</h3>
      <a href="#deprecating-the-brotli-toggle">
        
      </a>
    </div>
    <p>Brotli has been long supported by some <a href="https://caniuse.com/brotli">web browsers</a> since 2016 and Cloudflare offered Brotli Support in 2017. As with all new web technologies Brotli was unknown and we gave customers the ability to selectively enable or disable BrotlI via the API and our UI.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3e78OtHsTlLbAeCYGpgfjf/f4ade0204cbcf04021a54d5f63583afc/pasted-image-0--4-.png" />
            
            </figure><p>Now that Brotli has evolved and is supported by all browsers, we plan to enable Brotli on all zones by default in the coming months. Mirroring the Gzip behavior we currently support and removing the toggle from our dashboard. If browsers do not support Brotli, Cloudflare will continue to support their accepted encoding types such as Gzip or uncompressed and Enterprise customers will still be able to use Compression rules to granularly control how we compress data towards their users.</p>
    <div>
      <h3>The future of web compression</h3>
      <a href="#the-future-of-web-compression">
        
      </a>
    </div>
    <p>We've seen great adoption and great performance for Brotli as the new compression technique for the web. Looking forward, we are closely following trends and new compression algorithms such as <a href="https://www.rfc-editor.org/rfc/rfc8478">zstd</a> as a possible next-generation compression algorithm.</p><p>At the same time, we're looking to improve Brotli directly where we can. One development that we're particularly focused on is shared dictionaries with Brotli. Whenever you compress an asset, you use a "dictionary" that helps the compression to be more efficient. A simple analogy of this is typing OMW into an iPhone message. The iPhone will automatically translate it into On My Way using its own internal dictionary.</p>
<table>
<thead>
  <tr>
    <th><span>O</span></th>
    <th><span>M</span></th>
    <th><span>W</span></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
  </tr>
</thead>
<tbody>
  <tr>
    <td><span>O</span></td>
    <td><span>n</span></td>
    <td></td>
    <td><span>M</span></td>
    <td><span>y</span></td>
    <td></td>
    <td><span>W</span></td>
    <td><span>a</span></td>
    <td><span>y</span></td>
  </tr>
</tbody>
</table><p>This internal dictionary has taken three characters and morphed this into nine characters (including spaces) The internal dictionary has saved six characters which equals performance benefits for users.</p><p>By default, the <a href="https://www.rfc-editor.org/rfc/rfc7932#page-28">Brotli RFC</a> defines a static dictionary that both clients and the origin servers use. The static dictionary was designed to be general purpose and apply to everyone. Optimizing the size of the dictionary as to not be too large whilst able to generate best compression results. However, what if an origin could generate a bespoke dictionary tailored to a specific website? For example a Cloudflare-specific dictionary would allow us to compress the words and phrases that appear repeatedly on our site such as the word “Cloudflare”. The bespoke dictionary would be designed to compress this as heavily as possible and the browser using the same dictionary would be able to translate this back.</p><p>A <a href="https://github.com/wicG/compression-dictionary-transport">new proposal</a> by the Web Incubator CG aims to do just that, allowing you to specify your own dictionaries that browsers can use to allow websites to optimize compression further. We're excited about contributing to this proposal and plan on publishing our research soon.</p>
    <div>
      <h3>Try it now</h3>
      <a href="#try-it-now">
        
      </a>
    </div>
    <p>Compression Rules are available now! With End to End Brotli being rolled out over the coming weeks. Allowing you to improve performance, reduce bandwidth and granularly control how Cloudflare handles compression to your end users.</p>
    <div>
      <h3>Watch on Cloudflare TV</h3>
      <a href="#watch-on-cloudflare-tv">
        
      </a>
    </div>
    <div></div>
<p></p><p></p> ]]></content:encoded>
            <category><![CDATA[Speed Week]]></category>
            <category><![CDATA[Compression]]></category>
            <category><![CDATA[Connectivity Cloud]]></category>
            <guid isPermaLink="false">4AAiHlaL3F2QHTZdgtQ95r</guid>
            <dc:creator>Matt Bullock</dc:creator>
        </item>
        <item>
            <title><![CDATA[My internship: Brotli compression using a reduced dictionary]]></title>
            <link>https://blog.cloudflare.com/brotli-compression-using-a-reduced-dictionary/</link>
            <pubDate>Wed, 11 Nov 2020 16:32:39 GMT</pubDate>
            <description><![CDATA[ Using the brotli dictionary to improve compression of web content without sacrificing performance. ]]></description>
            <content:encoded><![CDATA[ <p><a href="https://github.com/google/brotli">Brotli</a> is a state of the art lossless compression format, supported by all major browsers. It is capable of achieving considerably better compression ratios than the ubiquitous gzip, and is rapidly gaining in popularity. Cloudflare uses the Google brotli library to dynamically compress web content whenever possible. In 2015, we took <a href="/results-experimenting-brotli/">an in-depth look at how brotli works</a> and its compression advantages.</p><p>One of the more interesting features of the <a href="https://tools.ietf.org/html/rfc7932">brotli file format</a>, in the context of textual web content compression, is the inclusion of a built-in static dictionary. The dictionary is quite large, and in addition to containing various strings in multiple languages, it also supports the option to apply multiple transformations to those words, increasing its versatility.</p><p>The <a href="https://github.com/google/brotli">open sourced brotli library</a>, that implements an encoder and decoder for brotli, has 11 predefined quality levels for the encoder, with higher quality level demanding more CPU in exchange for a better compression ratio. The static dictionary feature is used to a limited extent starting with level 5, and to the full extent only at levels 10 and 11, due to the high CPU cost of this feature.</p><p>We improve on the limited dictionary use approach and add optimizations to improve the compression at levels 5 through 9 at a negligible performance impact when compressing web content.</p>
    <div>
      <h3>Brotli Static Dictionary</h3>
      <a href="#brotli-static-dictionary">
        
      </a>
    </div>
    <p>Brotli primarily uses the LZ77 algorithm to compress its data. Our previous blog post about <a href="/results-experimenting-brotli/">brotli compression</a> provides an introduction.</p><p>To improve compression on text files and web content, brotli also includes a static, predefined dictionary. If a byte sequence cannot be matched with an earlier sequence using LZ77 the encoder will try to match the sequence with a reference to the static dictionary, possibly using one of the multiple transforms. For example, every HTML file contains the opening  tag that cannot be compressed with LZ77, as it is unique, but it is contained in the brotli static dictionary and will be replaced by a reference to it. The reference generally takes less space than the sequence itself, which decreases the compressed file size.</p><p>The dictionary contains 13,504 words in six languages, with lengths from 4 to 24 characters. To improve the compression of real-world text and web data, some dictionary words are common phrases ("The current") or strings common in web content (‘type=”text/javascript”’). Unlike usual LZ77 compression, a word from the dictionary can only be matched as a whole. Starting a match in the middle of a dictionary word, ending it before the end of a word or even extending into the next word is not supported by the brotli format.</p><p>Instead, the dictionary supports 120 transforms of dictionary words to support a larger number of matches and find longer matches. The transforms include adding suffixes (“work” becomes “working”) adding prefixes (“book” =&gt; “ the book”) making the first character uppercase ("process" =&gt; "Process") or converting the whole word to uppercase (“html” =&gt; “HTML”). In addition to transforms that make words longer or capitalize them, the cut transform allows a shortened match (“consistently” =&gt; “consistent”), which makes it possible to find even more matches.</p>
    <div>
      <h3>Methods</h3>
      <a href="#methods">
        
      </a>
    </div>
    <p>With the transforms included, the static dictionary contains 1,633,984 different words – too many for exhaustive search, except when used with the slow brotli compression levels 10 and 11. When used at a lower compression level, brotli either disables the dictionary or only searches through a subset of roughly 5,500 words to find matches in an acceptable time frame. It also only considers matches at positions where no LZ77 match can be found and only uses the cut transform.</p><p>Our approach to the brotli dictionary uses a larger, but more specialized subset of the dictionary than the default, using more aggressive heuristics to improve the compression ratio with negligible cost to performance. In order to provide a more specialized dictionary, we provide the compressor with a content type hint from our servers, relying on the Content-Type header to tell the compressor if it should use a dictionary for HTML, JavaScript or CSS. The dictionaries can be furthermore refined by colocation language in the future.</p>
    <div>
      <h3>Fast dictionary lookup</h3>
      <a href="#fast-dictionary-lookup">
        
      </a>
    </div>
    <p>To improve compression without sacrificing performance, we needed a fast way to find matches if we want to search the dictionary more thoroughly than brotli does by default. Our approach uses three data structures to find a matching word directly. The radix trie is responsible for finding the word while the hash table and bloom filter are used to speed up the radix trie and quickly eliminate many words that can’t be matched using the dictionary.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3JdGRN9pPZ0GPTRvecnh0P/a3a4bb1289e25385b098c719ad865a1e/image1.png" />
            
            </figure><p>Lookup for a position starting with “type”</p><p>The <b>radix trie</b> easily finds the longest matching word without having to try matching several words. To find the match, we traverse the graph based on the text at the current position and remember the last node with a matching word. The radix trie supports compressed nodes (having more than one character as an edge label), which greatly reduces the number of nodes that need to be traversed for typical dictionary words.</p><p>The radix trie is slowed down by the large number of positions where we can’t find a match. An important finding is that most mismatching strings have a mismatching character in the first four bytes. Even for positions where a match exists, a lot of time is spent traversing nodes for the first four bytes since the nodes close to the tree root usually have many children.</p><p>Luckily, we can use a <b>hash table</b> to look up the node equivalent to four bytes, matching if it exists or reject the possibility of a match. We thus look up the first four bytes of the string, if there is a matching node we traverse the trie from there, which will be fast as each four-byte prefix usually only has a few corresponding dict words. If there is no matching node, there will not be a matching word at this position and we do not need to further consider it.</p><p>While the hash table is designed to reject mismatches quickly and avoid cache misses and high search costs in the trie, it still suffers from similar problems: We might search through several 4-byte prefixes with the hash value of the given position, only to learn that no match can be found. Additionally, hash lookups can be expensive due to cache misses.</p><p>To quickly reject words that do not match the dictionary, but might still cause cache misses, we use a <b>k=1 bloom filter</b> to quickly rule out most non-matching positions. In the k=1 case, the filter is simply a lookup table with one bit indicating whether any matching 4-byte prefixes exist for a given hash value. If the hash value for the given bit is 0, there won’t be a match. Since the bloom filter uses at most one bit for each four-byte prefix while the hash table requires 16 bytes, cache misses are much less likely. (The actual size of the structures is a bit different since there are many empty spaces in both structures and the bloom filter has twice as many elements to reject more non-matching positions.)</p><p>This is very useful for performance as a bloom filter lookup requires a single memory access. The bloom filter is designed to be fast and simple, but still rejects more than half of all non-matching positions and thus allows us to save a full hash lookup, which would often mean a cache miss.</p>
    <div>
      <h2>Heuristics</h2>
      <a href="#heuristics">
        
      </a>
    </div>
    <p>To improve the compression ratio without sacrificing performance, we employed a number of heuristics:</p><p><b>Only search the dictionary at some positions</b>This is also done using the stock dictionary, but we search more aggressively. While the stock dictionary only considers positions where the LZ77 match finder did not find a match, we also consider positions that have a bad match according to the brotli cost model: LZ77 matches that are short or have a long distance between the current position and the reference usually only offer a small compression improvement, so it is worth trying to find a better match in the static dictionary.</p><p><b>Only consider the longest match and then transform it</b>Instead of finding and transforming all matches at a position, the radix trie only gives us the longest match which we then transform. This approach results in a vast performance improvement. In most cases, this results in finding the best match.</p><p><b>Only include some transforms</b>While all transformations can improve the compression ratio, we only included those that work well with the data structures. The suffix transforms can easily be applied after finding a non-transformed match. For the upper case transforms, we include both the non-transformed and the upper case version of a word in the radix trie. The prefix and cut transforms do not play well with the radix trie, therefore a cut of more than 1 byte and prefix transforms are not supported.</p>
    <div>
      <h2>Generating the reduced dictionary</h2>
      <a href="#generating-the-reduced-dictionary">
        
      </a>
    </div>
    <p>At low compression levels, brotli searches a subset of ~5,500 out of 13,504 words of the dictionary, negatively impacting compression. To store the entire dictionary, we would need to store ~31,700 words in the trie considering the upper case transformed output of ASCII sequences and ~11,000 four-byte prefixes in the hash. This would slow down hash table and radix trie, so we needed to find a different subset of the dictionary that works well for web content.</p><p>For this purpose, we used a large data set containing representative content. We made sure to use web content from several world regions to reflect language diversity and optimize compression. Based on this data set, we identified which words are most common and result in the largest compression improvement according to the brotli cost model. We only include the most useful words based on this calculation. Additionally, we remove some words if they slow down hash table lookups of other, more common words based on their hash value.</p><p>We have generated separate dictionaries for HTML, CSS and JavaScript content and use the MIME type to identify the right dictionary to use. The dictionaries we currently use include about 15-35% of the entire dictionary including uppercase transforms. Depending on the type of data and the desired compression/speed tradeoff, different options for the size of the dictionary can be useful. We have also developed code that automatically gathers statistics about matches and generates a reduced dictionary based on this, which makes it easy to extend this to other textual formats, perhaps data that is majority non-English or XML data and achieve better results for this type of data.</p>
    <div>
      <h2>Results</h2>
      <a href="#results">
        
      </a>
    </div>
    <p>We tested the reduced dictionary on a large data set of HTML, CSS and JavaScript files.</p><p>The improvement is especially big for small files as the LZ77 compression is less effective on them. Since the improvement on large files is a lot smaller, we only tested files up to 256KB. We used compression level 5, the same compression level we currently use for dynamic compression on our edge, and tested on a Intel Core i7-7820HQ CPU.</p><p>Compression improvement is defined as 1 - (compressed size using the reduced dictionary / compressed size without dictionary). This ratio is then averaged for each input size range. We also provide an average value weighted by file size. Our data set mirrors typical web traffic, covering a wide range of file sizes with small files being more common, which explains the large difference between the weighted and unweighted average.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6fjLER7c308iIMA3kQx9Qs/cc51eec61ad9c4b9b3637e7cfec397cb/image-2.png" />
            
            </figure><p>With the improved dictionary approach, we are now able to compress HTML, JavaScript and CSS files as well, or sometimes even better than using a higher compression level would allow us, all while using only 1% to 3% more CPU. For reference using compression level 6 over 5 would increase CPU usage by up to 12%.</p> ]]></content:encoded>
            <category><![CDATA[Compression]]></category>
            <category><![CDATA[Optimization]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Careers]]></category>
            <guid isPermaLink="false">34MUGp0LHm4s1IhKECGkCR</guid>
            <dc:creator>Felix Hanau</dc:creator>
        </item>
        <item>
            <title><![CDATA[A Solution to Compression Oracles on the Web]]></title>
            <link>https://blog.cloudflare.com/a-solution-to-compression-oracles-on-the-web/</link>
            <pubDate>Tue, 27 Mar 2018 12:00:00 GMT</pubDate>
            <description><![CDATA[ Compression is often considered an essential tool when reducing the bandwidth usage of internet services. The impact that the use of such compression schemes can have on security, however, has often been overlooked.  ]]></description>
            <content:encoded><![CDATA[ <p></p><p><a href="https://commons.wikimedia.org/wiki/File:Ressort_de_compression.jpg">CC 3.0 by Jean-Jacques MILAN</a></p><p><i>This is a guest post by Blake Loring, a PhD student at Royal Holloway, University of London. Blake worked at Cloudflare as an intern in the summer of 2017.</i></p><p>Compression is often considered an essential tool when reducing the bandwidth usage of internet services. The impact that the use of such compression schemes can have on security, however, has often been overlooked. The recently detailed <a href="https://en.wikipedia.org/wiki/CRIME">CRIME</a>, <a href="http://breachattack.com/">BREACH</a>, <a href="https://www.blackhat.com/eu-13/briefings.html#Beery">TIME</a> and <a href="https://www.blackhat.com/docs/us-16/materials/us-16-VanGoethem-HEIST-HTTP-Encrypted-Information-Can-Be-Stolen-Through-TCP-Windows-wp.pdf">HEIST</a> attacks on TLS have shown that if an attacker can make requests on behalf of a user then secret information can be extracted from encrypted messages using only the length of the response. Deciding whether an element of a web-page should be secret often depends on the content of the page, however there are some common elements of web-pages which should always remain secret such as <a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery">Cross-Site Request Forgery (CSRF)</a> tokens. Such tokens are used to ensure that malicious webpages cannot forge requests from a user by enforcing that any request must contain a secret token included in a previous response.</p><p>I worked at Cloudflare last summer to investigate possible solutions to this problem. The result is a project called <a href="https://github.com/cloudflare/cf-nocompress">cf-nocompress</a>. The aim of this project was to develop a tool which automatically mitigates instances of the attack, in particular CSRF extraction, on Cloudflare hosted services transparently without significantly impacting the effectiveness of compression. We have published a <a href="https://github.com/cloudflare/cf-nocompress/tree/master/cf-nocompress">proof-of-concept implementation</a> on GitHub, and provide a <a href="https://compression.website">challenge site</a> and <a href="https://github.com/cloudflare/cf-nocompress/tree/master/example_attack/src/main">tool</a> which demonstrates the attack in action).</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/OWY4iAZGfQjKwmBgRkway/2bc96e67c7df62a5316ec450b5f56142/compression.website.jpg" />
            
            </figure>
    <div>
      <h3>The Problem</h3>
      <a href="#the-problem">
        
      </a>
    </div>
    <p>Most web compression schemes reduce the size of data by replacing common sequences with references to a dictionary of terms created during the compression. When using such compression schemes the size of the encrypted response will be reduced if there are repeated strings within the plaintext. This can be exploited through the use of a canary, an element in a request which we know will be added to the response, to test whether a string exists within the original response using the compressed response length. From this we can extract the contents of portions of a webpage incrementally by guessing each subsequent character. This attack creates an opportunity for malicious JavaScript to extract CSRF tokens and other confidential information from a webpage through malicious code served to a browser using either a packet sniffer (a methodology created by Duong and Rizzo as part of the <a href="https://blog.cryptographyengineering.com/2011/09/21/brief-diversion-beast-attack-on-tlsssl/">BEAST attack</a>) or JavaScript APIs which reveal network statistics (described by Vanhoef and Van Goethem in <a href="https://www.blackhat.com/docs/us-16/materials/us-16-VanGoethem-HEIST-HTTP-Encrypted-Information-Can-Be-Stolen-Through-TCP-Windows.pdf">HEIST</a>).</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/23iahybYJxsleUyYAbpWoQ/81440ac0fe67f75711e7d1c5608d41ef/Artboard---4.png" />
            
            </figure><p>There are two common mitigation schemes for this attack. The first is to send a unique CSRF every time a page is loaded. By removing the consistent element from the page the threat of attack is removed. This approach requires the server to keep state of valid CSRFs and whether they have been used, additionally it can only be used to protect page tokens and not user-readable data. Another approach is to XOR all secrets in a response with a per-request random number and then transmit the number with the response. Once received a piece of JavaScript can then be used to recover the original secret by XORing the data again. Alternatively, the server can be modified to expect the XOR variant and the random number rather than the original secret. This approach allows for all secrets to be protected, however it requires client side post-processing. Additionally, both approaches require extensive, per page, modification which make mitigation incredibly cumbersome in practice. At present the only way to fully mitigate such an attack is to disable compression entirely on vulnerable websites, an impractical solution for most websites and content delivery networks.</p>
    <div>
      <h3>Our Solution</h3>
      <a href="#our-solution">
        
      </a>
    </div>
    <p>We decided to use selective compression, compressing only non-secret parts of a page, in order to stop the extraction of secret information from a page. We found that in most cases a secret within a webpage can be described in terms of a classical regular expression. These descriptions allow us to identify secrets online as a response is streamed. Once the secrets are identified they can be flagged so that a modified compression library can ensure that they are not added to the dictionary. The primary advantage of this approach is that protection can be offered transparently by the web-server and the application does not need to be modified as long as a regular expression can be used to clearly express which portions of a response are secret. In addition, we do not need to maintain state for each user or require client-side JavaScript to appropriately render the page.</p><p>The proof-of-concept is implemented as a plugin for NGINX and requires a small patch to the gzip module. The plugin uses <a href="https://github.com/openresty/sregex">sregex</a> to identify secrets within a page. The modified gzip functions as normal, however when a secret is processed compression is disabled. This ensures secrets do not get added to the compression dictionary, removing any on response size.</p>
    <div>
      <h3>Additional security considerations</h3>
      <a href="#additional-security-considerations">
        
      </a>
    </div>
    <p>The regular expression matching engine we use in this proof-of-concept is not guaranteed to run in constant time. As such, matching a string against some regular expressions could introduce a timing based side-channel attack. This issue is compounded by the complexity of modern regular expressions as matching time can often be non-intuitive. Whilst in many cases the risk such an attack would pose is minimal, a limited matcher with constant runtime and restrictions on unbounded loops should be developed if our mitigation is adopted.</p>
    <div>
      <h3>The Challenge Site</h3>
      <a href="#the-challenge-site">
        
      </a>
    </div>
    <p>We have set up the challenge website <a href="https://compression.website/">compression.website</a> with protection, and a clone of the site <a href="https://compression.website/unsafe/">compression.website/unsafe</a> without it. The page is a simple form with a per-client CSRF designed to emulate common CSRF protection. Using the example attack presented with the library we have shown that we are able to extract the CSRF from the size of request responses in the unprotected variant but we have not been able to extract it on the protected site. We welcome attempts to extract the CSRF without access to the unencrypted response.</p> ]]></content:encoded>
            <category><![CDATA[Compression]]></category>
            <category><![CDATA[HTTPS]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Cryptography]]></category>
            <guid isPermaLink="false">6s2fKoLeMHkDIfUFiJ7gxJ</guid>
            <dc:creator>Guest Author</dc:creator>
        </item>
        <item>
            <title><![CDATA[Everyone can now run JavaScript on Cloudflare with Workers]]></title>
            <link>https://blog.cloudflare.com/cloudflare-workers-unleashed/</link>
            <pubDate>Tue, 13 Mar 2018 13:00:00 GMT</pubDate>
            <description><![CDATA[ We believe the true dream of cloud computing is that your code lives in the network itself. Your code doesn't run in "us-west-4", it runs everywhere. ]]></description>
            <content:encoded><![CDATA[ <p><i>This post is also available in </i><a href="/ja-jp/cloudflare-workers-unleashed-ja-jp/"><i>日本語</i></a><i>.</i></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3Q5P6uXQ3lvPtobTplWq5U/7ef904ddcd1f9c2284caf65fd3b4511d/workers-social.png" />
            
            </figure><p>Exactly one year ago today, Cloudflare gave me a mission: Make it so people can run code on Cloudflare's edge. At the time, we didn't yet know what that would mean. Would it be container-based? A new Turing-incomplete domain-specific language? Lua? "Functions"? There were lots of ideas.</p><p>Eventually, we settled on what now seems the obvious choice: JavaScript, using the standard Service Workers API, running in a new environment built on V8. Five months ago, we <a href="/introducing-cloudflare-workers/">gave you a preview</a> of what we were building, and started the beta.</p><p>Today, with thousands of scripts deployed and many billions of requests served, <a href="https://www.cloudflare.com/developer-platform/workers/">Cloudflare Workers</a> is now ready for everyone.</p><p>"Moving away from VCL and adopting Cloudflare Workers will allow us to do some creative routing that will let us deliver JavaScript to npm's millions of users even faster than we do now. We will be building our next generation of services on Cloudflare's platform and we get to do it in JavaScript!"</p><p>— CJ Silverio, CTO, npm, Inc.</p>
    <div>
      <h3>What is the Cloud, really?</h3>
      <a href="#what-is-the-cloud-really">
        
      </a>
    </div>
    <p>Historically, web application code has been split between servers and browsers. Between them lies a vast but fundamentally <i>dumb</i> network which merely ferries data from point to point.</p><p>We don't believe this lives up to the promise of "The Cloud."</p><p>We believe the true dream of cloud computing is that your code lives in the network itself. Your code doesn't run in "us-west-4" or "South Central Asia (Mumbai)", it runs <i>everywhere</i>.</p><p>More concretely, it should run where it is most needed. When responding to a user in New Zealand, your code should run in New Zealand. When crunching data in your database, your code should run on the machines that store the data. When interacting with a third-party API, your code should run wherever that API is hosted. When human explorers reach Mars, they aren't going to be happy waiting a half an hour for your app to respond -- your code needs to be running on Mars.</p><p>Cloudflare Workers are our first step towards this vision. When you deploy a Worker, it is deployed to Cloudflare's entire edge network of over a hundred locations worldwide in under 30 seconds. Each request for your domain will be handled by your Worker at a Cloudflare location close to the end user, with no need for you to think about individual locations. The more locations we bring online, the more your code just "runs everywhere."</p><p>Well, OK… it won't run on Mars. Yet. You out there, Elon?</p>
    <div>
      <h3>What's a Worker?</h3>
      <a href="#whats-a-worker">
        
      </a>
    </div>
    <p>Cloudflare Workers derive their name from Web Workers, and more specifically Service Workers, the W3C standard API for scripts that run in the background in a web browser and intercept HTTP requests. Cloudflare Workers are written against the same standard API, but run on Cloudflare's servers, not in a browser.</p><p>Here are the tools you get to work with:</p><ul><li><p>Execute any JavaScript code, using the latest standard language features.</p></li><li><p>Intercept and modify HTTP request and response URLs, status, headers, and body content.</p></li><li><p>Respond to requests directly from your Worker, or forward them elsewhere.</p></li><li><p>Send HTTP requests to third-party servers.</p></li><li><p>Send multiple requests, in serial or parallel, and use the responses to compose a final response to the original request.</p></li><li><p>Send asynchronous requests after the response has already been returned to the client (for example, for logging or analytics).</p></li><li><p>Control other Cloudflare features, such as caching behavior.</p></li></ul><p>The possible uses for Workers are infinite, and we're excited to see what our customers come up with. Here are some ideas we've seen in the beta:</p><ul><li><p>Route different types of requests to different origin servers.</p></li><li><p>Expand HTML templates on the edge, to <a href="https://www.cloudflare.com/learning/cdn/how-cdns-reduce-bandwidth-cost/">reduce bandwidth costs</a> at your origin.</p></li><li><p>Apply <a href="https://www.cloudflare.com/learning/access-management/what-is-access-control/">access control</a> to cached content.</p></li><li><p>Redirect a fraction of users to a staging server.</p></li><li><p>Perform A/B testing between two entirely different back-ends.</p></li><li><p>Build "<a href="https://www.cloudflare.com/learning/serverless/what-is-serverless/">serverless</a>" applications that rely entirely on web APIs.</p></li><li><p>Create custom security filters to block unwanted traffic unique to your app.</p></li><li><p>Rewrite requests to <a href="https://www.cloudflare.com/learning/cdn/what-is-a-cache-hit-ratio/">improve cache hit rate</a>.</p></li><li><p>Implement custom <a href="https://www.cloudflare.com/learning/performance/what-is-load-balancing/">load balancing</a> and failover logic.</p></li><li><p>Apply quick fixes to your application without having to update your production servers.</p></li><li><p>Collect analytics without running code in the user's browser.</p></li><li><p>Much more.</p></li></ul><p>Here's an example.</p>
            <pre><code>// A Worker which:
// 1. Redirects visitors to the home page ("/") to a
//    country-specific page (e.g. "/US/").
// 2. Blocks hotlinks.
// 3. Serves images directly from Google Cloud Storage.
addEventListener('fetch', event =&gt; {
  event.respondWith(handle(event.request))
})

async function handle(request) {
  let url = new URL(request.url)
  if (url.pathname == "/") {
    // This is a request for the home page ("/").
    // Redirect to country-specific path.
    // E.g. users in the US will be sent to "/US/".
    let country = request.headers.get("CF-IpCountry")
    url.pathname = "/" + country + "/"
    return Response.redirect(url, 302)

  } else if (url.pathname.startsWith("/images/")) {
    // This is a request for an image (under "/images").
    // First, block third-party referrers to discourage
    // hotlinking.
    let referer = request.headers.get("Referer")
    if (referer &amp;&amp;
        new URL(referer).hostname != url.hostname) {
      return new Response(
          "Hotlinking not allowed.",
          { status: 403 })
    }

    // Hotlink check passed. Serve the image directly
    // from Google Cloud Storage, to save serving
    // costs. The image will be cached at Cloudflare's
    // edge according to its Cache-Control header.
    url.hostname = "example-bucket.storage.googleapis.com"
    return fetch(url, request)
  } else {
    // Regular request. Forward to origin server.
    return fetch(request)
  }
}</code></pre>
            
    <div>
      <h3>It's Really Fast</h3>
      <a href="#its-really-fast">
        
      </a>
    </div>
    <p>Sometimes people ask us if JavaScript is "slow". Nothing could be further from the truth.</p><p>Workers uses the V8 JavaScript engine built by Google for Chrome. V8 is not only one of the fastest implementations of JavaScript, but one of the fastest implementations of any dynamically-typed language, period. Due to the immense amount of work that has gone into optimizing V8, it outperforms just about any popular server programming language with the possible exceptions of C/C++, Rust, and Go. (Incidentally, we will support those soon, via WebAssembly.)</p><p>The bottom line: <b>A typical Worker script executes in less than one millisecond.</b> Most users are unable to measure any <a href="https://www.cloudflare.com/learning/performance/glossary/what-is-latency/">latency</a> difference when they enable Workers -- except, of course, when their worker actually <i>improves</i> latency by responding directly from the edge.</p><p>On another speed-related note, Workers deploy fast, too. <b>Workers deploy globally in under 30 seconds</b> from the time you save and enable the script.</p>
    <div>
      <h3>Pricing</h3>
      <a href="#pricing">
        
      </a>
    </div>
    <p>Workers are a paid add-on to Cloudflare. We wanted to keep the pricing as simple as possible, so here's the deal:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4FhizNXvRr918DQvbZPx9V/218889d4e4623f62dfe041a5590e650f/workers-pricing-text_4x.png" />
            
            </figure>
    <div>
      <h3>Get Started</h3>
      <a href="#get-started">
        
      </a>
    </div>
    <ul><li><p><a href="https://www.cloudflare.com/a/overview">Log into your Cloudflare account</a> and visit the "Workers" section to configure Workers.</p></li><li><p><a href="https://cloudflareworkers.com/">Experiment with Workers in the Playground</a>, no account required.</p></li><li><p><a href="https://developers.cloudflare.com/workers/">Read the documentation</a> to learn how Workers are written.</p></li><li><p><a href="/introducing-cloudflare-workers/">Check out the original announcement blog post</a> for more technical details.</p></li><li><p><a href="https://community.cloudflare.com/c/developers/workers">Discuss Workers in the Cloudflare Community.</a></p></li></ul><p>"Cloudflare Workers saves us a great deal of time. Managing bot traffic without Workers would consume valuable development and server resources that are better spent elsewhere."</p><p>— John Thompson, Senior System Administrator, MaxMind</p> ]]></content:encoded>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[JavaScript]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Compression]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">4srwZERwM1P2louT4gzvty</guid>
            <dc:creator>Kenton Varda</dc:creator>
        </item>
        <item>
            <title><![CDATA[Squeezing the firehose: getting the most from Kafka compression]]></title>
            <link>https://blog.cloudflare.com/squeezing-the-firehose/</link>
            <pubDate>Mon, 05 Mar 2018 16:17:03 GMT</pubDate>
            <description><![CDATA[ How Cloudflare was able to save hundreds of gigabits of network bandwidth and terabytes of storage from Kafka. ]]></description>
            <content:encoded><![CDATA[ <p>We at Cloudflare are long time <a href="https://kafka.apache.org/">Kafka</a> users, first mentions of it date back to beginning of 2014 when the most recent version was 0.8.0. We use Kafka as a log to power analytics (both HTTP and DNS), <a href="https://www.cloudflare.com/learning/ddos/ddos-mitigation/">DDoS mitigation</a>, logging and metrics.</p><p>While the idea of unifying abstraction of the log remained the same since then (<a href="https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying">read this classic blog post</a> from Jay Kreps if you haven't), Kafka evolved in other areas since then. One of these improved areas was compression support. Back in the old days we've tried enabling it a few times and ultimately gave up on the idea because of <a href="https://github.com/Shopify/sarama/issues/805">unresolved</a> <a href="https://issues.apache.org/jira/browse/KAFKA-1718">issues</a> in the protocol.</p>
    <div>
      <h3>Kafka compression overview</h3>
      <a href="#kafka-compression-overview">
        
      </a>
    </div>
    <p>Just last year Kafka 0.11.0 came out with the new improved protocol and log format.</p><p>The naive approach to compression would be to compress messages in the log individually:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4hIZ5PFDUxsm8R48jv6TfL/2d066d744d9e89775c35424db5b9f6d5/Screen-Shot-2018-03-05-at-12.10.00-PM.png" />
            
            </figure><p>Edit: originally we said this is how Kafka worked before 0.11.0, but that appears to be false.</p><p>Compression algorithms work best if they have more data, so in the new log format messages (now called records) are packed back to back and compressed in batches. In the previous log format messages recursive (compressed set of messages is a message), new format makes things more straightforward: compressed batch of records is just a batch.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6WMDU1akGFipMtPWFPXXJB/e0bc3c5bffc3bb215251c4bc33598fda/Screen-Shot-2018-03-05-at-12.10.13-PM.png" />
            
            </figure><p>Now compression has a lot more space to do its job. There's a high chance that records in the same Kafka topic share common parts, which means they can be compressed better. On the scale of thousands of messages difference becomes enormous. The downside here is that if you want to read record3 in the example above, you have to fetch records 1 and 2 as well, whether the batch is compressed or not. In practice this doesn't matter too much, because consumers usually read all records sequentially batch after batch.</p><p>The beauty of compression in Kafka is that it lets you trade off CPU vs disk and network usage. The protocol itself is designed to minimize overheads as well, by requiring decompression only in a few places:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/572hmHvuR97iuBVjOqD5SS/a5a10aceffd450e8b6563e94966cc53c/Screen-Shot-2018-03-05-at-12.10.19-PM.png" />
            
            </figure><p>On the receiving side of the log only consumers need to decompress messages:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Jyhzp3FFqxOEUML1Zw3Cv/85d1d134fd87dc74820da7afe99c090e/Screen-Shot-2018-03-05-at-12.10.25-PM.png" />
            
            </figure><p>In reality, if you don't use encryption, data can be copied between NIC and disks with <a href="https://www.ibm.com/developerworks/linux/library/j-zerocopy/">zero copies to user space</a>, lowering the cost to some degree.</p>
    <div>
      <h3>Kafka bottlenecks at Cloudflare</h3>
      <a href="#kafka-bottlenecks-at-cloudflare">
        
      </a>
    </div>
    <p>Having less network and disk usage was a big selling point for us. Back in 2014 we started with spinning disks under Kafka and never had issues with disk space. However, at some point we started having issues with random io. Most of the time consumers and replicas (which are just another type of consumer) read from the very tip of the log, and that data resides in page cache meaning you don't need to read from disks at all:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/LFhjE9d3QMnMvDRS4mtlJ/3e47d9695821f3374c4ad326cb32cce3/Screen-Shot-2018-03-01-at-13.59.06.png" />
            
            </figure><p>In this case the only time you touch the disk is during writes, and sequential writes are cheap. However, things start to fall apart when you have multiple lagging consumers:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2b3aYKJupVoKGdx3tLmTjq/1b740bb0dda4e74025d8299b9d808abc/Screen-Shot-2018-03-01-at-13.59.29.png" />
            
            </figure><p>Each consumer wants to read different part of the log from the physical disk, which means seeking back and forth. One lagging consumer was okay to have, but multiple of them would start fighting for disk io and just increase lag for all of them. To work around this problem we upgraded to SSDs.</p><p>Consumers were no longer fighting for disk time, but it felt terribly wasteful most of the time when consumers are not lagging and there's zero read io. We were not bored for too long, as other problems emerged:</p><ul><li><p>Disk space became a problem. SSDs are much more expensive and usable disk space reduced by a lot.</p></li><li><p>As we grew, we started saturating network. We used 2x10Gbit NICs and imperfect balance meant that we sometimes saturated network links.</p></li></ul><p>Compression promised to solve both of these problems, so we were eager to try again with improved support from Kafka.</p>
    <div>
      <h3>Performance testing</h3>
      <a href="#performance-testing">
        
      </a>
    </div>
    <p>At Cloudflare, we use Go extensively, which means that a lot of our Kafka consumers and producers are in Go. This means we can't just take off-the-shelf Java client provided by Kafka team with every server release and start enjoying the benefits of compression. We had to get support from our Kafka client library first (we use <a href="https://github.com/Shopify/sarama">sarama from Shopify</a>). Luckily, support was added at the end of 2017. With more fixes from our side we were able to get the test setup working.</p><p>Kafka supports 4 compression codecs: <code>none</code>, <code>gzip</code>, <code>lz4</code> and <code>snappy</code>. We had to figure out how these would work for our topics, so we wrote a simple producer that copied data from existing topic into destination topic. With four destination topics for each compression type we were able to get the following numbers.</p><p>Each destination topic was getting roughly the same amount of messages:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7MOMWEQrQVLcd9DeOpignN/81ee9337ba0266c03770c6684237e476/1.png" />
          </figure>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/48H1RMFi0oltn0zAHDtmzD/4e5dab19dc2d95d53563e331bcf60923/2.png" />
          </figure>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2w92h2jHYHdce63bWv0gJR/2c810303800bbd073569c3f086326c31/3.png" />
          </figure><p>To make it even more obvious, this was the disk usage of these topics:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/FQ5TGkcRqPR63TBPiLW85/cc2c905abd0af052b3db6b4df2660b89/4.png" />
          </figure><p>This looked amazing, but it was rather low throughput nginx errors topic, containing literally string error messages from nginx. Our main target was <code>requests</code> HTTP log topic with <a href="https://capnproto.org/">capnp</a> encoded messages that are much harder to compress. Naturally, we moved on to try out one <code>partition</code> of requests topic. First results were insanely good:</p>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/5.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1ziqP8gINUJ6THlURi989m/28ecfd3e722da5fdbe2cb5ab048cb80c/5.png" />
            </a>
            </figure><p>They were so good, because they were lies. If with nginx error logs we were pushing under 20Mbps of uncompressed logs, here we jumped 30x to 600Mbps and compression wasn't able to keep up. Still, as a starting point, this experiment gave us some expectations in terms of compression ratios for the main target.</p><table><tr><td><p><b>Compression</b></p></td><td><p><b>Messages consumed</b></p></td><td><p><b>Disk usage</b></p></td><td><p><b>Average message size</b></p></td></tr><tr><td><p>None</p></td><td><p>30.18M</p></td><td><p>48106MB</p></td><td><p>1594B</p></td></tr><tr><td><p>Gzip</p></td><td><p>3.17M</p></td><td><p>1443MB</p></td><td><p>455B</p></td></tr><tr><td><p>Snappy</p></td><td><p>20.99M</p></td><td><p>14807MB</p></td><td><p>705B</p></td></tr><tr><td><p>LZ4</p></td><td><p>20.93M</p></td><td><p>14731MB</p></td><td><p>703B</p></td></tr></table><p>Gzip sounded too expensive from the beginning (especially in Go), but Snappy should have been able to keep up. We profiled our producer, and it was spending just 2.4% of CPU time in Snappy compression, never saturating a single core:</p>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/6.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6X1yjXYICcPKzHxKKdQ76K/73278df1829440a7456f32d574d93f1a/6.png" />
            </a>
            </figure>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/7.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/57E71JTk6DPA6oo8SOLghQ/83edd6f2db81a83db4bd8762117eeb1d/7.png" />
            </a>
            </figure><p>For Snappy we were able to get the following thread stacktrace from Kafka with <code>jstack</code>:</p>
            <pre><code>"kafka-request-handler-3" #87 daemon prio=5 os_prio=0 tid=0x00007f80d2e97800 nid=0x1194 runnable [0x00007f7ee1adc000]
   java.lang.Thread.State: RUNNABLE
    at org.xerial.snappy.SnappyNative.rawCompress(Native Method)
    at org.xerial.snappy.Snappy.rawCompress(Snappy.java:446)
    at org.xerial.snappy.Snappy.compress(Snappy.java:119)
    at org.xerial.snappy.SnappyOutputStream.compressInput(SnappyOutputStream.java:376)
    at org.xerial.snappy.SnappyOutputStream.write(SnappyOutputStream.java:130)
    at java.io.DataOutputStream.write(DataOutputStream.java:107)
    - locked &lt;0x00000007a74cc8f0&gt; (a java.io.DataOutputStream)
    at org.apache.kafka.common.utils.Utils.writeTo(Utils.java:861)
    at org.apache.kafka.common.record.DefaultRecord.writeTo(DefaultRecord.java:203)
    at org.apache.kafka.common.record.MemoryRecordsBuilder.appendDefaultRecord(MemoryRecordsBuilder.java:622)
    at org.apache.kafka.common.record.MemoryRecordsBuilder.appendWithOffset(MemoryRecordsBuilder.java:409)
    at org.apache.kafka.common.record.MemoryRecordsBuilder.appendWithOffset(MemoryRecordsBuilder.java:442)
    at org.apache.kafka.common.record.MemoryRecordsBuilder.appendWithOffset(MemoryRecordsBuilder.java:595)
    at kafka.log.LogValidator$.$anonfun$buildRecordsAndAssignOffsets$1(LogValidator.scala:336)
    at kafka.log.LogValidator$.$anonfun$buildRecordsAndAssignOffsets$1$adapted(LogValidator.scala:335)
    at kafka.log.LogValidator$$$Lambda$675/1035377790.apply(Unknown Source)
    at scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:59)
    at scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:52)
    at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
    at kafka.log.LogValidator$.buildRecordsAndAssignOffsets(LogValidator.scala:335)
    at kafka.log.LogValidator$.validateMessagesAndAssignOffsetsCompressed(LogValidator.scala:288)
    at kafka.log.LogValidator$.validateMessagesAndAssignOffsets(LogValidator.scala:71)
    at kafka.log.Log.liftedTree1$1(Log.scala:654)
    at kafka.log.Log.$anonfun$append$2(Log.scala:642)
    - locked &lt;0x0000000640068e88&gt; (a java.lang.Object)
    at kafka.log.Log$$Lambda$627/239353060.apply(Unknown Source)
    at kafka.log.Log.maybeHandleIOException(Log.scala:1669)
    at kafka.log.Log.append(Log.scala:624)
    at kafka.log.Log.appendAsLeader(Log.scala:597)
    at kafka.cluster.Partition.$anonfun$appendRecordsToLeader$1(Partition.scala:499)
    at kafka.cluster.Partition$$Lambda$625/1001513143.apply(Unknown Source)
    at kafka.utils.CoreUtils$.inLock(CoreUtils.scala:217)
    at kafka.utils.CoreUtils$.inReadLock(CoreUtils.scala:223)
    at kafka.cluster.Partition.appendRecordsToLeader(Partition.scala:487)
    at kafka.server.ReplicaManager.$anonfun$appendToLocalLog$2(ReplicaManager.scala:724)
    at kafka.server.ReplicaManager$$Lambda$624/2052953875.apply(Unknown Source)
    at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:234)
    at scala.collection.TraversableLike$$Lambda$12/187472540.apply(Unknown Source)
    at scala.collection.mutable.HashMap.$anonfun$foreach$1(HashMap.scala:138)
    at scala.collection.mutable.HashMap$$Lambda$25/1864869682.apply(Unknown Source)
    at scala.collection.mutable.HashTable.foreachEntry(HashTable.scala:236)
    at scala.collection.mutable.HashTable.foreachEntry$(HashTable.scala:229)
    at scala.collection.mutable.HashMap.foreachEntry(HashMap.scala:40)
    at scala.collection.mutable.HashMap.foreach(HashMap.scala:138)
    at scala.collection.TraversableLike.map(TraversableLike.scala:234)
    at scala.collection.TraversableLike.map$(TraversableLike.scala:227)
    at scala.collection.AbstractTraversable.map(Traversable.scala:104)
    at kafka.server.ReplicaManager.appendToLocalLog(ReplicaManager.scala:708)
    at kafka.server.ReplicaManager.appendRecords(ReplicaManager.scala:459)
    at kafka.server.KafkaApis.handleProduceRequest(KafkaApis.scala:466)
    at kafka.server.KafkaApis.handle(KafkaApis.scala:99)
    at kafka.server.KafkaRequestHandler.run(KafkaRequestHandler.scala:65)
    at java.lang.Thread.run(Thread.java:748)</code></pre>
            <p>This pointed us to <a href="https://github.com/apache/kafka/blob/1.0.0/core/src/main/scala/kafka/log/LogValidator.scala#L70-L71">this piece of code</a> in Kafka repository.</p><p>There wasn't enough logging to figure out why Kafka was recompressing, but we were able to get this information out with a patched Kafka broker:</p>
            <pre><code>diff --git a/core/src/main/scala/kafka/log/LogValidator.scala b/core/src/main/scala/kafka/log/LogValidator.scala
index 15750e9cd..5197d0885 100644
--- a/core/src/main/scala/kafka/log/LogValidator.scala
+++ b/core/src/main/scala/kafka/log/LogValidator.scala
@@ -21,6 +21,7 @@ import java.nio.ByteBuffer
 import kafka.common.LongRef
 import kafka.message.{CompressionCodec, NoCompressionCodec}
 import kafka.utils.Logging
+import org.apache.log4j.Logger
 import org.apache.kafka.common.errors.{InvalidTimestampException, UnsupportedForMessageFormatException}
 import org.apache.kafka.common.record._
 import org.apache.kafka.common.utils.Time
@@ -236,6 +237,7 @@ private[kafka] object LogValidator extends Logging {
   
       // No in place assignment situation 1 and 2
       var inPlaceAssignment = sourceCodec == targetCodec &amp;&amp; toMagic &gt; RecordBatch.MAGIC_VALUE_V0
+      logger.info("inPlaceAssignment = " + inPlaceAssignment + ", condition: sourceCodec (" + sourceCodec + ") == targetCodec (" + targetCodec + ") &amp;&amp; toMagic (" + toMagic + ") &gt; RecordBatch.MAGIC_VALUE_V0 (" + RecordBatch.MAGIC_VALUE_V0 + ")")
   
       var maxTimestamp = RecordBatch.NO_TIMESTAMP
       val expectedInnerOffset = new LongRef(0)
@@ -250,6 +252,7 @@ private[kafka] object LogValidator extends Logging {
         // Do not compress control records unless they are written compressed
         if (sourceCodec == NoCompressionCodec &amp;&amp; batch.isControlBatch)
           inPlaceAssignment = true
+          logger.info("inPlaceAssignment = " + inPlaceAssignment + ", condition: sourceCodec (" + sourceCodec + ") == NoCompressionCodec (" + NoCompressionCodec + ") &amp;&amp; batch.isControlBatch (" + batch.isControlBatch + ")")
   
         for (record &lt;- batch.asScala) {
           validateRecord(batch, record, now, timestampType, timestampDiffMaxMs, compactedTopic)
@@ -261,21 +264,26 @@ private[kafka] object LogValidator extends Logging {
           if (batch.magic &gt; RecordBatch.MAGIC_VALUE_V0 &amp;&amp; toMagic &gt; RecordBatch.MAGIC_VALUE_V0) {
             // Check if we need to overwrite offset
             // No in place assignment situation 3
-            if (record.offset != expectedInnerOffset.getAndIncrement())
+            val off = expectedInnerOffset.getAndIncrement()
+            if (record.offset != off)
               inPlaceAssignment = false
+              logger.info("inPlaceAssignment = " + inPlaceAssignment + ", condition: record.offset (" + record.offset + ") != expectedInnerOffset.getAndIncrement() (" + off + ")")
             if (record.timestamp &gt; maxTimestamp)
               maxTimestamp = record.timestamp
           }
   
           // No in place assignment situation 4
-          if (!record.hasMagic(toMagic))
+          if (!record.hasMagic(toMagic)) {
+            logger.info("inPlaceAssignment = " + inPlaceAssignment + ", condition: !record.hasMagic(toMagic) (" + !record.hasMagic(toMagic) + ")")
             inPlaceAssignment = false
+          }
   
           validatedRecords += record
         }
       }
   
       if (!inPlaceAssignment) {
+        logger.info("inPlaceAssignment = " + inPlaceAssignment + "; recompressing")
         val (producerId, producerEpoch, sequence, isTransactional) = {
           // note that we only reassign offsets for requests coming straight from a producer. For records with magic V2,
           // there should be exactly one RecordBatch per request, so the following is all we need to do. For Records</code></pre>
            <p>And the output was:</p>
            <pre><code>Dec 29 23:18:59 mybroker kafka[33461]: INFO inPlaceAssignment = true, condition: sourceCodec (SnappyCompressionCodec) == targetCodec (SnappyCompressionCodec) &amp;&amp; toMagic (2) &gt; RecordBatch.MAGIC_VALUE_V0 (0) (kafka.log.LogValidator$)
Dec 29 23:18:59 mybroker kafka[33461]: INFO inPlaceAssignment = true, condition: sourceCodec (SnappyCompressionCodec) == NoCompressionCodec (NoCompressionCodec) &amp;&amp; batch.isControlBatch (false) (kafka.log.LogValidator$)
Dec 29 23:18:59 mybroker kafka[33461]: INFO inPlaceAssignment = true, condition: record.offset (0) != expectedInnerOffset.getAndIncrement() (0) (kafka.log.LogValidator$)
Dec 29 23:18:59 mybroker kafka[33461]: INFO inPlaceAssignment = false, condition: record.offset (0) != expectedInnerOffset.getAndIncrement() (1) (kafka.log.LogValidator$)
Dec 29 23:18:59 mybroker kafka[33461]: INFO inPlaceAssignment = false, condition: record.offset (0) != expectedInnerOffset.getAndIncrement() (2) (kafka.log.LogValidator$)
Dec 29 23:18:59 mybroker kafka[33461]: INFO inPlaceAssignment = false, condition: record.offset (0) != expectedInnerOffset.getAndIncrement() (3) (kafka.log.LogValidator$)
Dec 29 23:18:59 mybroker kafka[33461]: INFO inPlaceAssignment = false, condition: record.offset (0) != expectedInnerOffset.getAndIncrement() (4) (kafka.log.LogValidator$)
Dec 29 23:18:59 mybroker kafka[33461]: INFO inPlaceAssignment = false, condition: record.offset (0) != expectedInnerOffset.getAndIncrement() (5) (kafka.log.LogValidator$)
Dec 29 23:18:59 mybroker kafka[33461]: INFO inPlaceAssignment = false, condition: record.offset (0) != expectedInnerOffset.getAndIncrement() (6) (kafka.log.LogValidator$)</code></pre>
            <p>We promptly <a href="https://github.com/Shopify/sarama/pull/1015">fixed the issue</a> and resumed the testing. These were the results:</p><table><tr><td><p><b>Compression</b></p></td><td><p><b>User time</b></p></td><td><p><b>Messages</b></p></td><td><p><b>Time per 1m</b></p></td><td><p><b>CPU ratio</b></p></td><td><p><b>Disk usage</b></p></td><td><p><b>Avg. message size</b></p></td><td><p><b>Compression ratio</b></p></td></tr><tr><td><p>None</p></td><td><p>209.67s</p></td><td><p>26.00M</p></td><td><p>8.06s</p></td><td><p>1x</p></td><td><p>41448MB</p></td><td><p>1594B</p></td><td><p>1x</p></td></tr><tr><td><p>Gzip</p></td><td><p>570.56s</p></td><td><p>6.98M</p></td><td><p>81.74s</p></td><td><p>10.14x</p></td><td><p>3111MB</p></td><td><p>445B</p></td><td><p>3.58x</p></td></tr><tr><td><p>Snappy</p></td><td><p>337.55s</p></td><td><p>26.02M</p></td><td><p>12.97s</p></td><td><p>1.61x</p></td><td><p>17675MB</p></td><td><p>679B</p></td><td><p>2.35x</p></td></tr><tr><td><p>LZ4</p></td><td><p>525.82s</p></td><td><p>26.01M</p></td><td><p>20.22s</p></td><td><p>2.51x</p></td><td><p>22922MB</p></td><td><p>881B</p></td><td><p>1.81x</p></td></tr></table><p>Now we were able to keep up with both Snappy and LZ4. Gzip was still out of the question and LZ4 had incompatibility issues between Kafka versions and our Go client, which left us with Snappy. This was a winner in terms of compression ratio and speed too, so we were not very disappointed by the lack of choice.</p>
    <div>
      <h3>Deploying into production</h3>
      <a href="#deploying-into-production">
        
      </a>
    </div>
    <p>In production, we started small with Java based consumers and producers. Our first production topic was just 1Mbps and 600rps of nginx error logs. Messages there were very repetitive, and we were able to get whopping 8x decrease in size with batching records for just 1 second across 2 partitions.</p><p>This gave us some confidence to move onto next topic with <code>journald</code> logs encoded with JSON. Here we were able to reduce ingress from 300Mbps to just 50Mbps (yellow line on the graph):</p>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/8.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2MBwqqBM9fKfK5etewI3K8/a6c3e5154769721ace34e35448e0e9d8/8.png" />
            </a>
            </figure>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/10.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1VRIDnquD9cRDxx9EpNliX/abed839321dde455854bea1c93a0fd76/10.png" />
            </a>
            </figure>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/11.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1S1GiVnOPIEaZi20vDdu7B/4e0b4a064af5e576f615b18618e22a14/11.png" />
            </a>
            </figure>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/12.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6F0xiuQrUHNiU2Q0VgaYtC/2176de2b39c4ec3493f06dae8a995ff2/12.png" />
            </a>
            </figure>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/13.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/lvIkKRy1yrsTJNe4n4DEP/d2da438e3fbe1d9f40eb2e1347b684a7/13.png" />
            </a>
            </figure><p>With all major topics in DNS cluster switched to Snappy we saw even better picture in terms of broker CPU usage:</p>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/14.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7ERRHln9aNu0pSoKSFFQkA/197e14bebbe7642b690f23363a5cdf1e/14.png" />
            </a>
            </figure>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/15.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7cfmeyd2Jch2ZfTquu14rW/63fd83ad46a6484dd8b7ebe446b21e09/15.png" />
            </a>
            </figure><p>On the next graph you can see Kafka CPU usage as the purple line and producer CPU usage as the green line:</p>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/16.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5QWGl1NhwkYWMHl5Q5Lius/a4085495612071ae2a16a8aa9a675a05/16.png" />
            </a>
            </figure><p>CPU usage of the producer did not go up substantially, which means most of the work is spent in non compression related tasks. Consumers did not see any increase in CPU usage either, which means we've got our 2.6x decrease in size practically for free.</p><p>It was time to hunt the biggest beast of all: <code>requests</code> topic with HTTP access logs. There we were doing up to 100Gbps and 7.5Mrps of ingress at peak (a lot more when big attacks are happening, but this was a quiet week):</p>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/17.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/695gJ4I1AIqDHKIH5phRKh/36940318a0b87aa698b4e088c60fc0c2/17.png" />
            </a>
            </figure><p>With many smaller topics switched to Snappy already, we did not need to do anything special here. This is how it went:</p>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/18.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2b39JEnQP0w68PhPDkGjOz/b4f74fb90ed73a44aa8b05fca03a9de3/18.png" />
            </a>
            </figure>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/19.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7KvLGzI0Rnk44SXDJb6JXT/cdb6ee681318809a65b8ef8a61229589/19.png" />
            </a>
            </figure><p>That's a 2.25x decrease in ingress bandwidth and average message size. We have multiple replicas and consumers, which means egress is a multiple of ingress. We were able to reduce in-DC traffic by hundreds of gigabits of internal traffic and save terabytes of flash storage. With network and disks being bottlenecks, this meant we'd need less than a half of hardware we had. Kafka was one of the main hardware hogs in this datacenter, so this was a large scale win.</p><p>Yet, 2.25x seemed a bit on the low side.</p>
    <div>
      <h3>Looking for more</h3>
      <a href="#looking-for-more">
        
      </a>
    </div>
    <p>We wanted to see if we can do better. To do that, we extracted one batch of records from Kafka and ran some benchmarks on it. All batches are around 1 MB uncompressed, 600 records in each on average.</p><p>To run the benchmarks we used <a href="https://github.com/inikep/lzbench">lzbench</a>, which runs lots of different compression algorithms and provides a summary. Here's what we saw with results sorted by compression ratio (heavily filtered list):</p>
            <pre><code>lzbench 1.7.3 (64-bit MacOS)   Assembled by P.Skibinski
Compressor name         Compress. Decompress. Compr. size  Ratio Filename
memcpy                  33587 MB/s 33595 MB/s      984156 100.00
...
lz4 1.8.0                 594 MB/s  2428 MB/s      400577  40.70
...
snappy 1.1.4              446 MB/s  1344 MB/s      425564  43.24
...
zstd 1.3.3 -1             409 MB/s   844 MB/s      259438  26.36
zstd 1.3.3 -2             303 MB/s   889 MB/s      244650  24.86
zstd 1.3.3 -3             242 MB/s   899 MB/s      232057  23.58
zstd 1.3.3 -4             240 MB/s   910 MB/s      230936  23.47
zstd 1.3.3 -5             154 MB/s   891 MB/s      226798  23.04</code></pre>
            <p>This looked too good to be true. <a href="https://facebook.github.io/zstd/">Zstandard</a> is a fairly new (released 1.5 years ago) compression algorithm from Facebook. In benchmarks on the project's home page you can see this:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4RmnIQ4k2nqI5jabUBQLMq/035633fbc37b15c7fb80d2ed72de01f4/zstd.png" />
            
            </figure><p>In our case we were getting this:</p><table><tr><td><p><b>Compressor name</b></p></td><td><p><b>Ratio</b></p></td><td><p><b>Compression</b></p></td><td><p><b>Decompression</b></p></td></tr><tr><td><p>zstd</p></td><td><p>3.794</p></td><td><p>409 MB/s</p></td><td><p>844 MB/s</p></td></tr><tr><td><p>lz4</p></td><td><p>2.475</p></td><td><p>594 MB/s</p></td><td><p>2428 MB/s</p></td></tr><tr><td><p>snappy</p></td><td><p>2.313</p></td><td><p>446 MB/s</p></td><td><p>1344 MB/s</p></td></tr></table><p>Clearly, results are very dependent on the kind of data you are trying to compress. For our data zstd was giving amazing results even on the lowest compression level. Compression ratio was better than even gzip at maximum compression level, while throughput was a lot higher. For posterity, this is how DNS logs compressed (HTTP logs compressed similarly):</p>
            <pre><code>$ ./lzbench -ezstd/zlib rrdns.recordbatch
lzbench 1.7.3 (64-bit MacOS)   Assembled by P.Skibinski
Compressor name         Compress. Decompress. Compr. size  Ratio Filename
memcpy                  33235 MB/s 33502 MB/s      927048 100.00 rrdns.recordbatch
zstd 1.3.3 -1             430 MB/s   909 MB/s      226298  24.41 rrdns.recordbatch
zstd 1.3.3 -2             322 MB/s   878 MB/s      227271  24.52 rrdns.recordbatch
zstd 1.3.3 -3             255 MB/s   883 MB/s      217730  23.49 rrdns.recordbatch
zstd 1.3.3 -4             253 MB/s   883 MB/s      217141  23.42 rrdns.recordbatch
zstd 1.3.3 -5             169 MB/s   869 MB/s      216119  23.31 rrdns.recordbatch
zstd 1.3.3 -6             102 MB/s   939 MB/s      211092  22.77 rrdns.recordbatch
zstd 1.3.3 -7              78 MB/s   968 MB/s      208710  22.51 rrdns.recordbatch
zstd 1.3.3 -8              65 MB/s  1005 MB/s      204370  22.05 rrdns.recordbatch
zstd 1.3.3 -9              59 MB/s  1008 MB/s      204071  22.01 rrdns.recordbatch
zstd 1.3.3 -10             44 MB/s  1029 MB/s      202587  21.85 rrdns.recordbatch
zstd 1.3.3 -11             43 MB/s  1054 MB/s      202447  21.84 rrdns.recordbatch
zstd 1.3.3 -12             32 MB/s  1051 MB/s      201190  21.70 rrdns.recordbatch
zstd 1.3.3 -13             31 MB/s  1050 MB/s      201190  21.70 rrdns.recordbatch
zstd 1.3.3 -14             13 MB/s  1074 MB/s      200228  21.60 rrdns.recordbatch
zstd 1.3.3 -15           8.15 MB/s  1171 MB/s      197114  21.26 rrdns.recordbatch
zstd 1.3.3 -16           5.96 MB/s  1051 MB/s      190683  20.57 rrdns.recordbatch
zstd 1.3.3 -17           5.64 MB/s  1057 MB/s      191227  20.63 rrdns.recordbatch
zstd 1.3.3 -18           4.45 MB/s  1166 MB/s      187967  20.28 rrdns.recordbatch
zstd 1.3.3 -19           4.40 MB/s  1108 MB/s      186770  20.15 rrdns.recordbatch
zstd 1.3.3 -20           3.19 MB/s  1124 MB/s      186721  20.14 rrdns.recordbatch
zstd 1.3.3 -21           3.06 MB/s  1125 MB/s      186710  20.14 rrdns.recordbatch
zstd 1.3.3 -22           3.01 MB/s  1125 MB/s      186710  20.14 rrdns.recordbatch
zlib 1.2.11 -1             97 MB/s   301 MB/s      305992  33.01 rrdns.recordbatch
zlib 1.2.11 -2             93 MB/s   327 MB/s      284784  30.72 rrdns.recordbatch
zlib 1.2.11 -3             74 MB/s   364 MB/s      265415  28.63 rrdns.recordbatch
zlib 1.2.11 -4             68 MB/s   342 MB/s      269831  29.11 rrdns.recordbatch
zlib 1.2.11 -5             48 MB/s   367 MB/s      258558  27.89 rrdns.recordbatch
zlib 1.2.11 -6             32 MB/s   376 MB/s      247560  26.70 rrdns.recordbatch
zlib 1.2.11 -7             24 MB/s   409 MB/s      244623  26.39 rrdns.recordbatch
zlib 1.2.11 -8           9.67 MB/s   429 MB/s      239659  25.85 rrdns.recordbatch
zlib 1.2.11 -9           3.63 MB/s   446 MB/s      235604  25.41 rrdns.recordbatch</code></pre>
            <p>For our purposes we picked level 6 as the compromise between compression ratio and CPU cost. It is possible to be even more aggressive as real world usage proved later.</p><p>One great property of zstd is more or less the same decompression speed between levels, which means you only have one knob that connects CPU cost of compression to compression ratio.</p><p>Armed with this knowledge, we dug up <a href="https://issues.apache.org/jira/browse/KAFKA-4514">forgotten Kafka ticket</a> to add zstd, along with <a href="https://cwiki.apache.org/confluence/display/KAFKA/KIP-110%3A+Add+Codec+for+ZStandard+Compression">KIP</a> (Kafka Improvement Proposal) and even <a href="https://github.com/apache/kafka/pull/2267">PR on GitHub</a>. Sadly, these did not get traction back in the day, but this work saved us a lot of time.</p><p>We <a href="https://github.com/bobrik/kafka/commit/8b17836efda64dba1ebdc080e30ee2945793aef3">ported</a> the patch to Kafka 1.0.0 release and pushed it in production. After another round of smaller scale testing and with <a href="https://github.com/bobrik/sarama/commit/c36187fbafab5afe5c152d2012b05b9306196cdb">patched</a> clients we pushed Zstd into production for requests topic.</p><p>Graphs below include switch from no compression (before 2/9) to Snappy (2/9 to 2/17) to Zstandard (after 2/17):</p>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/20.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/466acpqo8HHXc9mml41SGp/178fefc55aa2e9d29a484169ee47c0ed/20.png" />
            </a>
            </figure>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/03/21.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6HSguckI7mJln65401u5Y2/cd9e39e86898c214637e0cb0c865ae02/21.png" />
            </a>
            </figure><p>The decrease in size was <b>4.5x</b> compared to no compression at all. On next generation hardware with 2.4x more storage and 2.5x higher network throughput we suddenly made our bottleneck more than 10x wider and shifted it from storage and network to CPU cost. We even got to cancel pending hardware order for Kafka expansion because of this.</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>Zstandard is a great modern compression algorithm, promising high compression ratio and throughput, tunable in small increments. Whenever you consider using compression, you should check zstd. If you don't consider compression, then it's worth seeing if you can get benefits from it. Run benchmarks with your data in either case.</p><p>Testing in real world scenario showed how benchmarks, even coming from zstd itself, can be misleading. Going beyond codecs built into Kafka allowed us to improve compression ratio 2x at very low cost.</p><p>We hope that the data we gathered can be a catalyst to making Zstandard an official compression codec in Kafka to benefit other people. There are 3 bits allocated for codec type and only 2 are used so far, which means there are 4 more vacant places.</p><p>If you were skeptical of compression benefits in Kafka because of old flaws in Kafka protocol, this may be the time to reconsider.</p><p>If you enjoy benchmarking, profiling and optimizing large scale services, come <a href="https://www.cloudflare.com/careers/">join us</a>.</p> ]]></content:encoded>
            <category><![CDATA[Compression]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Kafka]]></category>
            <guid isPermaLink="false">50nksAmOM8KO1t9ihhnJxe</guid>
            <dc:creator>Ivan Babrou</dc:creator>
        </item>
        <item>
            <title><![CDATA[ARM Takes Wing:  Qualcomm vs. Intel CPU comparison]]></title>
            <link>https://blog.cloudflare.com/arm-takes-wing/</link>
            <pubDate>Wed, 08 Nov 2017 20:03:14 GMT</pubDate>
            <description><![CDATA[ One of the nicer perks I have here at Cloudflare is access to the latest hardware, long before it even reaches the market. Until recently I mostly played with Intel hardware.  ]]></description>
            <content:encoded><![CDATA[ <p>One of the nicer perks I have here at Cloudflare is access to the latest hardware, long before it even reaches the market.</p><p>Until recently I mostly played with Intel hardware. For example Intel supplied us with an engineering sample of their Skylake based Purley platform back in August 2016, to give us time to evaluate it and optimize our software. As a former Intel Architect, who did a lot of work on Skylake (as well as Sandy Bridge, Ivy Bridge and Icelake), I really enjoy that.</p><p>Our previous generation of servers was based on the Intel Broadwell micro-architecture. Our configuration includes dual-socket Xeons E5-2630 v4, with 10 cores each, running at 2.2GHz, with a 3.1GHz turboboost and hyper-threading enabled, for a total of 40 threads per server.</p><p>Since Intel was, and still is, the undisputed leader of the server CPU market with greater than 98% market share, our upgrade process until now was pretty straightforward: every year Intel releases a new generation of CPUs, and every year we buy them. In the process we usually get two extra cores per socket, and all the extra architectural features such upgrade brings: hardware AES and CLMUL in Westmere, AVX in Sandy Bridge, AVX2 in Haswell, etc.</p><p>In the current upgrade cycle, our next server processor ought to be the Xeon Silver 4116, also in a dual-socket configuration. In fact, we have already purchased a significant number of them. Each CPU has 12 cores, but it runs at a lower frequency of 2.1GHz, with 3.0GHz turboboost. It also has smaller last level cache: 1.375 MiB/core, compared to 2.5 MiB the Broadwell processors had. In addition, the Skylake based platform supports 6 memory channels and the AVX-512 instruction set.</p><p>As we head into 2018, however, change is in the air. For the first time in a while, Intel has serious competition in the server market: Qualcomm and Cavium both have new server platforms based on the ARMv8 64-bit architecture (aka aarch64 or arm64). Qualcomm has the Centriq platform (code name Amberwing), based on the Falkor core, and Cavium has the ThunderX2 platform, based on the ahm ... ThunderX2 core?</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2OerKvEoy4NXTwjEpDA1tY/d8d9c4dad28ab5bc7100710b3dcf644e/25704115174_061e907e57_o.jpg" />
            
            </figure><p>The majestic Amberwing powered by the Falkor CPU <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC BY-SA 2.0</a> <a href="https://www.flickr.com/photos/drphotomoto/25704115174/in/photolist-FaoiVy-oqK2j2-f6A8TL-6PTBkR-gRasf9-f2R2Hz-7bZeUp-fmxSzZ-o9fogQ-8evb42-f4tgSX-eGzXYi-6umTDd-8evd8H-gdCU2L-uhCbnz-fmxSsX-oxnuko-wb7in9-oqsJSH-uxxAS2-CzS4Eh-6y8KQA-brLjKf-YT2jrY-eGG5QJ-8pLnKt-8eyvgY-cnQqJs-fXYs9f-f2R2jK-28ahBA-fXYjkD-a9K25u-289gvW-PHrqDS-cmkggf-Ff9NXa-EhMcP4-f36dMm-289xP7-Ehrz1y-f2QZRZ-GqT3vt-uUeHBq-xUDQoa-ymMxE9-wWFi3q-MDva8W-8CWG5X">image</a> by <a href="https://www.flickr.com/photos/drphotomoto/">DrPhotoMoto</a></p><p>Recently, both Qualcomm and Cavium provided us with engineering samples of their ARM based platforms, and in this blog post I would like to share my findings about Centriq, the Qualcomm platform.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4t48heslIVcJiaqGTuxwhl/a968a4b7f0a6a5dd25fc8481029db354/IMG_7222.jpg" />
            
            </figure><p>The actual Amberwing in question</p>
    <div>
      <h3>Overview</h3>
      <a href="#overview">
        
      </a>
    </div>
    <p>I tested the Qualcomm Centriq server, and compared it with our newest Intel Skylake based server and previous Broadwell based server.</p><table><tr><td><p><b>Platform</b></p></td><td><p><b>Grantley
(Intel)</b></p></td><td><p><b>Purley
(Intel)</b></p></td><td><p><b>Centriq
(Qualcomm)</b></p></td></tr><tr><td><p>Core</p></td><td><p>Broadwell</p></td><td><p>Skylake</p></td><td><p>Falkor</p></td></tr><tr><td><p>Process</p></td><td><p>14nm</p></td><td><p>14nm</p></td><td><p>10nm</p></td></tr><tr><td><p>Issue</p></td><td><p>8 µops/cycle</p></td><td><p>8 µops/cycle</p></td><td><p>8 instructions/cycle</p></td></tr><tr><td><p>Dispatch</p></td><td><p>4 µops/cycle</p></td><td><p>5 µops/cycle</p></td><td><p>4 instructions/cycle</p></td></tr><tr><td><p># Cores</p></td><td><p>10 x 2S + HT (40 threads)</p></td><td><p>12 x 2S + HT (48 threads)</p></td><td><p>46</p></td></tr><tr><td><p>Frequency</p></td><td><p>2.2GHz (3.1GHz turbo)</p></td><td><p>2.1GHz (3.0GHz turbo)</p></td><td><p>2.5 GHz</p></td></tr><tr><td><p>LLC</p></td><td><p>2.5 MB/core</p></td><td><p>1.35 MB/core</p></td><td><p>1.25 MB/core</p></td></tr><tr><td><p>Memory Channels</p></td><td><p>4</p></td><td><p>6</p></td><td><p>6</p></td></tr><tr><td><p>TDP</p></td><td><p>170W (85W x 2S)</p></td><td><p>170W (85W x 2S)</p></td><td><p>120W</p></td></tr><tr><td><p>Other features</p></td><td><p>AES
CLMUL
AVX2</p></td><td><p>AES
CLMUL
AVX512</p></td><td><p>AES
CLMUL
NEON
Trustzone
CRC32</p></td></tr></table><p>Overall on paper Falkor looks very competitive. In theory a Falkor core can process 8 instructions/cycle, same as Skylake or Broadwell, and it has higher base frequency at a lower TDP rating.</p>
    <div>
      <h3>Ecosystem readiness</h3>
      <a href="#ecosystem-readiness">
        
      </a>
    </div>
    <p>Up until now, a major obstacle to the deployment of ARM servers was lack, or weak, support by the majority of the software vendors. In the past two years, ARM’s enablement efforts have paid off, as most Linux distros, as well as most popular libraries support the 64-bit ARM architecture. Driver availability, however, is unclear at that point.</p><p>At Cloudflare, we run a complex software stack that consists of many integrated services, and running each of them efficiently is top priority.</p><p>On the edge we have the NGINX server software, that does support ARMv8. NGINX is written in C, and it also uses several libraries written in C, such as zlib and BoringSSL, therefore solid C compiler support is very important.</p><p>In addition, our flavor of NGINX is highly integrated with the <a href="https://github.com/openresty/lua-nginx-module">lua-nginx-module</a>, and we rely a lot on <a href="/pushing-nginx-to-its-limit-with-lua/">LuaJIT</a>.</p><p>Finally, a lot of our services, such as our DNS server, <a href="/what-weve-been-doing-with-go/#sts=RRDNS">RRDNS</a>, are written in Go.</p><p>The good news is that both gcc and clang not only support ARMv8 in general, but have optimization profiles for the Falkor core.</p><p>Go has official support for ARMv8 as well, and they improve the arm64 backend constantly.</p><p>As for LuaJIT, the stable version, 2.0.5 does not support ARMv8, but the beta version, 2.1.0 does. Let’s hope it gets out of beta soon.</p>
    <div>
      <h3>Benchmarks</h3>
      <a href="#benchmarks">
        
      </a>
    </div>
    
    <div>
      <h4>OpenSSL</h4>
      <a href="#openssl">
        
      </a>
    </div>
    <p>The first benchmark I wanted to perform, was OpenSSL version 1.1.1 (development version), using the bundled <code>openssl speed</code> tool. Although we recently switched to BoringSSL, I still prefer OpenSSL for benchmarking, because it has almost equally well optimized assembly code paths for both ARMv8 and the latest Intel processors.</p><p>In my opinion handcrafted assembly is the best measure of a CPU’s potential, as it bypasses the compiler bias.</p>
    <div>
      <h4>Public key cryptography</h4>
      <a href="#public-key-cryptography">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5jqlU0sjyAGrZaT2Y1x8JR/9fc0bccc931f0e8f8cbf1c8fcd2bdecf/pub_key_1_core-2.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/19nPDhEQXutllHbY08nO6O/0da82dc7260b6dbd732cc3cf79455ba8/pub_key_all_core-2.png" />
            
            </figure><p>Public key cryptography is all about raw ALU performance. It is interesting, but not surprising to see that in the single core benchmark, the Broadwell core is faster than Skylake, and both in turn are faster than Falkor. This is because Broadwell runs at a higher frequency, while architecturally it is not much inferior to Skylake.</p><p>Falkor is at a disadvantage here. First, in a single core benchmark, the turbo is engaged, meaning the Intel processors run at a higher frequency. Second, in Broadwell, Intel introduced two special instructions to accelerate big number multiplication: ADCX and ADOX. These perform two independent add-with-carry operations per cycle, whereas ARM can only do one. Similarly, the ARMv8 instruction set does not have a single instruction to perform 64-bit multiplication, instead it uses a pair of MUL and UMULH instructions.</p><p>Nevertheless, at the SoC level, Falkor wins big time. It is only marginally slower than Skylake at an RSA2048 signature, and only because RSA2048 does not have an optimized implementation for ARM. The <a href="https://www.cloudflare.com/learning/dns/dnssec/ecdsa-and-dnssec/">ECDSA</a> performance is ridiculously fast. A single Centriq chip can satisfy the ECDSA needs of almost any company in the world.</p><p>It is also very interesting to see Skylake outperform Broadwell by a 30% margin, despite losing the single core benchmark, and only having 20% more cores. This can be explained by more efficient all-core turbo, and improved hyper-threading.</p>
    <div>
      <h4>Symmetric key cryptography</h4>
      <a href="#symmetric-key-cryptography">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5c1gb11NBOBVq2PyFyxHIJ/05ddba2c522eecce2fe1fceca405c3ac/sym_key_1_core.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3HzScBUWaLPW2l7t0bvsuo/8c235d691f67a6fd9bbfe2176e3037d4/sym_key_all_core.png" />
            
            </figure><p>Symmetric key performance of the Intel cores is outstanding.</p><p>AES-GCM uses a combination of special hardware instructions to accelerate AES and CLMUL (carryless multiplication). Intel first introduced those instructions back in 2010, with their Westmere CPU, and every generation since they have improved their performance. ARM introduced a set of similar instructions just recently, with their 64-bit instruction set, and as an optional extension. Fortunately every hardware vendor I know of implemented those. It is very likely that Qualcomm will improve the performance of the cryptographic instructions in future generations.</p><p>ChaCha20-Poly1305 is a more generic algorithm, designed in such a way as to better utilize wide SIMD units. The Qualcomm CPU only has the 128-bit wide NEON SIMD, while Broadwell has 256-bit wide AVX2, and Skylake has 512-bit wide AVX-512. This explains the huge lead Skylake has over both in single core performance. In the all-cores benchmark the Skylake lead lessens, because it has to lower the clock speed when executing AVX-512 workloads. When executing AVX-512 on all cores, the base frequency goes down to just 1.4GHz---keep that in mind if you are mixing AVX-512 and other code.</p><p>The bottom line for symmetric crypto is that although Skylake has the lead, Broadwell and Falkor both have good enough performance for any real life scenario, especially considering the fact that on our edge, RSA consumes more CPU time than all the other crypto algorithms combined.</p>
    <div>
      <h3>Compression</h3>
      <a href="#compression">
        
      </a>
    </div>
    <p>The next benchmark I wanted to see was compression. This is for two reasons. First, it is a very important workload on the edge, as having better compression saves bandwidth, and helps deliver content faster to the client. Second, it is a very demanding workload, with a high rate of branch mispredictions.</p><p>Obviously the first benchmark would be the popular zlib library. At Cloudflare, we use an <a href="/cloudflare-fights-cancer/">improved version of the library</a>, optimized for 64-bit Intel processors, and although it is written mostly in C, it does use some Intel specific intrinsics. Comparing this optimized version to the generic zlib library wouldn’t be fair. Not to worry, with little effort I <a href="https://github.com/cloudflare/zlib/tree/vlad/aarch64">adapted the library</a> to work very well on the ARMv8 architecture, with the use of NEON and CRC32 intrinsics. In the process it is twice as fast as the generic library for some files.</p><p>The second benchmark is the emerging brotli library, it is written in C, and allows for a level playing field for all platforms.</p><p>All the benchmarks are performed on the HTML of <a href="/">blog.cloudflare.com</a>, in memory, similar to the way NGINX performs streaming compression. The size of the specific version of the HTML file is 29,329 bytes, making it a good representative of the type of files we usually compress. The parallel benchmark compresses multiple files in parallel, as opposed to compressing a single file on many threads, also similar to the way NGINX works.</p>
    <div>
      <h4>gzip</h4>
      <a href="#gzip">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2pi68sRQQHPJLXZO5vIbhW/72c6e8568f4d95e3e8d592dd72577ea8/gzip_1_core.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/S2VrG4fcrmbDUeOtJ453S/bbc5092e3565339bb7cb44ba7c8dd14d/gzip_all_core.png" />
            
            </figure><p>When using gzip, at the single core level Skylake is the clear winner. Despite having lower frequency than Broadwell, it seems that having lower penalty for branch misprediction helps it pull ahead. The Falkor core is not far behind, especially with lower quality settings. At the system level Falkor performs significantly better, thanks to the higher core count. Note how well gzip scales on multiple cores.</p>
    <div>
      <h4>brotli</h4>
      <a href="#brotli">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5LA2CLLkYa9iCe1eQzgs6x/1ff12aa354d80166407fe4f9fd538d78/brot_1_core.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1cuPwzCY4eJzgsOXmNrLXu/f7be60b7ac3497c13b3c0a2abe6ac522/brot_all_core.png" />
            
            </figure><p>With brotli on single core the situation is similar. Skylake is the fastest, but Falkor is not very much behind, and with quality setting 9, Falkor is actually faster. Brotli with quality level 4 performs very similarly to gzip at level 5, while actually compressing slightly better (8,010B vs 8,187B).</p><p>When performing many-core compression, the situation becomes a bit messy. For levels 4, 5 and 6 brotli scales very well. At level 7 and 8 we start seeing lower performance per core, bottoming with level 9, where we get less than 3x the performance of single core, running on all cores.</p><p>My understanding is that at those quality levels Brotli consumes significantly more memory, and starts thrashing the cache. The scaling improves again at levels 10 and 11.</p><p>Bottom line for brotli, Falkor wins, since we would not consider going above quality 7 for dynamic compression.</p>
    <div>
      <h3>Golang</h3>
      <a href="#golang">
        
      </a>
    </div>
    <p>Golang is another very important language for Cloudflare. It is also one of the first languages to offer ARMv8 support, so one would expect good performance. I used some of the built-in benchmarks, but modified them to run on multiple goroutines.</p>
    <div>
      <h4>Go crypto</h4>
      <a href="#go-crypto">
        
      </a>
    </div>
    <p>I would like to start the benchmarks with crypto performance. Thanks to OpenSSL we have good reference numbers, and it is interesting to see just how good the Go library is.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/64U9LsIbiAKp7Ut75mMmhj/c73c1142233e260e8733b079dac06ff8/go_pub_key_1_core.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5e9MFqm53p78KdB211Z8rZ/bc16992088b6aa826819bec645f99581/go_pub_key_all_core.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3TZyK91xQAJk4yfvYLnVjZ/c33931e0ead0f425c157746b9d637fc5/go_sym_key_1_core.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1S2LKr04vDIi1WrrZNaJn1/827505e17425e0521142cb397e209504/go_sym_key_all_core.png" />
            
            </figure><p>As far as Go crypto is concerned ARM and Intel are not even on the same playground. Go has very optimized assembly code for ECDSA, AES-GCM and Chacha20-Poly1305 on Intel. It also has Intel optimized math functions, used in RSA computations. All those are missing for ARMv8, putting it at a big disadvantage.</p><p>Nevertheless, the gap can be bridged with a relatively small effort, and we know that with the right optimizations, performance can be on par with OpenSSL. Even a very minor change, such as implementing the function <a href="https://go-review.googlesource.com/c/go/+/76270">addMulVVW</a> in assembly, lead to an over tenfold improvement in RSA performance, putting Falkor ahead of both Broadwell and Skylake, with 8,009 signatures/second.</p><p>Another interesting thing to note is that on Skylake, the Go Chacha20-Poly1305 code, that uses AVX2 performs almost identically to the OpenSSL AVX512 code, this is again due to AVX2 running at higher clock speeds.</p>
    <div>
      <h4>Go gzip</h4>
      <a href="#go-gzip">
        
      </a>
    </div>
    <p>Next in Go performance is gzip. Here again we have a reference point to pretty well optimized code, and we can compare it to Go. In the case of the gzip library, there are no Intel specific optimizations in place.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1GbKt2HcdJT5eNMdfsLw4d/7c55522afbfa58653a91c28479de8c3d/go_gzip_1_core.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/59TTlWALNqIkryEc7IwOnB/73ee80ab27313c3f55d3cb22c06b6704/go_gzip_all_core.png" />
            
            </figure><p>Gzip performance is pretty good. The single core Falkor performance is way below both Intel processors, but at the system level it manages to outperform Broadwell, and lags behind Skylake. Since we already know that Falkor outperforms both when C is used, it can only mean that Go’s backend for ARMv8 is still pretty immature compared to gcc.</p>
    <div>
      <h4>Go regexp</h4>
      <a href="#go-regexp">
        
      </a>
    </div>
    <p>Regexp is widely used in a variety of tasks, so its performance is quite important too. I ran the built-in benchmarks on 32KB strings.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2AhmqRQhO37Z03KXfz9PUk/9b39646f7e800c9a7c8565134d516815/go_regexp_easy_1_core.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6PH0IDtvODvYcGbECgOvEt/fbb7c4a099ad2b478aa12b8f3b7fb0a0/go_regexp_easy_all_core.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7eAhDauk00ZR4A37NPqQm5/8f1c94f9d34087b57b8672a4928e76a7/go_regexp_comp_1_core.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3ytsG1skhi4B1v24tqlrWw/d0d803c543a990854bd60916ed03b089/go_regexp_comp_all_core.png" />
            
            </figure><p>Go regexp performance is not very good on Falkor. In the medium and hard tests it takes second place, thanks to the higher core count, but Skylake is significantly faster still.</p><p>Doing some profiling shows that a lot of the time is spent in the function bytes.IndexByte. This function has an assembly implementation for amd64 (runtime.indexbytebody), but generic implementation for Go. The easy regexp tests spend most time in this function, which explains the even wider gap.</p>
    <div>
      <h4>Go strings</h4>
      <a href="#go-strings">
        
      </a>
    </div>
    <p>Another important library for a web server is the Go strings library. I only tested the basic Replacer class here.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/G20n7Ru5izVdUBOMM8af7/9a257ef47b5a38e3f745a22da7f36da5/go_str_1_core.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2W6GTABFY1t23YOIhyLc1A/ce51cb7081718a724b797f39e7ff80e2/go_str_all_core.png" />
            
            </figure><p>In this test again, Falkor lags behind, and loses even to Broadwell. Profiling shows significant time is spent in the function runtime.memmove. Guess what? It has a highly optimized assembly code for amd64, that uses AVX2, but only very simple ARM assembly, that copies 8 bytes at a time. By changing three lines in that code, and using the LDP/STP instructions (load pair/store pair) to copy 16 bytes at a time, I improved the performance of memmove by 30%, which resulted in 20% faster EscapeString and UnescapeString performance. And that is just scratching the surface.</p>
    <div>
      <h4>Go conclusion</h4>
      <a href="#go-conclusion">
        
      </a>
    </div>
    <p>Go support for aarch64 is quite disappointing. I am very happy to say that everything compiles and works flawlessly, but on the performance side, things should get better. Is seems like the enablement effort so far was concentrated on the compiler back end, and the library was left largely untouched. There are a lot of low hanging optimization fruits out there, like my 20-minute fix for <a href="https://go-review.googlesource.com/c/go/+/76270">addMulVVW</a> clearly shows. Qualcomm and other ARMv8 vendors intends to put significant engineering resources to amend this situation, but really anyone can contribute to Go. So if you want to leave your mark, now is the time.</p>
    <div>
      <h3>LuaJIT</h3>
      <a href="#luajit">
        
      </a>
    </div>
    <p>Lua is the glue that holds Cloudflare together.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/eTdeElawEbvCLpjM9XMUa/8527a83cf68acc1e6a48af94860b190c/luajit_1_core.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2ohY0uFyotdk0eJt9lU725/472f32803de7ecd6e2d95f27948aa85a/luajit_all_cores.png" />
            
            </figure><p>Except for the binary_trees benchmark, the performance of LuaJIT on ARM is very competitive. It wins two benchmarks, and is in almost a tie in a third one.</p><p>That being said, binary_trees is a very important benchmark, because it triggers many memory allocations and garbage collection cycles. It will require deeper investigation in the future.</p>
    <div>
      <h3>NGINX</h3>
      <a href="#nginx">
        
      </a>
    </div>
    <p>For the NGINX workload, I decided to generate a load that would resemble an actual server.</p><p>I set up a server that serves the HTML file used in the gzip benchmark, over https, with the ECDHE-ECDSA-AES128-GCM-SHA256 cipher suite.</p><p>It also uses LuaJIT to redirect the incoming request, remove all line breaks and extra spaces from the HTML file, while adding a timestamp. The HTML is then compressed using brotli with quality 5.</p><p>Each server was configured to work with as many workers as it has virtual CPUs. 40 for Broadwell, 48 for Skylake and 46 for Falkor.</p><p>As the client for this test, I used the <a href="https://github.com/rakyll/hey">hey</a> program, running from 3 Broadwell servers.</p><p>Concurrently with the test, we took power readings from the respective BMC units of each server.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3d3OUyJ1m0q11IoWUNCyTp/5cde6f742d596d71d14e5f1355a4b570/nginx.png" />
            
            </figure><p>With the NGINX workload Falkor handled almost the same amount of requests as the Skylake server, and both significantly outperform Broadwell. The power readings, taken from the BMC show that it did so while consuming less than half the power of other processors. That means Falkor managed to get 214 requests/watt vs the Skylake’s 99 requests/watt and Broadwell’s 77.</p><p>I was a bit surprised to see Skylake and Broadwell consume about the same amount of power, given both are manufactured with the same process, and Skylake has more cores.</p><p>The low power consumption of Falkor is not surprising, Qualcomm processors are known for their great power efficiency, which has allowed them to be a dominant player in the mobile phone CPU market.</p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>The engineering sample of Falkor we got certainly impressed me a lot. This is a huge step up from any previous attempt at ARM based servers. Certainly core for core, the Intel Skylake is far superior, but when you look at the system level the performance becomes very attractive.</p><p>The production version of the Centriq SoC will feature up to 48 Falkor cores, running at a frequency of up to 2.6GHz, for a potential additional 8% better performance.</p><p>Obviously the Skylake server we tested is not the flagship Platinum unit that has 28 cores, but those 28 cores come both with a big price and over 200W TDP, whereas we are interested in improving our bang for buck metric, and performance per watt.</p><p>Currently, my main concern is weak Go language performance, but that is bound to improve quickly once ARM based servers start gaining some market share.</p><p>Both C and LuaJIT performance is very competitive, and in many cases outperforms the Skylake contender. In almost every benchmark Falkor shows itself as a worthy upgrade from Broadwell.</p><p>The largest win by far for Falkor is the low power consumption. Although it has a TDP of 120W, during my tests it never went above 89W (for the go benchmark). In comparison, Skylake and Broadwell both went over 160W, while the TDP of the two CPUs is 170W.</p><p><i>If you enjoy testing and selecting hardware on behalf of millions of Internet properties, come [join us](</i><a href="https://www.cloudflare.com/careers/"><i>https://www.cloudflare.com/careers/</i></a><i>).</i></p> ]]></content:encoded>
            <category><![CDATA[SSL]]></category>
            <category><![CDATA[OpenSSL]]></category>
            <category><![CDATA[Compression]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[LUA]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Go]]></category>
            <category><![CDATA[Cryptography]]></category>
            <guid isPermaLink="false">3Rk7Ip66PBd0Hn31OM4NuO</guid>
            <dc:creator>Vlad Krasnov</dc:creator>
        </item>
        <item>
            <title><![CDATA[A Very WebP New Year from Cloudflare]]></title>
            <link>https://blog.cloudflare.com/a-very-webp-new-year-from-cloudflare/</link>
            <pubDate>Wed, 21 Dec 2016 14:00:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare has an automatic image optimization feature called Polish, available for paid plan users. It recompresses images and stripping excess data, speeding up delivery to browsers. ]]></description>
            <content:encoded><![CDATA[ <p>Cloudflare has an automatic image optimization feature called <a href="/introducing-polish-automatic-image-optimizati/">Polish</a>, available to customers on paid plans. It recompresses images and removes unnecessary data so that they are delivered to browsers more quickly.</p><p>Up until now, Polish has not changed image types when optimizing (even if, for example, a PNG might sometimes have been smaller than the equivalent JPEG). But a new feature in Polish allows us to swap out an image for an equivalent image compressed using Google’s WebP format when the browser is capable of handling WebP and delivering that type of image would be quicker.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6RCs2VzrEL7pYO3RNWBRYa/5434d04bd47f702aa548ea48985c2aff/holly.jpg" />
            
            </figure><p><a href="https://creativecommons.org/licenses/by/2.0/">CC-BY 2.0</a> <a href="https://www.flickr.com/photos/john47kent/5307525503/">image</a> by <a href="https://www.flickr.com/photos/john47kent/">John Stratford</a></p>
    <div>
      <h3>What is WebP?</h3>
      <a href="#what-is-webp">
        
      </a>
    </div>
    <p>The main image formats used on the web haven’t changed much since the early days (apart from the SVG vector format, PNG was the last one to establish itself, <a href="https://en.wikipedia.org/wiki/Portable_Network_Graphics#History_and_development">almost two decades ago</a>).</p><p><a href="https://en.wikipedia.org/wiki/WebP">WebP</a> is a newer image format for the web, proposed by Google. It takes advantage of progress in <a href="https://www.cloudflare.com/learning/performance/glossary/what-is-image-compression/">image compression techniques</a> since formats such as JPEG and PNG were designed. It is often able to compress the images into a significantly smaller amount of data than the older formats.</p><p>WebP is versatile and able to replace the three main raster image formats used on the web today:</p><ul><li><p>WebP can do lossy compression, so it can be used instead of JPEG for photographic and photo-like images.</p></li><li><p>WebP can do lossless compression, and supports an alpha channel meaning images can have transparent regions. So it can be used instead of PNG, such as for images with sharp transitions that should be reproduced exactly (e.g. line art and graphic design elements).</p></li><li><p>WebP images can be animated, so it can be used as a replacement for animated GIF images.</p></li></ul><p>Currently, the main browser that supports WebP is Google’s Chrome (both on desktop and mobile devices). See <a href="http://caniuse.com/#feat=webp">the WebP page on caniuse.com</a> for more details.</p>
    <div>
      <h3>Polish WebP conversion</h3>
      <a href="#polish-webp-conversion">
        
      </a>
    </div>
    <p>Customers on the Pro, Business, and Enterprise plans can enable the automatic creation of WebP images by checking the WebP box in the Polish settings for a zone (these are found on the “Speed” page of the dashboard):</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Beloplqsl1SNVyjFSOyl4/d6853afc514b5a6fc9cf94758d629695/polish-webp.png" />
            
            </figure><p>When this is enabled, Polish will optimize images just as it always has. But it will also convert the image to WebP, if WebP can shrink the image data more than the original format. These WebP images are only returned to web browsers that indicate they support WebP (e.g. Google Chrome), so most websites using Polish should be able to benefit from WebP conversion.</p><p>(Although Polish can now produce WebP images by converting them from other formats, it can't consume WebP images to optimize them. If you put a WebP image on an origin site, Polish won't do anything with it. Until the WebP ecosystem grows and matures, it is uncertain that attempting to optimize WebP is worthwhile.)</p><p>Polish has two modes: <i>lossless</i> and <i>lossy</i>. In lossless mode, JPEG images are optimized to remove unnecessary data, but the image displayed is unchanged. In lossy mode, Polish reduces the quality of JPEG images in a way that should not have a significant visible effect, but allows it to further reduce the size of the image data.</p><p>These modes are respected when JPEG images are converted to WebP. In lossless mode, the conversion is done in a way that preserves the image as faithfully as possible (due to the nature of the conversion, the resulting WebP might not be exactly identical, but there are unlikely to be any visible differences). In lossy mode, the conversion sacrifices a little quality in order to shrink the image data further, but as before, there should not be a significant visible effect.</p><p>These modes do not affect PNGs and GIFs, as these are lossless formats and so Polish will preserve images in those formats exactly.</p><p>Note that WebP conversion does not change the URLs of images, even if the file extension in the URL implies a different format. For example, a JPEG image at <code>https://example.com/picture.jpg</code> that has been converted to WebP will still have that same URL. The “Content-Type” HTTP header tells the browser the true format of an image.</p>
    <div>
      <h3>By the Numbers</h3>
      <a href="#by-the-numbers">
        
      </a>
    </div>
    <p>A few studies have been published of how well WebP compresses images compared with established formats. These studies provide a useful picture of how WebP performs. But before we released our WebP support, we decided to do a survey based on the context on which we planned to use WebP:</p><ul><li><p>We evaluated WebP based on a collection of images gathered from the websites of our customers. The corpus consisted of 23,500 images (JPEG, PNG and GIFs).</p></li><li><p>Some studies compare WebP with JPEG by taking uncompressed images and compressing them to JPEG and WebP directly. But we wanted to know what happens when we convert an image that was has already been compressed as a JPEG. In a sense this is an unfair test, because a JPEG may contain artifacts due to compression that would not be present in the original raw image, and conversion to WebP may try to retain those artifacts. But it is such conversions matter for our use of WebP (this consideration does not apply to PNG and GIF conversions, because they are lossless).</p></li><li><p>We’re not just interested in whether WebP conversion can shrink images found on the web. We want to know how much WebP allows Polish to reduce the size further than it already does, thus providing a real end-user benefit. So our survey also includes the results of Polish without WebP.</p></li><li><p>In some cases, converting to WebP does not produce a result smaller than the optimized image in the original format. In such cases, we discard the WebP image. So the figures presented below do not penalize WebP for such cases.</p></li></ul><p>Here is a chart showing the results of Polish, with and without WebP conversion. For each format, the average original image size is normalized to 100%, and the average sizes after Polishing are shown relative to that.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5YazeQmjUBmLEir39hYrUh/bab2f4768bf57f5a1f8aecd3f07d9d9a/webp-chart.png" />
            
            </figure><p>Here are the average savings corresponding to the chart:</p><table><tr><td><p><b>Original Format</b></p></td><td><p><b>Polish without WebP</b></p></td><td><p><b>Polish using WebP</b></p></td></tr><tr><td><p>JPEG (with Polish lossless mode)</p></td><td><p>9%</p></td><td><p>19%</p></td></tr><tr><td><p>JPEG (with Polish lossy mode)</p></td><td><p>34%</p></td><td><p>47%</p></td></tr><tr><td><p>PNG</p></td><td><p>16%</p></td><td><p>38%</p></td></tr><tr><td><p>GIF</p></td><td><p>3%</p></td><td><p>16%</p></td></tr></table><p>(The saving is calculated as 100% - (polished size) / (original size).)</p><p>As you can see, WebP conversion achieves significant size improvements not only for JPEG images, but also for PNG and GIF images. We believe supporting WebP will result in lower bandwidth and faster website delivery.</p>
    <div>
      <h3>… and a WebP New Year</h3>
      <a href="#and-a-webp-new-year">
        
      </a>
    </div>
    <p>WebP does not yet have the same level of browser support as JPEG, PNG and GIF, but we are excited about its potential to streamline web pages. Polish WebP conversion allows our customers to adopt WebP with a simple change to the settings in the Cloudflare dashboard. So, if you are on one of our <a href="https://www.cloudflare.com/plans/">paid plans</a>, we encourage you to try it out today.</p><p>PS — Want to help optimize the web? We’re <a href="https://www.cloudflare.com/join-our-team/">hiring</a>.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Polish]]></category>
            <category><![CDATA[Compression]]></category>
            <category><![CDATA[Optimization]]></category>
            <category><![CDATA[WebP]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <guid isPermaLink="false">7M6kU8b93kMUiHWhgQTkjv</guid>
            <dc:creator>David Wragg</dc:creator>
        </item>
        <item>
            <title><![CDATA[Results of experimenting with Brotli for dynamic web content]]></title>
            <link>https://blog.cloudflare.com/results-experimenting-brotli/</link>
            <pubDate>Fri, 23 Oct 2015 14:24:50 GMT</pubDate>
            <description><![CDATA[ Compression is one of the most important tools CloudFlare has to accelerate website performance. Compressed content takes less time to transfer, and consequently reduces load times. ]]></description>
            <content:encoded><![CDATA[ <p>Compression is one of the most important tools CloudFlare has to accelerate website performance. Compressed content takes less time to transfer, and consequently reduces load times. On expensive mobile data plans, compression even saves money for consumers. However, compression is not free—it comes at a price. It is one of the most compute expensive operations our servers perform, and the better the compression rate we want, the more effort we have to spend.</p><p>The most popular compression format on the web is gzip. We put a great deal of effort into improving the performance of the gzip compression, so we can perform compression on the fly with fewer CPU cycles. Recently a potential replacement for gzip, called Brotli, was announced by Google. Being early adopters for many technologies, we at CloudFlare want to see for ourselves if it is as good as claimed.</p><p>This post takes a look at a bit of history behind gzip and Brotli, followed by a performance comparison.</p>
    <div>
      <h3>Compression 101</h3>
      <a href="#compression-101">
        
      </a>
    </div>
    <p>Many popular lossless compression algorithms rely on LZ77 and Huffman coding, so it’s important to have a basic understanding of these two techniques before getting into gzip or Brotli.</p>
    <div>
      <h4>LZ77</h4>
      <a href="#lz77">
        
      </a>
    </div>
    <p>LZ77 is a simple technique developed by Abraham Lempel and Jacob Ziv in 1977 (hence the original name). Let's call the input to the algorithm a string (a sequence of bytes, not necessarily letters) and each consecutive sequence of bytes in the input a substring. LZ77 compresses the input string by replacing some of its substrings by pointers (or backreferences) to an identical substring previously encountered in the input.</p><p>The pointer usually has the form of <code>&lt;length, distance&gt;</code>, where length indicates the number of identical bytes found, and distance indicates how many bytes separate the current occurrence of the substring from the previous one. For example the string <code>abcdeabcdf</code> can be compressed with LZ77 to <code>abcde&lt;4,5&gt;f</code> and <code>aaaaaaaaaa</code> can be compressed to simply <code>a&lt;9, 1&gt;</code>. The decompressor when encountering a backreference will simply copy the required number of bytes from the already decompressed output, which makes it very fast for decompression. This is a nice illustration of LZ77 from my previous blog <a href="/improving-compression-with-preset-deflate-dictionary/">Improving compression with a preset DEFLATE dictionary</a>:</p><table><tr><td><p>L</p></td><td><p>i</p></td><td><p>t</p></td><td><p>t</p></td><td><p>l</p></td><td><p>e</p></td><td><p></p></td><td><p>b</p></td><td><p>u</p></td><td><p>n</p></td><td><p>n</p></td><td><p>y</p></td><td><p></p></td><td><p>F</p></td><td><p>o</p></td><td><p>o</p></td><td><p></p></td><td><p>F</p></td><td><p>o</p></td><td><p>o</p></td></tr><tr><td><p></p></td><td><p>W</p></td><td><p>e</p></td><td><p>n</p></td><td><p>t</p></td><td><p></p></td><td><p>h</p></td><td><p>o</p></td><td><p>p</p></td><td><p>p</p></td><td><p>i</p></td><td><p>n</p></td><td><p>g</p></td><td><p></p></td><td><p>t</p></td><td><p>h</p></td><td><p>r</p></td><td><p>o</p></td><td><p>u</p></td><td><p>g</p></td></tr><tr><td><p>h</p></td><td><p></p></td><td><p>t</p></td><td><p>h</p></td><td><p>e</p></td><td><p></p></td><td><p>f</p></td><td><p>o</p></td><td><p>r</p></td><td><p>e</p></td><td><p>s</p></td><td><p>t</p></td><td><p></p></td><td><p>S</p></td><td><p>c</p></td><td><p>o</p></td><td><p>o</p></td><td><p>p</p></td><td><p>i</p></td><td><p>n</p></td></tr><tr><td><p>g</p></td><td><p></p></td><td><p>u</p></td><td><p>p</p></td><td><p></p></td><td><p>t</p></td><td><p>h</p></td><td><p>e</p></td><td><p></p></td><td><p>f</p></td><td><p>i</p></td><td><p>e</p></td><td><p>l</p></td><td><p>d</p></td><td><p></p></td><td><p>m</p></td><td><p>i</p></td><td><p>c</p></td><td><p>e</p></td><td><p></p></td></tr><tr><td><p>A</p></td><td><p>n</p></td><td><p>d</p></td><td><p></p></td><td><p>b</p></td><td><p>o</p></td><td><p>p</p></td><td><p>p</p></td><td><p>i</p></td><td><p>n</p></td><td><p>g</p></td><td><p></p></td><td><p>t</p></td><td><p>h</p></td><td><p>e</p></td><td><p>m</p></td><td><p></p></td><td><p>o</p></td><td><p>n</p></td><td><p></p></td></tr><tr><td><p>t</p></td><td><p>h</p></td><td><p>e</p></td><td><p></p></td><td><p>h</p></td><td><p>e</p></td><td><p>a</p></td><td><p>d</p></td><td><p></p></td><td><p>D</p></td><td><p>o</p></td><td><p>w</p></td><td><p>n</p></td><td><p></p></td><td><p>c</p></td><td><p>a</p></td><td><p>m</p></td><td><p>e</p></td><td><p></p></td><td><p>t</p></td></tr><tr><td><p>h</p></td><td><p>e</p></td><td><p></p></td><td><p>G</p></td><td><p>o</p></td><td><p>o</p></td><td><p>d</p></td><td><p></p></td><td><p>F</p></td><td><p>a</p></td><td><p>i</p></td><td><p>r</p></td><td><p>y</p></td><td><p>,</p></td><td><p></p></td><td><p>a</p></td><td><p>n</p></td><td><p>d</p></td><td><p></p></td><td><p>s</p></td></tr><tr><td><p>h</p></td><td><p>e</p></td><td><p></p></td><td><p>s</p></td><td><p>a</p></td><td><p>i</p></td><td><p>d</p></td><td><p></p></td><td><p>"</p></td><td><p>L</p></td><td><p>i</p></td><td><p>t</p></td><td><p>t</p></td><td><p>l</p></td><td><p>e</p></td><td><p></p></td><td><p>b</p></td><td><p>u</p></td><td><p>n</p></td><td><p>n</p></td></tr><tr><td><p>y</p></td><td><p></p></td><td><p>F</p></td><td><p>o</p></td><td><p>o</p></td><td><p></p></td><td><p>F</p></td><td><p>o</p></td><td><p>o</p></td><td><p></p></td><td><p>I</p></td><td><p></p></td><td><p>d</p></td><td><p>o</p></td><td><p>n</p></td><td><p>'</p></td><td><p>t</p></td><td><p></p></td><td><p>w</p></td><td><p>a</p></td></tr><tr><td><p>n</p></td><td><p>t</p></td><td><p></p></td><td><p>t</p></td><td><p>o</p></td><td><p></p></td><td><p>s</p></td><td><p>e</p></td><td><p>e</p></td><td><p></p></td><td><p>y</p></td><td><p>o</p></td><td><p>u</p></td><td><p></p></td><td><p>S</p></td><td><p>c</p></td><td><p>o</p></td><td><p>o</p></td><td><p>p</p></td><td><p>i</p></td></tr><tr><td><p>n</p></td><td><p>g</p></td><td><p></p></td><td><p>u</p></td><td><p>p</p></td><td><p></p></td><td><p>t</p></td><td><p>h</p></td><td><p>e</p></td><td><p></p></td><td><p>f</p></td><td><p>i</p></td><td><p>e</p></td><td><p>l</p></td><td><p>d</p></td><td><p></p></td><td><p>m</p></td><td><p>i</p></td><td><p>c</p></td><td><p>e</p></td></tr><tr><td><p></p></td><td><p>A</p></td><td><p>n</p></td><td><p>d</p></td><td><p></p></td><td><p>b</p></td><td><p>o</p></td><td><p>p</p></td><td><p>p</p></td><td><p>i</p></td><td><p>n</p></td><td><p>g</p></td><td><p></p></td><td><p>t</p></td><td><p>h</p></td><td><p>e</p></td><td><p>m</p></td><td><p></p></td><td><p>o</p></td><td><p>n</p></td></tr><tr><td><p></p></td><td><p>t</p></td><td><p>h</p></td><td><p>e</p></td><td><p></p></td><td><p>h</p></td><td><p>e</p></td><td><p>a</p></td><td><p>d</p></td><td><p>.</p></td><td><p>"</p></td><td><p></p></td><td><p></p></td><td><p></p></td><td><p></p></td><td><p></p></td><td><p></p></td><td><p></p></td><td><p></p></td><td><p></p></td></tr></table><p>Output (length tokens are blue, distance tokens are red):</p><table><tr><td><p>L</p></td><td><p>i</p></td><td><p>t</p></td><td><p>t</p></td><td><p>l</p></td><td><p>e</p></td><td><p></p></td><td><p>b</p></td><td><p>u</p></td><td><p>n</p></td><td><p>n</p></td><td><p>y</p></td><td><p></p></td><td><p>F</p></td><td><p>o</p></td><td><p>o</p></td><td><p>5</p></td><td><p>4</p></td><td><p>W</p></td><td><p>e</p></td></tr><tr><td><p>n</p></td><td><p>t</p></td><td><p></p></td><td><p>h</p></td><td><p>o</p></td><td><p>p</p></td><td><p>p</p></td><td><p>i</p></td><td><p>n</p></td><td><p>g</p></td><td><p></p></td><td><p>t</p></td><td><p>h</p></td><td><p>r</p></td><td><p>o</p></td><td><p>u</p></td><td><p>g</p></td><td><p>h</p></td><td><p>3</p></td><td><p>8</p></td></tr><tr><td><p>e</p></td><td><p></p></td><td><p>f</p></td><td><p>o</p></td><td><p>r</p></td><td><p>e</p></td><td><p>s</p></td><td><p>t</p></td><td><p></p></td><td><p>S</p></td><td><p>c</p></td><td><p>o</p></td><td><p>o</p></td><td><p>5</p></td><td><p>28</p></td><td><p>u</p></td><td><p>p</p></td><td><p>6</p></td><td><p>23</p></td><td><p>i</p></td></tr><tr><td><p>e</p></td><td><p>l</p></td><td><p>d</p></td><td><p></p></td><td><p>m</p></td><td><p>i</p></td><td><p>c</p></td><td><p>e</p></td><td><p></p></td><td><p>A</p></td><td><p>n</p></td><td><p>d</p></td><td><p></p></td><td><p>b</p></td><td><p>9</p></td><td><p>58</p></td><td><p>e</p></td><td><p>m</p></td><td><p></p></td><td><p>o</p></td></tr><tr><td><p>n</p></td><td><p>5</p></td><td><p>35</p></td><td><p>h</p></td><td><p>e</p></td><td><p>a</p></td><td><p>d</p></td><td><p></p></td><td><p>D</p></td><td><p>o</p></td><td><p>w</p></td><td><p>n</p></td><td><p></p></td><td><p>c</p></td><td><p>a</p></td><td><p>m</p></td><td><p>e</p></td><td><p>5</p></td><td><p>19</p></td><td><p>G</p></td></tr><tr><td><p>o</p></td><td><p>o</p></td><td><p>d</p></td><td><p></p></td><td><p>F</p></td><td><p>a</p></td><td><p>i</p></td><td><p>r</p></td><td><p>y</p></td><td><p>,</p></td><td><p></p></td><td><p>a</p></td><td><p>3</p></td><td><p>55</p></td><td><p>s</p></td><td><p>3</p></td><td><p>20</p></td><td><p>s</p></td><td><p>a</p></td><td><p>i</p></td></tr><tr><td><p>d</p></td><td><p></p></td><td><p>"</p></td><td><p>L</p></td><td><p>20</p></td><td><p>149</p></td><td><p>I</p></td><td><p></p></td><td><p>d</p></td><td><p>o</p></td><td><p>n</p></td><td><p>'</p></td><td><p>t</p></td><td><p></p></td><td><p>w</p></td><td><p>a</p></td><td><p>3</p></td><td><p>157</p></td><td><p>t</p></td><td><p>o</p></td></tr><tr><td><p></p></td><td><p>s</p></td><td><p>e</p></td><td><p>e</p></td><td><p></p></td><td><p>y</p></td><td><p>o</p></td><td><p>u</p></td><td><p>56</p></td><td><p>141</p></td><td><p>.</p></td><td><p>"</p></td><td><p></p></td><td><p></p></td><td><p></p></td><td><p></p></td><td><p></p></td><td><p></p></td><td><p></p></td><td><p></p></td></tr></table><p>The deflate algorithm managed to reduce the original text from 251 characters, to just 152 tokens! Those tokens are later compressed further by Huffman coding.</p>
    <div>
      <h4>Huffman Coding</h4>
      <a href="#huffman-coding">
        
      </a>
    </div>
    <p>Huffman coding is another lossless compression algorithm. Developed by David Huffman back in the 50s, it is used for many compression algorithms, including JPEG. A Huffman code is a type of prefix coding, where, given an alphabet and an input, frequently occurring characters are replaced by shorter bit sequences and rarely occurring characters are replaced with longer sequences.</p><p>The code can be expressed as a binary tree, where the leaf nodes are the literals of the alphabet and the two edges from each node are marked with 0 and 1. To decode the next character, the decompressor can parse the tree from the root until it encounters a literal in the tree.</p><p>Each compression format uses Huffman coding differently, and for our little example we will create a Huffman code for an alphabet that includes only the literals and the length codes we actually used. To start, we must count the frequency of each letter in the LZ77 compressed text:</p><p>(space) - 19, o -14, e - 11, n - 8, t - 7, a - 6, d - 6, i - 6, 3 - 4, 5 - 4, h - 4, s - 4, u - 4, c - 3, m - 3, p - 3, r - 3, y - 3, (") - 2, F - 2, L - 2, b - 2, g - 2, l - 2, w - 2, (') - 1, (,) - 1, (.) - 1, A - 1, D - 1, G - 1, I - 1, 9 - 1, 20 - 1, S - 1, 56 - 1, W - 1, 6 - 1, f - 1.</p><p>We can then use the algorithm from <a href="https://en.wikipedia.org/wiki/Huffman_coding">Wikipedia</a> to build this Huffman code (binary):</p><p>(space) - 101, o - 000, e - 1101, n - 0101, t - 0011, a - 11101, d - 11110, i - 11100, 3 - 01100, 5 - 01111, h - 10001, s - 11000, u - 10010, c - 00100, m - 111111, p - 110011, r - 110010, y - 111110, (") - 011010, F - 010000, L - 100000, b - 011101, g - 010011, l - 011100, w - 011011, (') - 0100101, (,) - 1001110, (.) - 1001100, A - 0100011, D - 0100010, G - 1000011, I - 0010110, 9 - 1000010, 20 - 0010111, S - 0010100, 56 - 0100100, W - 0010101, 6 - 1001101, f - 1001111.</p><p>Here the most frequent letters - space and 'o' got the shortest codes, only 3 bit long, whereas the letters that occur only once get 7 bit codes. If we were to represent the alphabet of 256 bytes and some length tokens, we would require 9 bits per every letter.</p><p>Now apply the code to the LZ77 output, (leaving the distance tokens untouched) and we get:</p><p>100000 11100 0011 0011 011100 1101 101 011101 10010 0101 0101 111110 101 010000 000 000 01111 4 0010101 1101 0101 0011 101 10001 000 110011 110011 11100 0101 010011 101 0011 10001 110010 000 10010 010011 01100 8 10001 1101 101 1001111 000 110010 1101 11000 0011 101 0010100 00100 000 000 01111 28 10010 110011 1001101 23 11100 1101 011100 11110 101 111111 11100 00100 1101 101 0100011 0101 11110 101 011101 1000010 58 1101 111111 101 000 0101 01111 35 10001 1101 11101 11110 101 0100010 000 011011 0101 101 00100 11101 111111 1101 01111 19 1000011 000 000 11110 101 010000 11101 11100 110010 111110 1001110 101 11101 01100 55 11000 01100 20 11000 11101 11100 11110 101 011010 100000 0010111 149 0010110 101 11110 000 0101 0100101 0011 101 011011 11101 01100 157 0011 000 101 11000 1101 1101 101 111110 000 10010 0100100 141 1001100 011010</p><p>Assuming all the distance tokens require one byte to store, the total output length is 898 bits. Compared to the 1356 bits we would require to store the LZ77 output, and 2008 bits for the original input. We achieved a compression ratio of 31% which is very good. In reality this is not the case, since we must also encode the Huffman tree we used, otherwise it would be impossible to decompress the text.</p>
    <div>
      <h4>gzip</h4>
      <a href="#gzip">
        
      </a>
    </div>
    <p>The compression algorithm used in gzip is called "deflate," and it’s a combination of the LZ77 and Huffman algorithms discussed above.</p><p>On the LZ77 side, gzip has a minimum size of 3 bytes and a maximum of 258 bytes for the length tokens and a maximum of 32768 bytes for the distance tokens. The maximum distance also defines the sliding window size the implementation uses for compression and decompression.</p><p>For Huffman coding, deflate has two alphabets. The first alphabet includes the input literals ("letters" 0-255), the end-of-block symbol (the "letter" 256) and the length tokens ("letters" 257-285). There are only 29 letters to encode all the possible lengths and the tokens 265-284 will always have additional bits encoded in the stream to cover the entire range from 3 to 258. For example the letter 257 indicates the minimal length of 3, whereas the letter 265 will indicate the length 11 if followed by the bit 0, and the length 12 if followed by the bit 1.</p><p>The second alphabet is for the distance tokens only. Its letters are the codewords 0 through 29. Similar to the length tokens, the codes 4-29 are followed by 1 to 13 additional bits to cover the whole range from 1 to 32768.</p><p>Using two distinct alphabets makes a lot of sense, because it allows us to represent the distance code with fewer bits while avoiding any ambiguity, since the distance codes always follow the length codes.</p><p>The most common implementation of gzip compression is the zlib library. At CloudFlare, we use a custom version of zlib <a href="/cloudflare-fights-cancer/">optimized for our servers</a>.</p><p>zlib has 9 preset quality settings for the deflate algorithm, labeled from 1 to 9. It can also be run with quality set to 0, in which case it does not perform any compression. These quality settings can be divided into two categories:</p><ul><li><p>Fast compression (levels 1-3): When a sufficiently long backreference is found, it is emitted immediately, and the search moves to the position at the end of the matched string. If the match is longer than a few bytes, the entire matched string will not be hashed, meaning its substrings will never be referenced in the future. Clearly, this reduces the compression ratio.</p></li><li><p>Slow compression (levels 4-9): Here, <i>every</i> substring is hashed; therefore, any substring can be referenced in the future. In addition, slow compression enables "lazy" matches. If at a given position a sufficiently long match is found, it will not be immediately emitted. Instead, the algorithm attempts to find a match at the next position. If a longer match is found, the algorithm will emit a single literal at the current position instead the shorter match, and continue to the next position. As a rule, this results in a better compression ratio than levels 1-3.</p></li></ul><p>Other differences between the quality levels are how far to search for a backreference and how long a match should be before stopping.</p><p>A very decent compression rate can be observed at levels 3-4, which are also quite fast. Increasing compression quality from level 4 and up gives incrementally smaller compression gains, while requiring substantially more time. Often, levels 8 and 9 will produce similarly compressed output, but level 9 will require more time to do it.</p><p>It is important to understand that the zlib implementation does not guarantee the best compression possible with the format, even when the quality setting is set to 9. Instead, it uses a set of heuristics and optimizations that allow for very good compression at reasonable speed.</p><p>Better gzip compression can be achieved by the <a href="https://github.com/google/zopfli">zopfli</a> library, but it is significantly slower than zlib.</p>
    <div>
      <h4>Brotli</h4>
      <a href="#brotli">
        
      </a>
    </div>
    <p>The Brotli format was developed by Google and has been refined for a while now. Here at CloudFlare, we built an nginx module that performs dynamic Brotli compression, and we deployed it on our <a href="https://http2.cloudflare.com/">test server</a> that supports HTTP2 and other new features.</p><p>To check the Brotli compression on the test server you can use the nightly build of the Firefox browser, which also supports this format.</p>
    <div>
      <h4>Brotli, deflate, and gzip</h4>
      <a href="#brotli-deflate-and-gzip">
        
      </a>
    </div>
    <p>Brotli and deflate are very closely related. Brotli also uses the LZ77 and Huffman algorithms for compression. Both algorithms use a sliding window for backreferences. Gzip uses a fixed size, 32KB window, and Brotli can use any window size from 1KB to 16MB, in powers of 2 (minus 16 bytes). This means that the Brotli window can be up to 512 times larger window than the deflate window. This difference is almost irrelevant in web-server context, as text files larger than 32KB are the minority.</p><p>Other differences include smaller minimal match length (2 bytes minimum in Brotli, compared to 3 bytes minimum in deflate) and larger maximal match length (16779333 bytes in Broli, compared to 258 bytes in deflate).</p>
    <div>
      <h4>Static dictionary</h4>
      <a href="#static-dictionary">
        
      </a>
    </div>
    <p>Brotli also features a static dictionary. The "dictionary" supported by deflate can greatly improve compression, but has to be supplied independently and can only be addressed as part of the sliding window. The Brotli dictionary is part of the implementation and can be referenced from anywhere in the stream, somewhat increasing its efficiency for larger files. Moreover, different transformations can be applied to words of the dictionary effectively increasing its size.</p>
    <div>
      <h4>Context modeling</h4>
      <a href="#context-modeling">
        
      </a>
    </div>
    <p>Brotli also supports something called context modeling. Context modeling is a feature that allows multiple Huffman trees for the same alphabet in the same block. For example, in deflate each block consists of a series of literals (bytes that could not be compressed by backreferencing) and <code>&lt;length, distance&gt;</code> pairs that define a backreference for copying. Literals and lengths form a single alphabet, while the distances are a different alphabet.</p><p>In Brotli, each block is composed of "commands". A command consists of 3 parts. The first part of each command is a word <code>&lt;insert, copy&gt;</code>. "Insert" defines the number of literals that will follow the word, and it may have the value of 0. "Copy" defines the number of literals to copy from a back reference. The word <code>&lt;insert,copy&gt;</code> is followed by a sequence of "insert" literals: <code>&lt;lit&gt; … &lt;lit&gt;</code>. Finally, the command ends with a <code>&lt;distance&gt;</code>. Distance defines the backreference from which to copy the previously defined number of bytes. Unlike the distance in deflate, Brotli distance can have additional meanings, such as references to the static dictionary, or references to recently used distances.</p><p>There are three alphabets here. One is for <code>&lt;lit&gt;</code>s and it simply covers all the possible byte values from 0 to 255. The other one is for <code>&lt;distance&gt;</code>s, and its size depends on the size of the sliding window and other parameters. The third alphabet is for the <code>&lt;insert,copy&gt;</code> length pairs, with 704 letters. Here <code>&lt;insert, copy&gt;</code> indicates a single letter in the alphabet, as opposed to <code>&lt;length,distance&gt;</code> pairs in deflate where length and distance are letters in distinct alphabets.</p><p>So why do we care about context modeling? It means that for any of the alphabets—up to 256 different Huffman trees—can be used in the same block. The switch between different trees is determined by "context". This can be useful when the compressed file consists of different types of characters. For example binary data interleaved with UTF-8 strings, or a multilingual dictionary.</p><p>Whereas the basic idea behind Brotli remains identical to that of deflate, the way the data is encoded is very different. Those improvements allow for significantly better compression, but they also require a significant amount of processing. To alleviate the performance cost somewhat, Brotli drops the error detection CRC check present in gzip.</p>
    <div>
      <h3>Benchmarking</h3>
      <a href="#benchmarking">
        
      </a>
    </div>
    <p>The heaviest operation in both deflate and Brotli is the search for backward references. A higher level in zlib generally means that the backward reference search will attempt to find a better match for longer, but not necessarily succeed. This leads to significant increase in processing time. In contrast, Brotli trades longer searches for lighter operations that give better ROI, such as context modeling, dictionary references and more efficient data encoding in general. For that reason Brotli is, in theory, capable of outperforming zlib at some stage for similar compression rates, as well as giving better compression at its maximal levels.</p><p>We decided to put those theories to the test. At CloudFlare, one of the primary use cases for Brotli/gzip is on-the-fly compression of textual web assets like HTML, CSS, and JavaScript, so that’s what we’ll be testing.</p><p>There is a tradeoff between compression speed and transfer speed. It is only beneficial to increase your compression ratio if you can reduce the number of bytes you have to transfer faster than you would actually transfer them. Slower compression will actually slow the connection down. CloudFlare currently uses our own zlib implementation with quality set to 8, and that is our benchmarking baseline.</p><p>The benchmark set consists of 10,655 HTML, CSS, and JavaScript files. The benchmarks were performed on an Intel 3.5GHz, E3-1241 v3 CPU.</p><p>The files were grouped into several size groups, since different websites have different size characteristics, and we must optimize for them all.</p><p>Although the quality setting in the Brotli implementation states the possible values are 0 to 11, we didn't see any differences between 0 and 1, or between 10 and 11, therefore only quality settings 1 to 10 are reported.</p>
    <div>
      <h3>Compression quality</h3>
      <a href="#compression-quality">
        
      </a>
    </div>
    <p>Compression quality is measured as (total size of all files after compression)/(total size of all files before compression)*100%. The columns represent the size distribution of the HTML, CSS, and JavaScript files used in the test in bytes (the file size ranges are shown using the [x,y) notation).</p><table><tr><td><p>
</p></td><td><p><b>[20,
1024)</b></p></td><td><p><b>[1024,
2048)</b></p></td><td><p><b>[2048,
3072)</b></p></td><td><p><b>[3072,
4096)</b></p></td><td><p><b>[4096,
8192)</b></p></td><td><p><b>[8192,
16384)</b></p></td><td><p><b>[16384,
32768)</b></p></td><td><p><b>[32768,
65536)</b></p></td><td><p><b>[65536,
+∞)</b></p></td><td><p><b>All files</b></p></td></tr><tr><td><p>zlib 1</p></td><td><p>65.0%</p></td><td><p>46.8%</p></td><td><p>42.8%</p></td><td><p>38.7%</p></td><td><p>34.4%</p></td><td><p>32.0%</p></td><td><p>29.9%</p></td><td><p>31.1%</p></td><td><p>31.4%</p></td><td><p>31.5%</p></td></tr><tr><td><p>zlib 2</p></td><td><p>64.9%</p></td><td><p>46.5%</p></td><td><p>42.5%</p></td><td><p>38.3%</p></td><td><p>33.8%</p></td><td><p>31.4%</p></td><td><p>29.2%</p></td><td><p>30.3%</p></td><td><p>30.5%</p></td><td><p>30.6%</p></td></tr><tr><td><p>zlib 3</p></td><td><p>64.9%</p></td><td><p>46.4%</p></td><td><p>42.3%</p></td><td><p>38.0%</p></td><td><p>33.6%</p></td><td><p>31.1%</p></td><td><p>28.8%</p></td><td><p>29.8%</p></td><td><p>29.9%</p></td><td><p>30.1%</p></td></tr><tr><td><p>zlib 4</p></td><td><p>64.5%</p></td><td><p>45.8%</p></td><td><p>41.6%</p></td><td><p>37.1%</p></td><td><p>32.6%</p></td><td><p>30.0%</p></td><td><p>27.8%</p></td><td><p>28.5%</p></td><td><p>28.7%</p></td><td><p>28.9%</p></td></tr><tr><td><p>zlib 5</p></td><td><p>64.5%</p></td><td><p>45.5%</p></td><td><p>41.3%</p></td><td><p>36.7%</p></td><td><p>32.1%</p></td><td><p>29.4%</p></td><td><p>27.1%</p></td><td><p>27.8%</p></td><td><p>27.8%</p></td><td><p>28.0%</p></td></tr><tr><td><p>zlib 6</p></td><td><p>64.4%</p></td><td><p>45.5%</p></td><td><p>41.3%</p></td><td><p>36.7%</p></td><td><p>32.0%</p></td><td><p>29.3%</p></td><td><p>27.0%</p></td><td><p>27.6%</p></td><td><p>27.6%</p></td><td><p>27.8%</p></td></tr><tr><td><p>zlib 7</p></td><td><p>64.4%</p></td><td><p>45.5%</p></td><td><p>41.3%</p></td><td><p>36.6%</p></td><td><p>32.0%</p></td><td><p>29.2%</p></td><td><p>26.9%</p></td><td><p>27.5%</p></td><td><p>27.5%</p></td><td><p>27.7%</p></td></tr><tr><td><p>zlib 8</p></td><td><p>64.4%</p></td><td><p>45.5%</p></td><td><p>41.3%</p></td><td><p>36.6%</p></td><td><p>32.0%</p></td><td><p>29.2%</p></td><td><p>26.9%</p></td><td><p>27.5%</p></td><td><p>27.4%</p></td><td><p>27.7%</p></td></tr><tr><td><p>zlib 9</p></td><td><p>64.4%</p></td><td><p>45.5%</p></td><td><p>41.3%</p></td><td><p>36.6%</p></td><td><p>32.0%</p></td><td><p>29.2%</p></td><td><p>26.9%</p></td><td><p>27.5%</p></td><td><p>27.4%</p></td><td><p>27.7%</p></td></tr><tr><td><p>brotli 1</p></td><td><p>61.3%</p></td><td><p>46.6%</p></td><td><p>42.7%</p></td><td><p>38.4%</p></td><td><p>33.8%</p></td><td><p>30.8%</p></td><td><p>28.6%</p></td><td><p>29.5%</p></td><td><p>28.6%</p></td><td><p>29.0%</p></td></tr><tr><td><p>brotli 2</p></td><td><p>61.9%</p></td><td><p>46.9%</p></td><td><p>42.8%</p></td><td><p>38.3%</p></td><td><p>33.6%</p></td><td><p>30.6%</p></td><td><p>28.3%</p></td><td><p>29.1%</p></td><td><p>28.3%</p></td><td><p>28.6%</p></td></tr><tr><td><p>brotli 3</p></td><td><p>61.8%</p></td><td><p>46.8%</p></td><td><p>42.7%</p></td><td><p>38.2%</p></td><td><p>33.3%</p></td><td><p>30.4%</p></td><td><p>28.1%</p></td><td><p>28.9%</p></td><td><p>28.0%</p></td><td><p>28.4%</p></td></tr><tr><td><p>brotli 4</p></td><td><p>53.8%</p></td><td><p>40.8%</p></td><td><p>38.6%</p></td><td><p>34.8%</p></td><td><p>30.9%</p></td><td><p>28.7%</p></td><td><p>27.0%</p></td><td><p>28.0%</p></td><td><p>27.5%</p></td><td><p>27.7%</p></td></tr><tr><td><p>brotli 5</p></td><td><p>49.9%</p></td><td><p>37.7%</p></td><td><p>35.7%</p></td><td><p>32.3%</p></td><td><p>28.7%</p></td><td><p>26.6%</p></td><td><p>25.2%</p></td><td><p>26.2%</p></td><td><p>26.0%</p></td><td><p>26.1%</p></td></tr><tr><td><p>brotli 6</p></td><td><p>50.0%</p></td><td><p>37.7%</p></td><td><p>35.7%</p></td><td><p>32.3%</p></td><td><p>28.6%</p></td><td><p>26.5%</p></td><td><p>25.1%</p></td><td><p>26.0%</p></td><td><p>25.7%</p></td><td><p>25.9%</p></td></tr><tr><td><p>brotli 7</p></td><td><p>50.0%</p></td><td><p>37.6%</p></td><td><p>35.6%</p></td><td><p>32.3%</p></td><td><p>28.5%</p></td><td><p>26.4%</p></td><td><p>25.0%</p></td><td><p>25.9%</p></td><td><p>25.5%</p></td><td><p>25.7%</p></td></tr><tr><td><p>brotli 8</p></td><td><p>50.0%</p></td><td><p>37.6%</p></td><td><p>35.6%</p></td><td><p>32.3%</p></td><td><p>28.5%</p></td><td><p>26.4%</p></td><td><p>25.0%</p></td><td><p>25.9%</p></td><td><p>25.4%</p></td><td><p>25.6%</p></td></tr><tr><td><p>brotli 9</p></td><td><p>50.0%</p></td><td><p>37.6%</p></td><td><p>35.5%</p></td><td><p>32.2%</p></td><td><p>28.5%</p></td><td><p>26.4%</p></td><td><p>25.0%</p></td><td><p>25.8%</p></td><td><p>25.3%</p></td><td><p>25.5%</p></td></tr><tr><td><p>brotli 10</p></td><td><p>46.8%</p></td><td><p>33.4%</p></td><td><p>32.5%</p></td><td><p>29.4%</p></td><td><p>26.0%</p></td><td><p>23.9%</p></td><td><p>22.9%</p></td><td><p>23.8%</p></td><td><p>23.0%</p></td><td><p>23.3%</p></td></tr></table><p>Clearly, the compression possible by Brotli is significant. On average, Brotli at the maximal quality setting produces 1.19X smaller results than zlib at the maximal quality. For files smaller than 1KB the result is 1.38X smaller on average, a very impressive improvement, that can probably be attributed to the use of static dictionary.</p>
    <div>
      <h3>Compression speed</h3>
      <a href="#compression-speed">
        
      </a>
    </div>
    <p>Compression speed is measured as (total size of files before compression)/(total time to compress all files) and is reported in MB/s.</p><table><tr><td><p></p></td><td><p><b>[20,
1024)</b></p></td><td><p><b>[1024,
2048)</b></p></td><td><p><b>[2048,
3072)</b></p></td><td><p><b>[3072,
4096)</b></p></td><td><p><b>[4096,
8192)</b></p></td><td><p><b>[8192,
16384)</b></p></td><td><p><b>[16384,
32768)</b></p></td><td><p><b>[32768,
65536)</b></p></td><td><p><b>[65536,
+∞)</b></p></td><td><p><b>All files</b></p></td></tr><tr><td><p>zlib 1</p></td><td><p>5.9</p></td><td><p>21.8</p></td><td><p>34.4</p></td><td><p>43.4</p></td><td><p>62.1</p></td><td><p>89.8</p></td><td><p>117.9</p></td><td><p>127.9</p></td><td><p>139.6</p></td><td><p>125.5</p></td></tr><tr><td><p>zlib 2</p></td><td><p>5.9</p></td><td><p>21.7</p></td><td><p>34.3</p></td><td><p>43.0</p></td><td><p>61.2</p></td><td><p>87.6</p></td><td><p>114.3</p></td><td><p>123.1</p></td><td><p>130.7</p></td><td><p>118.9</p></td></tr><tr><td><p>zlib 3</p></td><td><p>5.9</p></td><td><p>21.7</p></td><td><p>34.0</p></td><td><p>42.4</p></td><td><p>60.5</p></td><td><p>84.8</p></td><td><p>108.0</p></td><td><p>114.5</p></td><td><p>114.9</p></td><td><p>106.9</p></td></tr><tr><td><p>zlib 4</p></td><td><p>5.8</p></td><td><p>20.9</p></td><td><p>32.2</p></td><td><p>39.8</p></td><td><p>54.8</p></td><td><p>74.9</p></td><td><p>93.3</p></td><td><p>97.5</p></td><td><p>96.1</p></td><td><p>90.7</p></td></tr><tr><td><p>zlib 5</p></td><td><p>5.8</p></td><td><p>20.6</p></td><td><p>31.4</p></td><td><p>38.3</p></td><td><p>51.6</p></td><td><p>68.4</p></td><td><p>82.0</p></td><td><p>81.3</p></td><td><p>73.2</p></td><td><p>71.6</p></td></tr><tr><td><p>zlib 6</p></td><td><p>5.8</p></td><td><p>20.6</p></td><td><p>31.2</p></td><td><p>37.9</p></td><td><p>50.6</p></td><td><p>64.0</p></td><td><p>73.7</p></td><td><p>70.2</p></td><td><p>57.5</p></td><td><p>58.0</p></td></tr><tr><td><p>zlib 7</p></td><td><p>5.8</p></td><td><p>20.5</p></td><td><p>31.0</p></td><td><p>37.4</p></td><td><p>49.6</p></td><td><p>60.8</p></td><td><p>67.4</p></td><td><p>64.6</p></td><td><p>51.0</p></td><td><p>52.0</p></td></tr><tr><td><p>zlib 8</p></td><td><p>5.8</p></td><td><p>20.5</p></td><td><p>31.0</p></td><td><p>37.2</p></td><td><p>48.8</p></td><td><p>53.2</p></td><td><p>56.6</p></td><td><p>56.5</p></td><td><p>41.6</p></td><td><p>43.1</p></td></tr><tr><td><p>zlib 9</p></td><td><p>5.8</p></td><td><p>20.6</p></td><td><p>30.8</p></td><td><p>37.3</p></td><td><p>48.6</p></td><td><p>51.7</p></td><td><p>56.6</p></td><td><p>54.2</p></td><td><p>40.4</p></td><td><p>41.9</p></td></tr><tr><td><p>brotli 1</p></td><td><p>3.4</p></td><td><p>12.8</p></td><td><p>20.4</p></td><td><p>25.9</p></td><td><p>37.8</p></td><td><p>57.3</p></td><td><p>80.0</p></td><td><p>94.1</p></td><td><p>105.8</p></td><td><p>91.3</p></td></tr><tr><td><p>brotli 2</p></td><td><p>3.4</p></td><td><p>12.4</p></td><td><p>19.5</p></td><td><p>24.4</p></td><td><p>35.2</p></td><td><p>52.3</p></td><td><p>71.2</p></td><td><p>82.0</p></td><td><p>89.0</p></td><td><p>78.8</p></td></tr><tr><td><p>brotli 3</p></td><td><p>3.4</p></td><td><p>12.3</p></td><td><p>19.0</p></td><td><p>23.7</p></td><td><p>34.0</p></td><td><p>49.8</p></td><td><p>67.4</p></td><td><p>76.3</p></td><td><p>81.5</p></td><td><p>73.0</p></td></tr><tr><td><p>brotli 4</p></td><td><p>2.0</p></td><td><p>7.6</p></td><td><p>11.9</p></td><td><p>15.2</p></td><td><p>22.2</p></td><td><p>33.1</p></td><td><p>44.7</p></td><td><p>51.9</p></td><td><p>58.5</p></td><td><p>51.0</p></td></tr><tr><td><p>brotli 5</p></td><td><p>2.0</p></td><td><p>5.2</p></td><td><p>8.0</p></td><td><p>10.3</p></td><td><p>15.0</p></td><td><p>22.0</p></td><td><p>29.7</p></td><td><p>33.3</p></td><td><p>32.8</p></td><td><p>30.3</p></td></tr><tr><td><p>brotli 6</p></td><td><p>1.8</p></td><td><p>3.8</p></td><td><p>5.5</p></td><td><p>7.0</p></td><td><p>10.5</p></td><td><p>16.3</p></td><td><p>23.5</p></td><td><p>28.6</p></td><td><p>28.4</p></td><td><p>25.6</p></td></tr><tr><td><p>brotli 7</p></td><td><p>1.5</p></td><td><p>2.3</p></td><td><p>3.1</p></td><td><p>3.7</p></td><td><p>4.9</p></td><td><p>7.2</p></td><td><p>10.7</p></td><td><p>15.5</p></td><td><p>19.6</p></td><td><p>16.2</p></td></tr><tr><td><p>brotli 8</p></td><td><p>1.4</p></td><td><p>2.3</p></td><td><p>2.7</p></td><td><p>3.1</p></td><td><p>4.0</p></td><td><p>5.3</p></td><td><p>7.1</p></td><td><p>10.6</p></td><td><p>15.1</p></td><td><p>12.2</p></td></tr><tr><td><p>brotli 9</p></td><td><p>1.3</p></td><td><p>2.1</p></td><td><p>2.4</p></td><td><p>2.8</p></td><td><p>3.4</p></td><td><p>4.3</p></td><td><p>5.5</p></td><td><p>7.0</p></td><td><p>10.6</p></td><td><p>8.8</p></td></tr><tr><td><p>brotli 10</p></td><td><p>0.2</p></td><td><p>0.4</p></td><td><p>0.4</p></td><td><p>0.5</p></td><td><p>0.5</p></td><td><p>0.6</p></td><td><p>0.6</p></td><td><p>0.6</p></td><td><p>0.5</p></td><td><p>0.5</p></td></tr></table><p>On average for all files, we can see that Brotli at quality level 4 is slightly faster than zlib at quality level 8 (and 9) while having comparable compression ratio. However that is misleading. Most files are smaller than 64KB, and if we look only at those files then Brotli 4 is actually 1.48X slower than zlib level 8!</p>
    <div>
      <h3>Connection speedup</h3>
      <a href="#connection-speedup">
        
      </a>
    </div>
    <p>For on-the-fly compression, the most important question is how much time to invest in compression to make data transfer faster. Because increasing compression quality only gives incremental improvement over a given level, we need the added compressed bytes to outweigh the additional time spent compressing those bytes.</p><p>Again, CloudFlare uses compression quality 8 with zlib, so that’s our baseline. For each quality setting of Brotli starting at 4 (which is somewhat comparable to zlib 8 in terms of both time and compression ratio), we compute the added compression speed as: ((total size for zlib 8) - (total size after compression with Brotli))/((total time for Brotli)-(total time for zlib 8)).</p><p>The results are reported in MB/s. Negative numbers indicate lower compression ratio.</p><table><tr><td><p>
</p></td><td><p><b>[20,
1024)</b></p></td><td><p><b>[1024,
2048)</b></p></td><td><p><b>[2048,
3072)</b></p></td><td><p><b>[3072,
4096)</b></p></td><td><p><b>[4096,
8192)</b></p></td><td><p><b>[8192,
16384)</b></p></td><td><p><b>[16384,
32768)</b></p></td><td><p><b>[32768,
65536)</b></p></td><td><p><b>[65536,
+∞)</b></p></td><td><p><b>All files</b></p></td></tr><tr><td><p>brotli 4</p></td><td><p>0.33</p></td><td><p>0.56</p></td><td><p>0.52</p></td><td><p>0.47</p></td><td><p>0.42</p></td><td><p>0.44</p></td><td><p>-0.24</p></td><td><p>-2.86</p></td><td><p>0.02</p></td><td><p>0.00</p></td></tr><tr><td><p>brotli 5</p></td><td><p>0.44</p></td><td><p>0.55</p></td><td><p>0.60</p></td><td><p>0.61</p></td><td><p>0.71</p></td><td><p>0.97</p></td><td><p>1.01</p></td><td><p>1.08</p></td><td><p>2.24</p></td><td><p>1.58</p></td></tr><tr><td><p>brotli 6</p></td><td><p>0.36</p></td><td><p>0.36</p></td><td><p>0.37</p></td><td><p>0.37</p></td><td><p>0.45</p></td><td><p>0.63</p></td><td><p>0.69</p></td><td><p>0.86</p></td><td><p>1.52</p></td><td><p>1.12</p></td></tr><tr><td><p>brotli 7</p></td><td><p>0.28</p></td><td><p>0.20</p></td><td><p>0.19</p></td><td><p>0.18</p></td><td><p>0.19</p></td><td><p>0.23</p></td><td><p>0.24</p></td><td><p>0.34</p></td><td><p>0.72</p></td><td><p>0.52</p></td></tr><tr><td><p>brotli 8</p></td><td><p>0.26</p></td><td><p>0.20</p></td><td><p>0.17</p></td><td><p>0.15</p></td><td><p>0.15</p></td><td><p>0.17</p></td><td><p>0.15</p></td><td><p>0.21</p></td><td><p>0.48</p></td><td><p>0.35</p></td></tr><tr><td><p>brotli 9</p></td><td><p>0.25</p></td><td><p>0.19</p></td><td><p>0.15</p></td><td><p>0.13</p></td><td><p>0.13</p></td><td><p>0.13</p></td><td><p>0.11</p></td><td><p>0.13</p></td><td><p>0.30</p></td><td><p>0.24</p></td></tr><tr><td><p>brotli 10</p></td><td><p>0.03</p></td><td><p>0.04</p></td><td><p>0.04</p></td><td><p>0.04</p></td><td><p>0.03</p></td><td><p>0.03</p></td><td><p>0.02</p></td><td><p>0.02</p></td><td><p>0.02</p></td><td><p>0.02</p></td></tr></table><p>Those numbers are quite low, due to the slow speed of Brotli. To get any speedup we really want to see those numbers being greater than the connection speed. It seems that for files greater than 64KB, Brotli at quality setting 5 can speed up slow connections.</p><p>Keep in mind, that on a real server, compression is only one of many tasks that share the CPU, and compression speeds would be slower there.</p>
    <div>
      <h3>Conclusions</h3>
      <a href="#conclusions">
        
      </a>
    </div>
    <p>The current state of Brotli gives us some mixed impressions. There is no yes/no answer to the question "Is Brotli better than gzip?". It definitely looks like a big win for static content compression, but on the web where the content is dynamic we also need to consider on-the-fly compression.</p><p>The way I see it, Brotli already has an advantage over zlib for large files (larger than 64KB) on slow connections. However, those constitute only 20% of our sampled dataset (and 80% of the total size).</p><p>Our Brotli module has a minimal size setting for Brotli compression that allows us to use gzip for smaller files and Brotli only for large ones.</p><p>It is important to remember that zlib has the advantage of being the optimization target for years by the entire web community, while Brotli is the development effort of a small but capable and talented team. There is no doubt that the current implementation will only improve with time.</p> ]]></content:encoded>
            <category><![CDATA[Compression]]></category>
            <category><![CDATA[Tech Talks]]></category>
            <category><![CDATA[Optimization]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Deep Dive]]></category>
            <guid isPermaLink="false">499uvv9lvLrJ47pbY7HZTq</guid>
            <dc:creator>Vlad Krasnov</dc:creator>
        </item>
        <item>
            <title><![CDATA[Simple Helix chooses CloudFlare to ignite white-hot Magento performance]]></title>
            <link>https://blog.cloudflare.com/simple-helix-chooses-cloudflare-to-ignite-white-hot-magento-performance/</link>
            <pubDate>Tue, 01 Sep 2015 17:04:32 GMT</pubDate>
            <description><![CDATA[ Some months ago, we made a big bet on partnering with CloudFlare for performance improvements and website security for our Magento hosting customers. Customer experience is core to our business and relying on another company is a major deal.  ]]></description>
            <content:encoded><![CDATA[ <p><i>Today’s guest blogger is George Cagle. George is a system administrator at Simple Helix, a CloudFlare partner.</i></p><p>Some months ago, we made a big bet on partnering with CloudFlare for performance improvements and website security for our Magento hosting customers. Customer experience is core to our business and relying on another company is a major deal. CloudFlare is now included in Default–On mode for select Simple Helix hosting plans and can be added to any existing plan. The results have been great and we wanted to share a couple successes with the rest of the CloudFlare community.</p>
    <div>
      <h3>Testing the waters</h3>
      <a href="#testing-the-waters">
        
      </a>
    </div>
    <p>The first thing one notices after melding their site with the worldwide CloudFlare <a href="https://www.cloudflare.com/features-cdn">CDN network</a> is just how fast a website becomes. In Simple Helix’s testing, we found that proper CloudFlare implementation can yield 100% speed increases, and an even faster 143% speed increase when paired with the <a href="https://www.cloudflare.com/railgun">Railgun™</a> web optimizer for dynamic content.</p><p>Adding CloudFlare will certainly improve performance, but it can also significantly improve security through the <a href="https://www.cloudflare.com/waf">Web Application Firewall</a> <a href="https://www.cloudflare.com/waf"></a>feature. The security benefits of having the CloudFlare service can be seen after just the first few days of adoption as outlined below:</p><p>Total number of threats mitigated by CloudFlare in a week</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7NsKnptazEMY2zHX8ibJ5/e868d404812e487313adbd0d614d4f97/image_0.png" />
            
            </figure><p>The results provided insight: "CloudFlare helped break down the problem in an elegant way that made threat assessments much easier for our customers to digest and make well-reasoned decisions based on the information presented" said Brian Rorex, systems administrator at Simple Helix. CloudFlare protects sites from some of the most common maladies that plague the modern Internet like overly-aggressive crawler bots, botnet attacks, and DDoS attacks.</p>
    <div>
      <h3>Getting results</h3>
      <a href="#getting-results">
        
      </a>
    </div>
    <p>Given our happiness with the performance of the CloudFlare service, we have chosen it to respond to some unique performance challenges of our customers with much success. One such Simple Helix client is a popular fashion accessory company that let us know that they were launching a high-traffic media campaign that would increase traffic significantly for several days. We responded by putting them on CloudFlare as their first step in bolstering the company's infrastructure in preparation for the big day. When the tweet finally hit the internet and the traffic ramped up, CloudFlare and the servers hardly broke a sweat, reducing the effective bandwidth usage at the origin by almost 70%.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1hvNetMi85ACNUto4fKWDw/b050d5ce7d9bd40755e40bc8e948608f/image_1.png" />
            
            </figure><p>Another Simple Helix customer with a popular apparel store routinely saw 300-500% spikes in traffic during regular sales events. Nesting their web servers behind the CloudFlare CDN evened out the traffic to an almost flat bandwidth usage graph and provided an 83.4% bandwidth savings at the origin server.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3qMTtUyEGJ79ObmlgFcyVm/caf912f0fd083ec81f96b2e017ebfc6c/image_2.png" />
            
            </figure><p>Simple Helix is one of the leading hosting providers serving the Magento e-commerce market with over 100,000 domains. Our customers vary from mom and pop shops to internationally recognized brands. A young, rapidly growing company, Simple Helix is constantly on the look-out for new technology that will improve the quality of service for their customers and set them apart from the pack.</p><p>We’re excited to make CloudFlare a standard part of setup at Simple Helix. This means that our customers get the performance and security benefits of CloudFlare without any additional work. If you currently run an eCommerce store that could take advantage of what we have to offer, please contact <a href="https://manage.simplehelix.com/submitticket.php?step=2&amp;deptid=2&amp;__hstc=86869831.707593530589295e70264e08352f1582.1435944664030.1439996175366.1439999612847.67&amp;__hssc=86869831.1.1439999612847&amp;__hsfp=2729578089">the Simple Helix team</a> to find out which plan is right for you.</p> ]]></content:encoded>
            <category><![CDATA[Railgun]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Compression]]></category>
            <category><![CDATA[WAF]]></category>
            <category><![CDATA[Optimization]]></category>
            <category><![CDATA[Partners]]></category>
            <guid isPermaLink="false">7moWo2v1ykcY0tAqOmGxP5</guid>
            <dc:creator>Guest Author</dc:creator>
        </item>
        <item>
            <title><![CDATA[Railgun v5 has landed: better, faster, lighter]]></title>
            <link>https://blog.cloudflare.com/railgun-v5-has-landed/</link>
            <pubDate>Mon, 31 Aug 2015 22:31:51 GMT</pubDate>
            <description><![CDATA[ Three years ago we launched Railgun, CloudFlare's origin network optimizer. Railgun allows us to cache the uncacheable to accelerate the connection between CloudFlare and our customers' origin servers.  ]]></description>
            <content:encoded><![CDATA[ <p>Three years ago we launched <a href="https://www.cloudflare.com/railgun">Railgun</a>, CloudFlare's origin network optimizer. Railgun allows us to <a href="/cacheing-the-uncacheable-cloudflares-railgun-73454/">cache the uncacheable</a> to accelerate the connection between CloudFlare and our customers' origin servers. That brings the benefit of a CDN to even dynamic content with no need for 'fast purging' or other tricks. With Railgun even dynamic, ever-changing pages benefit from caching.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4tGSSOyLcACP5qioD3Ssol/ef4edbd58835782afb34061a085b3e2f/2300190277_360853ae0d_z.jpg" />
            
            </figure><p><a href="https://creativecommons.org/licenses/by/2.0/">CC BY 2.0</a> <a href="https://www.flickr.com/photos/thatguyfromcchs08/2300190277/in/photolist-4vg5r4-at4qz-oMjmcq-wnQ3p-dTxa4e-5otM4s-4dqdHU-9saDQZ-cu6Fc-4Pmroo-sBSNuJ-9dehck-ngDfDT-nUu6YR-5UgoU2-6jneZb-3a4fyp-7YsAXD-bZtZaN-a8M5vq-bEKeA8-GhFjt-roySGp-i14jSw-cAPypE-87LoH-5Uy3NF-6M6Pse-rVtN9-cfdeos-bfMtZP-u3Zw2b-8N97Uu-jamkM-7x2csx-7RyBfo-ibekvp-ibepqt-a6B8Ss-je9ZR9-toS7kJ-9tNfaF-5gt9Wg-6UbNjh-aCPkJ1-RvxwP-8qkQfq-9ndbXy-7HqKEz-4me8vi">image</a> by <a href="https://www.flickr.com/photos/thatguyfromcchs08/">Nathan E Photography</a></p><p>Over those three years Railgun has been deployed widely by our customers to accelerate the delivery of their web sites and <a href="/railgun-gives-our-ecommerce-sites-the-edge/">lower</a> their bandwidth costs.</p><p>Today we're announcing the availability of Railgun v5 with a number of significant improvements:</p>
    <div>
      <h4>We've substantially reduced memory utilization and CPU requirements</h4>
      <a href="#weve-substantially-reduced-memory-utilization-and-cpu-requirements">
        
      </a>
    </div>
    <p>Railgun performs delta compression on every request/response requiring CPU (to perform the compression) and memory (to keep a cache of pages to delta against). Version 5 has undergone extensive optimization based on the performance of Railgun on large web sites and at hosting providers. Version 5 requires much less memory and lower CPU.</p>
    <div>
      <h4>A new, lighter weight, faster wire protocol</h4>
      <a href="#a-new-lighter-weight-faster-wire-protocol">
        
      </a>
    </div>
    <p>The original Railgun wire protocol that transfer requests and compressed responses between the customer server and CloudFlare's infrastructure has been completely replaced with a new, lighter-weight completely binary protocol that is faster and uses less bandwidth.</p>
    <div>
      <h4>An extra layer of compression</h4>
      <a href="#an-extra-layer-of-compression">
        
      </a>
    </div>
    <p>We noticed in real-world tests that although delta compression provided <a href="/railgun-in-the-real-world/">incredible compression</a> and faster page load times that it was possible to squeeze out even greater compression by performing traditional non-delta compression in addition to the delta compression. This is now standard and all content is compressed yielding an extra 10-15% compression.</p>
    <div>
      <h4>Streaming mode for large downloads</h4>
      <a href="#streaming-mode-for-large-downloads">
        
      </a>
    </div>
    <p>Large downloads are not delta compressed for performance reasons (the benefits of the delta compression are outweighed by the cost of compressing very large pages). To ensure that large pages are downloaded as quickly as possible, Railgun v5 provides an automatic streaming mode where the page is streamed from the origin server across the Internet to CloudFlare and on to the end web browser. This substantially reduces the time to download very large pages through Railgun.</p>
    <div>
      <h4>Better utilization of origin web server connections</h4>
      <a href="#better-utilization-of-origin-web-server-connections">
        
      </a>
    </div>
    <p>Railgun's management of the connection between Railgun and the customer origin server has been improved to pool connections and make best use of HTTP keep-alives. This reduces the load on the origin server and improves performance as connections are efficiently reused resulting in lower latency.</p>
    <div>
      <h4>Improved cryptographic infrastructure</h4>
      <a href="#improved-cryptographic-infrastructure">
        
      </a>
    </div>
    <p>CloudFlare has been moving all communication between servers to encrypted connections. Railgun has always used a TLS connection between CloudFlare and the customer server even if the requests being passed were HTTP and not HTTPS. With version 5 we've switched Railgun to use our new CA for greater security. The connection between CloudFlare and the customer's Railgun is <a href="/how-to-build-your-own-public-key-infrastructure/">secured with certificates in both directions</a> that are verified against the CloudFlare CA.</p>
    <div>
      <h4>Optimized partners</h4>
      <a href="#optimized-partners">
        
      </a>
    </div>
    <p>CloudFlare Optimized Partners in particular can benefit from the lower resource usage of Railgun version 5. A2 Hosting, an Optimized Partner and Railgun Beta participant, reported increased compression rates using version 5. Also new for partners is the ability to assign subdomains to a Railgun. Upgrading to the latest version, or installing Railgun for the first time, only takes a few minutes (<a href="https://www.cloudflare.com/static/media/downloads/optimized-partner-rg-quick.pdf">Railgun Quick Start Guide</a> for Optimized Partners). Railgun is perfect for ecommerce sites as well as news sites and popular blogs.</p>
    <div>
      <h4>Install or upgrade today</h4>
      <a href="#install-or-upgrade-today">
        
      </a>
    </div>
    <p>Railgun is available as part of CloudFlare's Business and Enterprise plans or from an Optimized Partner. <a href="https://www.cloudflare.com/resources-downloads#railgun">Installation instructions for Railgun</a> are available on CloudFlare's resources and downloads page. We recommend installing from <a href="https://pkg.cloudflare.com">CloudFlare's package repository</a>, which makes it easy to keep Railgun up-to-date. This release also sees Railgun available on Red Hat Enterprise Linux (RHEL) and CentOS 7. Railgun v5's configuration is completely compatible with version 4 and customers can simply replace the Railgun binary and restart to use version 5 and immediately see the benefits.</p> ]]></content:encoded>
            <category><![CDATA[Railgun]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Compression]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Speed]]></category>
            <category><![CDATA[Partners]]></category>
            <guid isPermaLink="false">65AObM1ggWI7puGROpTutw</guid>
            <dc:creator>John Graham-Cumming</dc:creator>
        </item>
        <item>
            <title><![CDATA[Fighting Cancer: The Unexpected Benefit Of Open Sourcing Our Code]]></title>
            <link>https://blog.cloudflare.com/cloudflare-fights-cancer/</link>
            <pubDate>Wed, 08 Jul 2015 13:28:00 GMT</pubDate>
            <description><![CDATA[ Recently I was contacted by Dr. Igor Kozin from The Institute of Cancer Research in London. He asked about the optimal way to compile CloudFlare's open source fork of zlib. ]]></description>
            <content:encoded><![CDATA[ <p>Recently I was contacted by Dr. Igor Kozin from <a href="http://www.icr.ac.uk/">The Institute of Cancer Research</a> in London. He asked about the optimal way to compile CloudFlare's open source fork of <a href="https://github.com/cloudflare/zlib">zlib</a>. It turns out that zlib is widely used to compress the <a href="https://en.wikipedia.org/wiki/SAMtools">SAM/BAM</a> files that are used for DNA sequencing. And it turns out our zlib fork is the best open source solution for that file <a href="http://www.htslib.org/benchmarks/zlib.html">format</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/Vz94lbV5IqYHgXdHKNWW7/23658931cdf1dd3ecb2fc73fd74d9c0b/2653833040_644faff824_z.jpg" />
            
            </figure><p><a href="https://creativecommons.org/licenses/by-sa/2.0/">CC BY-SA 2.0</a> <a href="https://www.flickr.com/photos/shaury/2653833040/in/photolist-53vA75-53rmFB-7hFuix-bpRbbf-hY8WDp-o3PyM-7KZrSc-a8Yj5r-66zTgT-6H5EcX-83C9AP-4i7Wsj-4i3QFK-bvHJVB-4i3QLV-i49oXB-8xqd9g-buojvP-hy8GiB-5dopRY-2xdo6Y-nGM3PK-nEJrfL-nqhpNL-4gxWpM-6aV2Yg-bgKpqv-b8T29n-5iijjc-pwvN8L-aA67rU-aA3r6R-aA67qW-aA3r7c-nEJpZE-63xBT-uKbHEa-oqrPhv-hWkGhN-4i3QQt-yRRDS-sgiRUL-sxJ9os-sxJ8Ky-sgj7j9-4i3QPa-svAPTm-4i7WBY-4i3QRD-4i7Wtm">image</a> by <a href="https://www.flickr.com/photos/shaury/">Shaury Nash</a></p><p>The files used for this kind of research reach hundreds of gigabytes and every time they are compressed and decompressed with our library many important seconds are saved, bringing the cure for cancer that much closer. At least that's what I am going to tell myself when I go to bed.</p><p>This made me realize that the benefits of open source go much farther than one can imagine, and you never know where a piece of code may end up. Open sourcing makes sophisticated algorithms and software accessible to individuals and organizations that would not have the resources to develop them on their own, or the money pay for a proprietary solution.</p><p>It also made me wonder exactly what we did to zlib that makes it stand out from other zlib forks.</p>
    <div>
      <h3>Recap</h3>
      <a href="#recap">
        
      </a>
    </div>
    <p>Zlib is a compression library that supports two formats: deflate and gzip. Both formats use the same algorithm also called <a href="https://en.wikipedia.org/wiki/DEFLATE">DEFLATE</a>, but with different headers and checksum functions. The deflate algorithm is described <a href="/improving-compression-with-preset-deflate-dictionary/">here</a>.</p><p>Both formats are supported by the absolute majority of web browsers, and we at CloudFlare compress all text content on the fly using the gzip format. Moreover DEFLATE is also used by the PNG file format, and our fork of zlib also accelerates our image optimization engine <a href="/introducing-polish-automatic-image-optimizati/">Polish</a>. You can find the optimized fork of pngcrush <a href="https://github.com/cloudflare/pngcrush">here</a>.</p><p>Given the amount of traffic we must handle, compression optimization really makes sense for us. Therefore we included several improvements over the default implementation.</p><p>First of all it is important to understand the current state of zlib. It is a very old library, one of the oldest that is still used as is to this day. It is so old it was written in K&amp;R C. It is so old USB was not invented yet. It is so old that DOS was still a thing. It is so old (insert your favorite so old joke here). More precisely it dates back to 1995. Back to the days 16-bit computers with 64KB addressable space were still in use.</p><p>Still it represents one of the best pieces of code ever written, and even modernizing it gives only modest performance boost. Which shows the great skill of its authors and the long way compilers have come since 1995.</p><p>Below is a list of some of the improvements in our fork of zlib. This work was done by me, my colleague Shuxin Yang, and also includes improvements from other sources.</p><ul><li><p><code>uint64_t</code> as the standard type - the default fork used 16-bit types.</p></li><li><p>Using an improved hash function - we use the <a href="https://tools.ietf.org/html/rfc3385">iSCSI CRC32</a> function as the hash function in our zlib. This specific function is implemented as a hardware instruction on Intel processors. It has very fast performance and better collision properties.</p></li><li><p>Search for matches of at least 4 bytes, instead the 3 bytes the format suggests. This leads to fewer hash collisions, and less effort wasted on insignificant matches. It also improves the compression rate a little bit for the majority of cases (but not all).</p></li><li><p>Using SIMD instructions for window rolling.</p></li><li><p>Using the hardware carry-less multiplication instruction <code>PLCMULQDQ</code> for the CRC32 checksum.</p></li><li><p>Optimized longest-match function. This is the most performance demanding function in the library. It is responsible for finding the (length, distance) matches in the current window.</p></li></ul><p>In addition, we have an experimental branch that implements an improved version of the linked list used in zlib. It has much better performance for compression levels 6 to 9, while retaining the same compression ratio. You can find the experimental branch <a href="https://github.com/cloudflare/zlib/tree/experimental">here</a>.</p>
    <div>
      <h3>Benchmarking</h3>
      <a href="#benchmarking">
        
      </a>
    </div>
    <p>You can find independent benchmarks of our library <a href="https://www.snellman.net/blog/archive/2015-06-05-updated-zlib-benchmarks/">here</a> and <a href="http://www.htslib.org/benchmarks/zlib.html">here</a>. In addition, I performed some in-house benchmarking, and put the results here for your convenience.</p><p>All the benchmarks were performed on an i5-4278U CPU. The compression was performed from and to a ramdisk. All libraries were compiled with gcc version 4.8.4 with the compilation flags: "-O3 -march=native".</p><p>I tested the performance of the <a href="https://github.com/madler/zlib">main zlib fork</a>, optimized implementation by <a href="https://github.com/jtkukunas/zlib">Intel</a>, our own <a href="https://github.com/cloudflare/zlib">main branch</a>, and our <a href="https://github.com/cloudflare/zlib/tree/experimental">experimental branch</a>.</p><p>Four data sets were used for the benchmarks. The <a href="http://corpus.canterbury.ac.nz/resources/calgary.tar.gz">Calgary corpus</a>, the <a href="http://corpus.canterbury.ac.nz/resources/cantrbry.tar.gz">Canterbury corpus</a>, the <a href="http://corpus.canterbury.ac.nz/resources/large.tar.gz">Large Canterbury corpus</a> and the <a href="http://sun.aei.polsl.pl/~sdeor/corpus/silesia.zip">Silesia corpus</a>.</p><p><b>Calgary corpus</b></p><p>Performance:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1swjNfobp1N11Oj2GBmdA3/d3190f3208c1b31365129e71b13334bc/calgary-1.png" />
            
            </figure><p>Compression rates:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6ZIne0q7jXVMUHZZUpW5hx/5275c8abea0aa98fe9334fb1ece3a403/calgary_c-1.png" />
            
            </figure><p>For this benchmark, Intel only outperforms our implementation for level 1, but at the cost of 1.39X larger files. This difference is far greater than even the difference between levels 1 and 9, and should probably be regarded as a different compression level. CloudFlare is faster on all other levels, and outperforms significantly for levels 6 to 9. The experimental implementation is even faster for those levels.</p><p><b>Canterbury corpus</b></p><p>Performance:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/Sj9CVjFYbS2CGZDJYoNhA/a198c2e7cd82d2dee27b37f1c2c777c7/canterbry.png" />
            
            </figure><p>Compression rates:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2lVTEFn90wmzkWC05IZbq4/4b6cfc78fa09972da920f859e9f2c846/canterbry_c.png" />
            
            </figure><p>Here we see a similar situation. Intel at level 1 gets 1.44X larger files. CloudFlare is faster for levels 2 to 9. On level 9, the experimental branch outperforms the reference implementation by 2X.</p><p><b>Large corpus</b></p><p>Performance:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1ZRBEXqOI6FpT900xL3qrC/68e11482156b5460fc9175dd7fa3afc9/large.png" />
            
            </figure><p>Compression rates:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6PqDEMmdGUstoT8b5jFy9h/b0c68295d26a32afdea95f24ceb6b153/large_c.png" />
            
            </figure><p>This time Intel is slightly faster for levels 5 and 6 than the CloudFlare implementation. The experimental CloudFlare implementation is faster still on level 6. The compression rate for Intel level 1 is 1.58 lower than CloudFlare. On level 9, the experimental fork is 7.5X(!) faster than reference.</p><p><b>Silesia corpus</b></p><p>Performance:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5mzcc6T1U4rNNsQF9tPBzK/bd84eb1387a5c52506d3d9205999ba4e/seli.png" />
            
            </figure><p>Compression rates:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/359sqp7ZUTycFlJVDoebdc/d77c055ad0e57374c7addf8a622e9a57/seli_c-1.png" />
            
            </figure><p>Here again, CloudFlare is the fastest on levels 2 to 9. On level 9 the difference in speed between the experimental fork and the reference fork is 2.44X.</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>As evident from the benchmarks, the CloudFlare implementation outperforms the competition in the vast majority of settings. We put great effort in making it as fast as possible on our servers.</p><p>If you intend to use our library, you should check for yourself if it delivers the best balance of performance and compression for your dataset. As between different file format and sizes performance can vary.</p><p>And if you like open source software, don't forget to give back to the community, by contributing your own code!</p> ]]></content:encoded>
            <category><![CDATA[Open Source]]></category>
            <category><![CDATA[Compression]]></category>
            <category><![CDATA[Optimization]]></category>
            <guid isPermaLink="false">42jiDxmNfkq3Cch2DFVty0</guid>
            <dc:creator>Vlad Krasnov</dc:creator>
        </item>
        <item>
            <title><![CDATA[Improving compression with a preset DEFLATE dictionary]]></title>
            <link>https://blog.cloudflare.com/improving-compression-with-preset-deflate-dictionary/</link>
            <pubDate>Mon, 30 Mar 2015 09:21:21 GMT</pubDate>
            <description><![CDATA[ A few years ago Google made a proposal for a new HTTP compression method, called SDCH (SanDwiCH). The idea behind the method is to create a dictionary of long strings that appear throughout many pages of the same domain (or popular search results). ]]></description>
            <content:encoded><![CDATA[ <p>A few years ago Google made a proposal for a new HTTP compression method, called SDCH (SanDwiCH). The idea behind the method is to create a dictionary of long strings that appear throughout many pages of the same domain (or popular search results). The compression is then simply searching for the appearance of the long strings in a dictionary and replacing them with references to the aforementioned dictionary. Afterwards the output is further compressed with <a href="https://en.wikipedia.org/wiki/DEFLATE">DEFLATE</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6DlU37lpBE25TGdgfyM43y/495a90025471558e0ccb4978a8043ee1/15585984911_6a6bff8f30_z.jpg" />
            
            </figure><p><a href="https://creativecommons.org/licenses/by-sa/2.0/"><sub>CC BY SA 2.0</sub></a><sub> </sub><a href="https://www.flickr.com/photos/quinnanya/15585984911/in/photolist-pKhfiM-52LCYm-abTcAV-9eBXNP-9EQsPE-k2zt7W-3wGhDE-8FHYSs-4H94jD-8FFL2G-4U8deD-8FHZ7h-9zxQ3Q-96AfdV-8F5h1w-8FjCSW-8FeYBM-RbMqS-8FgTbT-8FjUvd-8FgF7T-8FjM4E-8Fgwtt-8Fjvxf-8FjtpU-8FjpUm-8Fjn3u-8Fjiv9-8Ff5ex-8Fid39-8FeTN6-8Fi3KW-8F29dP-8FhaH2-8FkcNm-8Fk8Kw-6DASnR-3T6V24-7PMw1h-7PJhZ8-uMSAt-cUzN8q-4fjD2P-eRviNL-BZg6b-qz5e4C-cqkfc-4szYvT-u1RCZ-ctvmWW"><sub>image</sub></a><sub> by </sub><a href="https://www.flickr.com/photos/quinnanya/"><sub>Quinn Dombrowski</sub></a></p><p>With the right dictionary for the right page the savings can be spectacular, even 70% smaller than gzip alone. In theory, a whole file can be replaced by a single token.</p><p>The drawbacks of the method are twofold: first - the dictionary that is created is fairly large and must be distributed as a separate file, in fact the dictionary is often larger than the individual pages it compresses; second - the dictionary is usually absolutely useless for another set of pages.</p><p>For large domains that are visited repeatedly the advantage is huge: at a cost of single dictionary download, all the following page views can be compressed with much higher efficiency. Currently we aware of Google and LinkedIn compressing content with SDCH.</p>
    <div>
      <h3>SDCH for millions of domains</h3>
      <a href="#sdch-for-millions-of-domains">
        
      </a>
    </div>
    <p>Here at CloudFlare our task is to support millions of domains, which have little in common, and creating a single SDCH dictionary is very difficult. Nevertheless better compression is important, because it produces smaller payloads, which result in content being delivered faster to our clients. That is why we set out to find that little something that is common to all the pages and to see if we could compress them further.</p><p>Besides SDCH (which is only supported by the Chrome browser), the common compression methods over HTTP are gzip and DEFLATE. Some do not know it but the compression they perform is identical. The two formats differ in the content headers they use, with gzip having slightly larger headers, and the error detection function - gzip uses CRC32, whereas DEFLATE uses Adler32.</p><p>Usually the servers opt to compress with gzip, however its cousin DEFLATE supports a neat feature called "Preset Dictionary". This dictionary is not like the dictionary used by SDCH, in fact it is not a real dictionary. To understand how this "dictionary" can be used to our advantage, it is important to understand how the DEFLATE algorithm works.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/15p0MSMrIz6i5jMxj6DmrP/191d330fe298e473be033432d5136dff/5510506796_dff8c07b64_z.jpg" />
            
            </figure><p><sub></sub><a href="https://creativecommons.org/licenses/by/2.0/"><sub>CC BY 2.0</sub></a><sub> </sub><a href="https://www.flickr.com/photos/crdot/5510506796/in/photolist-9oWMFS-5ZetrG-aB6EXd-7BFq6C-8bfuDz-8a7u13-8bfv1z-89PDmK-8a7uvj-8a4fbn-89STbj-5U3gbF-8a4fPH-8a7qPA-89PADp-7BFqLU-8a4fha-7A9GMJ-89SQaN-8biMCh-89PASH-89PCRT-8biMuU-8biM8j-89PCgx-8a7unY-89SSf3-49Vg4c-89PzPn-8a4e4B-89PziH-8biMRU-8a7uHq-ngPPhQ-nkCZTe-7TQ4Yj-8a7qhy-8biLH7-8a7rCW-8a4fp4-76ixPX-8a4fiH-7BFqow-eijkuy-8a7uDY-8a4aat-89Pzv8-89SQdU-nkDdYH-89Pzsp"><sub>image</sub></a><sub> by </sub><a href="https://www.flickr.com/photos/crdot/"><sub>Caleb Roenigk</sub></a></p><p>The DEFLATE algorithm consists of two stages, first it performs the LZ77 algorithm, where it simply goes over the input, and replaces occurrences of previously encountered strings with (short) "directions" where the same string can be found in the previous input. The directions are a tuple of (length, distance), where distance tells how far back in the input the string was and length tells how many bytes were matched. The minimal length deflate will match is 3 (4 in the highly optimized implementation CloudFlare uses), the maximal length is 258, and the farthest distance back is 32KB.</p><p>This is an illustration of the LZ77 algorithm:</p><p>Input:</p>
<div><table><thead>
  <tr>
    <th><span>L</span></th>
    <th><span>i</span></th>
    <th><span>t</span></th>
    <th><span>t</span></th>
    <th><span>l</span></th>
    <th><span>e</span></th>
    <th></th>
    <th><span>b</span></th>
    <th><span>u</span></th>
    <th><span>n</span></th>
    <th><span>n</span></th>
    <th><span>y</span></th>
    <th></th>
    <th><span>F</span></th>
    <th><span>o</span></th>
    <th><span>o</span></th>
    <th></th>
    <th><span>F</span></th>
    <th><span>o</span></th>
    <th><span>o</span></th>
  </tr>
</thead>
<tbody>
  <tr>
    <td></td>
    <td><span>W</span></td>
    <td><span>e</span></td>
    <td><span>n</span></td>
    <td><span>t</span></td>
    <td></td>
    <td><span>h</span></td>
    <td><span>o</span></td>
    <td><span>p</span></td>
    <td><span>p</span></td>
    <td><span>i</span></td>
    <td><span>n</span></td>
    <td><span>g</span></td>
    <td></td>
    <td><span>t</span></td>
    <td><span>h</span></td>
    <td><span>r</span></td>
    <td><span>o</span></td>
    <td><span>u</span></td>
    <td><span>g</span></td>
  </tr>
  <tr>
    <td><span>h</span></td>
    <td></td>
    <td><span>t</span></td>
    <td><span>h</span></td>
    <td><span>e</span></td>
    <td></td>
    <td><span>f</span></td>
    <td><span>o</span></td>
    <td><span>r</span></td>
    <td><span>e</span></td>
    <td><span>s</span></td>
    <td><span>t</span></td>
    <td></td>
    <td><span>S</span></td>
    <td><span>c</span></td>
    <td><span>o</span></td>
    <td><span>o</span></td>
    <td><span>p</span></td>
    <td><span>i</span></td>
    <td><span>n</span></td>
  </tr>
  <tr>
    <td><span>g</span></td>
    <td></td>
    <td><span>u</span></td>
    <td><span>p</span></td>
    <td></td>
    <td><span>t</span></td>
    <td><span>h</span></td>
    <td><span>e</span></td>
    <td></td>
    <td><span>f</span></td>
    <td><span>i</span></td>
    <td><span>e</span></td>
    <td><span>l</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>m</span></td>
    <td><span>i</span></td>
    <td><span>c</span></td>
    <td><span>e</span></td>
    <td></td>
  </tr>
  <tr>
    <td><span>A</span></td>
    <td><span>n</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>b</span></td>
    <td><span>o</span></td>
    <td><span>p</span></td>
    <td><span>p</span></td>
    <td><span>i</span></td>
    <td><span>n</span></td>
    <td><span>g</span></td>
    <td></td>
    <td><span>t</span></td>
    <td><span>h</span></td>
    <td><span>e</span></td>
    <td><span>m</span></td>
    <td></td>
    <td><span>o</span></td>
    <td><span>n</span></td>
    <td></td>
  </tr>
  <tr>
    <td><span>t</span></td>
    <td><span>h</span></td>
    <td><span>e</span></td>
    <td></td>
    <td><span>h</span></td>
    <td><span>e</span></td>
    <td><span>a</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>D</span></td>
    <td><span>o</span></td>
    <td><span>w</span></td>
    <td><span>n</span></td>
    <td></td>
    <td><span>c</span></td>
    <td><span>a</span></td>
    <td><span>m</span></td>
    <td><span>e</span></td>
    <td></td>
    <td><span>t</span></td>
  </tr>
  <tr>
    <td><span>h</span></td>
    <td><span>e</span></td>
    <td></td>
    <td><span>G</span></td>
    <td><span>o</span></td>
    <td><span>o</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>F</span></td>
    <td><span>a</span></td>
    <td><span>i</span></td>
    <td><span>r</span></td>
    <td><span>y</span></td>
    <td><span>,</span></td>
    <td></td>
    <td><span>a</span></td>
    <td><span>n</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>s</span></td>
  </tr>
  <tr>
    <td><span>h</span></td>
    <td><span>e</span></td>
    <td></td>
    <td><span>s</span></td>
    <td><span>a</span></td>
    <td><span>i</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>"</span></td>
    <td><span>L</span></td>
    <td><span>i</span></td>
    <td><span>t</span></td>
    <td><span>t</span></td>
    <td><span>l</span></td>
    <td><span>e</span></td>
    <td></td>
    <td><span>b</span></td>
    <td><span>u</span></td>
    <td><span>n</span></td>
    <td><span>n</span></td>
  </tr>
  <tr>
    <td><span>y</span></td>
    <td></td>
    <td><span>F</span></td>
    <td><span>o</span></td>
    <td><span>o</span></td>
    <td></td>
    <td><span>F</span></td>
    <td><span>o</span></td>
    <td><span>o</span></td>
    <td></td>
    <td><span>I</span></td>
    <td></td>
    <td><span>d</span></td>
    <td><span>o</span></td>
    <td><span>n</span></td>
    <td><span>'</span></td>
    <td><span>t</span></td>
    <td></td>
    <td><span>w</span></td>
    <td><span>a</span></td>
  </tr>
  <tr>
    <td><span>n</span></td>
    <td><span>t</span></td>
    <td></td>
    <td><span>t</span></td>
    <td><span>o</span></td>
    <td></td>
    <td><span>s</span></td>
    <td><span>e</span></td>
    <td><span>e</span></td>
    <td></td>
    <td><span>y</span></td>
    <td><span>o</span></td>
    <td><span>u</span></td>
    <td></td>
    <td><span>S</span></td>
    <td><span>c</span></td>
    <td><span>o</span></td>
    <td><span>o</span></td>
    <td><span>p</span></td>
    <td><span>i</span></td>
  </tr>
  <tr>
    <td><span>n</span></td>
    <td><span>g</span></td>
    <td></td>
    <td><span>u</span></td>
    <td><span>p</span></td>
    <td></td>
    <td><span>t</span></td>
    <td><span>h</span></td>
    <td><span>e</span></td>
    <td></td>
    <td><span>f</span></td>
    <td><span>i</span></td>
    <td><span>e</span></td>
    <td><span>l</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>m</span></td>
    <td><span>i</span></td>
    <td><span>c</span></td>
    <td><span>e</span></td>
  </tr>
  <tr>
    <td></td>
    <td><span>A</span></td>
    <td><span>n</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>b</span></td>
    <td><span>o</span></td>
    <td><span>p</span></td>
    <td><span>p</span></td>
    <td><span>i</span></td>
    <td><span>n</span></td>
    <td><span>g</span></td>
    <td></td>
    <td><span>t</span></td>
    <td><span>h</span></td>
    <td><span>e</span></td>
    <td><span>m</span></td>
    <td></td>
    <td><span>o</span></td>
    <td><span>n</span></td>
  </tr>
  <tr>
    <td></td>
    <td><span>t</span></td>
    <td><span>h</span></td>
    <td><span>e</span></td>
    <td></td>
    <td><span>h</span></td>
    <td><span>e</span></td>
    <td><span>a</span></td>
    <td><span>d</span></td>
    <td><span>.</span></td>
    <td><span>"</span></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
  </tr>
</tbody></table></div><p>Output (length tokens are blue, distance tokens are red):</p>
<div><table><thead>
  <tr>
    <th><span>L</span></th>
    <th><span>i</span></th>
    <th><span>t</span></th>
    <th><span>t</span></th>
    <th><span>l</span></th>
    <th><span>e</span></th>
    <th></th>
    <th><span>b</span></th>
    <th><span>u</span></th>
    <th><span>n</span></th>
    <th><span>n</span></th>
    <th><span>y</span></th>
    <th></th>
    <th><span>F</span></th>
    <th><span>o</span></th>
    <th><span>o</span></th>
    <th>5</th>
    <th>4</th>
    <th><span>W</span></th>
    <th><span>e</span></th>
  </tr>
</thead>
<tbody>
  <tr>
    <td><span>n</span></td>
    <td><span>t</span></td>
    <td></td>
    <td><span>h</span></td>
    <td><span>o</span></td>
    <td><span>p</span></td>
    <td><span>p</span></td>
    <td><span>i</span></td>
    <td><span>n</span></td>
    <td><span>g</span></td>
    <td></td>
    <td><span>t</span></td>
    <td><span>h</span></td>
    <td><span>r</span></td>
    <td><span>o</span></td>
    <td><span>u</span></td>
    <td><span>g</span></td>
    <td><span>h</span></td>
    <td>3</td>
    <td>8</td>
  </tr>
  <tr>
    <td><span>e</span></td>
    <td></td>
    <td><span>f</span></td>
    <td><span>o</span></td>
    <td><span>r</span></td>
    <td><span>e</span></td>
    <td><span>s</span></td>
    <td><span>t</span></td>
    <td></td>
    <td><span>S</span></td>
    <td><span>c</span></td>
    <td><span>o</span></td>
    <td><span>o</span></td>
    <td>5</td>
    <td>28</td>
    <td><span>u</span></td>
    <td><span>p</span></td>
    <td>6</td>
    <td>23</td>
    <td><span>i</span></td>
  </tr>
  <tr>
    <td><span>e</span></td>
    <td><span>l</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>m</span></td>
    <td><span>i</span></td>
    <td><span>c</span></td>
    <td><span>e</span></td>
    <td></td>
    <td><span>A</span></td>
    <td><span>n</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>b</span></td>
    <td>9</td>
    <td>58</td>
    <td><span>e</span></td>
    <td><span>m</span></td>
    <td></td>
    <td><span>o</span></td>
  </tr>
  <tr>
    <td><span>n</span></td>
    <td>5</td>
    <td>35</td>
    <td><span>h</span></td>
    <td><span>e</span></td>
    <td><span>a</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>D</span></td>
    <td><span>o</span></td>
    <td><span>w</span></td>
    <td><span>n</span></td>
    <td></td>
    <td><span>c</span></td>
    <td><span>a</span></td>
    <td><span>m</span></td>
    <td><span>e</span></td>
    <td>5</td>
    <td>19</td>
    <td><span>G</span></td>
  </tr>
  <tr>
    <td><span>o</span></td>
    <td><span>o</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>F</span></td>
    <td><span>a</span></td>
    <td><span>i</span></td>
    <td><span>r</span></td>
    <td><span>y</span></td>
    <td><span>,</span></td>
    <td></td>
    <td><span>a</span></td>
    <td>3</td>
    <td>55</td>
    <td><span>s</span></td>
    <td>3</td>
    <td>20</td>
    <td><span>s</span></td>
    <td><span>a</span></td>
    <td><span>i</span></td>
  </tr>
  <tr>
    <td><span>d</span></td>
    <td></td>
    <td><span>"</span></td>
    <td><span>L</span></td>
    <td>20</td>
    <td>149</td>
    <td><span>I</span></td>
    <td></td>
    <td><span>d</span></td>
    <td><span>o</span></td>
    <td><span>n</span></td>
    <td><span>'</span></td>
    <td><span>t</span></td>
    <td></td>
    <td><span>w</span></td>
    <td><span>a</span></td>
    <td>3</td>
    <td>157</td>
    <td><span>t</span></td>
    <td><span>o</span></td>
  </tr>
  <tr>
    <td></td>
    <td><span>s</span></td>
    <td><span>e</span></td>
    <td><span>e</span></td>
    <td></td>
    <td><span>y</span></td>
    <td><span>o</span></td>
    <td><span>o</span></td>
    <td>56</td>
    <td>141</td>
    <td><span>.</span></td>
    <td><span>"</span></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
  </tr>
</tbody></table></div><p>DEFLATE managed to reduce the original text from 251 characters, to just 152 tokens! Those tokens are later compressed further by Huffman encoding, which is the second stage.</p><p>How long and devotedly the algorithm searches for a string before it stops is defined by the compression level. For example with compression level 4 the algorithm will be happy to find a match of 16 bytes, whereas with level 9 it will attempt to look for the maximal 258 byte match. If a match was not found the algorithm outputs the input as is, uncompressed.</p><p>Clearly at the beginning of the input, there can be no references to previous strings, and it is always uncompressed. Similarly the first occurrence of any string in the input will never be compressed. For example almost all HTML files start with the string "&lt;html ", however in this string only the second HTML will be replaced with a match, and the rest of the string will remain uncompressed.</p><p>To solve this problem the deflate dictionary effectively acts as an initial back reference for possible matches. So if we add the aforementioned string "&lt;html " to the dictionary, the algorithm will be able to match it from the start, improving the compression ratio. And there are many more such strings that are used in any HTML page, which we can put in the dictionary to improve compression ratio. In fact the SPDY protocol uses this technique for HTTP header compression.</p><p>To illustrate, lets compress the children’s song with the help of a 42 byte dictionary, containing the following: Little bunny Foo hopping forest Good Fairy. The compressed output will then be:</p>
<div><table><thead>
  <tr>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
    <th></th>
  </tr></thead>
<tbody>
  <tr>
    <td>17</td>
    <td>42</td>
    <td>4</td>
    <td>4</td>
    <td><span>W</span></td>
    <td><span>e</span></td>
    <td><span>n</span></td>
    <td><span>t</span></td>
    <td>9</td>
    <td>51</td>
    <td><span>t</span></td>
    <td><span>h</span></td>
    <td></td>
    <td><span>o</span></td>
    <td><span>u</span></td>
    <td><span>g</span></td>
    <td><span>h</span></td>
    <td>3</td>
    <td>8</td>
    <td><span>e</span></td>
  </tr>
  <tr>
    <td>8</td>
    <td>63</td>
    <td><span>S</span></td>
    <td><span>c</span></td>
    <td><span>o</span></td>
    <td><span>o</span></td>
    <td>5</td>
    <td>28</td>
    <td><span>u</span></td>
    <td><span>p</span></td>
    <td>6</td>
    <td>23</td>
    <td><span>i</span></td>
    <td><span>e</span></td>
    <td><span>l</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>m</span></td>
    <td><span>i</span></td>
    <td><span>c</span></td>
  </tr>
  <tr>
    <td><span>e</span></td>
    <td></td>
    <td><span>A</span></td>
    <td><span>n</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>b</span></td>
    <td>9</td>
    <td>58</td>
    <td><span>e</span></td>
    <td><span>m</span></td>
    <td></td>
    <td><span>o</span></td>
    <td><span>n</span></td>
    <td>5</td>
    <td>35</td>
    <td><span>h</span></td>
    <td><span>e</span></td>
    <td><span>a</span></td>
    <td><span>d</span></td>
  </tr>
  <tr>
    <td></td>
    <td><span>D</span></td>
    <td><span>o</span></td>
    <td><span>w</span></td>
    <td><span>n</span></td>
    <td></td>
    <td><span>c</span></td>
    <td><span>a</span></td>
    <td><span>m</span></td>
    <td><span>e</span></td>
    <td>5</td>
    <td>19</td>
    <td>10</td>
    <td>133</td>
    <td><span>,</span></td>
    <td><span>a</span></td>
    <td>3</td>
    <td>55</td>
    <td><span>s</span></td>
    <td>3</td>
  </tr>
  <tr>
    <td>20</td>
    <td><span>s</span></td>
    <td><span>a</span></td>
    <td><span>i</span></td>
    <td><span>d</span></td>
    <td></td>
    <td><span>"</span></td>
    <td>21</td>
    <td>149</td>
    <td><span>I</span></td>
    <td></td>
    <td><span>d</span></td>
    <td><span>o</span></td>
    <td><span>n</span></td>
    <td><span>'</span></td>
    <td><span>t</span></td>
    <td></td>
    <td><span>w</span></td>
    <td><span>a</span></td>
    <td>3</td>
  </tr>
  <tr>
    <td>157</td>
    <td><span>t</span></td>
    <td><span>o</span></td>
    <td></td>
    <td><span>s</span></td>
    <td><span>e</span></td>
    <td><span>e</span></td>
    <td></td>
    <td><span>y</span></td>
    <td><span>o</span></td>
    <td><span>o</span></td>
    <td>56</td>
    <td>141</td>
    <td><span>.</span></td>
    <td><span>"</span></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
  </tr>
</tbody></table></div><p>Now, even strings at the very beginning of the input are compressed and strings that only appear once in the file are compressed as well. With the help of the dictionary we are down to 115 tokens. That means roughly 25% better compression rate.</p>
    <div>
      <h2>An experiment</h2>
      <a href="#an-experiment">
        
      </a>
    </div>
    <p>We wanted to see if we could make a dictionary that would benefit ALL the HTML pages we serve, and not just a specific domain. To that end we scanned over 20,000 publicly available random HTML pages that passed through our servers on a random sunny day, we took the first 16KB of each page, and used them to prepare two dictionaries, one of 16KB and the other of 32KB. Using a larger dictionary is useless, because then it would be larger than the LZ77 window used by deflate.</p><p>To build a dictionary, I made a little go program that takes a set of files and performs "pseudo" LZ77 over them, finding strings that DEFLATE would not compress in the first 16KB of each input file. It then performs a frequency count of the individual strings, and scores them according to their length and frequency. In the end the highest scoring strings are saved into the dictionary file.</p><p>Our benchmark consists of another set of pages obtained in similar manner. The number of benchmarked files was about 19,000 with total size of 563MB.</p>
<div><table><thead>
  <tr>
    <th><span>deflate -4</span></th>
    <th><span>deflate -9</span></th>
    <th><span>deflate -4 + 16K dict</span></th>
    <th><span>deflate -9 + 16K dict</span></th>
    <th><span>deflate -4 + 32K dict</span></th>
    <th><span>deflate -9 + 32K dict</span></th>
    <th></th>
  </tr></thead>
<tbody>
  <tr>
    <td><span>Size (KB)</span></td>
    <td><span>169,176</span></td>
    <td><span>166,012</span></td>
    <td><span>161,896</span></td>
    <td><span>158,352</span></td>
    <td><span>161,212</span></td>
    <td><span>157,444</span></td>
  </tr>
  <tr>
    <td><span>Time (sec)</span></td>
    <td><span>6.90</span></td>
    <td><span>11.56</span></td>
    <td><span>7.15</span></td>
    <td><span>11.80</span></td>
    <td><span>7.88</span></td>
    <td><span>11.82</span></td>
  </tr>
</tbody></table></div><p>We can see from the results that the compression we gain for level 4 is almost 5% better than without the dictionary, which is even greater than the compression gained by using level 9 compression, while being substantially faster. For level 9, the gain is greater than 5% without a significant performance hit.</p><p>The results highly depend on the dataset used for the dictionary and on the compressed pages. For example when making a dicitonary aimed at a specific web site, the compression rate for that site increased by up to 30%.</p><p>For very small pages, such as error pages, with size less than 1KB, a DEFLATE dictionary was able to gain compression rates of up to 50% smaller than DEFLATE alone.</p><p>Of course different dictionaries may be used for different file types. In fact we think that it would make sense to create a standard set of dictionaries that could be used accross the web.</p><p>The utility to make a dictionary for deflate can be found at <a href="https://github.com/vkrasnov/dictator">https://github.com/vkrasnov/dictator</a>.The optimized version of zlib used by CloudFlare can be found at <a href="https://github.com/cloudflare/zlib">https://github.com/cloudflare/zlib</a></p> ]]></content:encoded>
            <category><![CDATA[Google]]></category>
            <category><![CDATA[Optimization]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Compression]]></category>
            <guid isPermaLink="false">4bo94j5hDmZv1CmCO7udHU</guid>
            <dc:creator>Vlad Krasnov</dc:creator>
        </item>
        <item>
            <title><![CDATA[Efficiently compressing dynamically generated web content]]></title>
            <link>https://blog.cloudflare.com/efficiently-compressing-dynamically-generated-53805/</link>
            <pubDate>Thu, 06 Dec 2012 09:19:00 GMT</pubDate>
            <description><![CDATA[ With the widespread adoption of high bandwidth Internet connections in the home, offices and on mobile devices, limitations in available bandwidth to download web pages have largely been eliminated. ]]></description>
            <content:encoded><![CDATA[ <p><i>I originally wrote this article for the </i><a href="http://calendar.perfplanet.com/2012/efficiently-compressing-dynamically-generated-web-content/"><i>Web Performance Calendar website</i></a><i>, which is a terrific resource of expert opinions on making your website as fast as possible. We thought CloudFlare users would be interested so we reproduced it here. Enjoy!</i></p>
    <div>
      <h3>Efficiently compressing dynamically generated web content</h3>
      <a href="#efficiently-compressing-dynamically-generated-web-content">
        
      </a>
    </div>
    <p>With the widespread adoption of high bandwidth Internet connections in the home, offices and on mobile devices, limitations in available bandwidth to download web pages have largely been eliminated.</p><p>At the same time latency remains a major problem. According to a recent presentation by Google, broadband Internet latency is 18ms for fiber technologies, 26ms for cable-based services, 43ms for DSL and 150ms-400ms for mobile devices. Ultimately, bandwidth can be expanded greatly with new technologies but latency is limited by the speed of light. The latency of an Internet connection directly affects the speed with which a web page can be downloaded.</p><p>The latency problem occurs because the TCP protocol requires round trips to acknowledge received information (since packets can and do get lost while traversing the Internet) and to prevent Internet congestion TCP has mechanisms to limit the amount of data sent per round trip until it has learnt how much it can send without causing congestion.</p><p>The collision between the speed of light and the TCP protocol is made worse by the fact that web site owners are likely to choose the cheapest hosting available without thinking about its physical location. In fact, the move to ‘the cloud' encourages the idea that web sites are simply ‘out there' without taking into account the very real problem of latency introduced by the distance between the end user's web browser and the server. It is not uncommon, for example, to see web sites aimed at UK consumers being hosted in the US. A web user in London accessing a .co.uk site that is actually hosted in Chicago incurs an additional 60ms round trip time because of the distance traversed.</p><p>Dealing with speed-of-light induced latency requires moving web content closer to user who are browsing, or making the web content smaller so that fewer round trips are required (or both).</p>
    <div>
      <h3>The caching challenge</h3>
      <a href="#the-caching-challenge">
        
      </a>
    </div>
    <p>Caching technologies and content delivery services mean that static content (such as images, CSS, JavaScript) can be e cached close to end users helping to reduce latency when they are loaded. CloudFlare sees on average that about 65% of web content is cacheable.</p><p>But the most critical part of a web page, the actual HTML content is often dynamically generated and cannot be cached. Because none of the relatively fast to load content that's in cache cannot even be loaded before the HTML, any delay in the web browser receiving the HTML affects the entire web browsing experience.</p><p>Thus being able to deliver the page HTML as quickly as possible even in high latency environments is vital to ensuring a good browsing experience. Studies have shown that the slower the page load time the more likely the user is to give up and move elsewhere. A recent Google study said that a response time of less than 100ms is perceived by a human as ‘instant' (a human eye blink is somewhere in the 100ms to 400ms range); less than 300ms the computer seems sluggish; above 1s and the user's train of thought is lost to distraction or other thoughts. TCP's congestion avoidance algorithm means that many round trips are necessary when downloading a web page. For example, getting just the HTML for the CNN home page takes approximately 15 round trips; it's not hard to see how long latency can quickly multiply into a situation where the end-user is losing patience with the web site.</p><p>Unfortunately, it is not possible to cache the HTML of most web pages because it is dynamically generated. Dynamic pages are commonplace because the HTML is programmatically generated and not static. For example, a news web site will generate fresh HTML as news stories change or to show a different page depending on the geographical location of the end user. Many web pages are also dynamically generated because they are personalized for the end user — each person's Facebook page is unique. And web application frameworks, such as WordPress, encourage the use dynamically generate HTML by default and mark the content as uncachable.</p>
    <div>
      <h3>Compression to the rescue</h3>
      <a href="#compression-to-the-rescue">
        
      </a>
    </div>
    <p>Given that web pages need to be dynamically generated the only viable option is to reduce the page size so that fewer TCP round trips are needed minimizing the effect of latency. The current best option for doing this is the use of the gzip encoding. On typical web page content gzip encoding will reduce the page size to about 20-25% of the original size. But this still results in multiple-kilobytes of page data being transmitted incurring the TCP congestion avoidance and latency penalty; in the CNN example above there were 15 round-trips even though the page was gzip compressed.</p><p>Gzip encoding is completely generic. It does not take into account any special features of the content it is compressing. It is also self-referential: a gzip encoded page is entirely self-contained. This is advantageous because it means that a system that uses gzipped content can be stateless, but it means that even larger compression ratios that would be possible with external dictionaries of common content are notpossible.</p><p>External dictionaries increase compression ratios dramatically because the compressed data can refer to items from the dictionary. Those references can be very small (a few bytes each) but expand to very large content from the dictionary.</p><p>For example, imagine that it's necessary to transmit The King James Bible to a user. The plain text version from Project Gutenberg is 4,452,097 bytes and compressed with gzip it is 1,404,452 bytes (a reduction in size to 31%). But imagine the case where the compressor knows that the end user has a separate copy of the Old Testament and New Testament in a dictionary of useful content. Instead of transmitting a megabyte of gzip compressed content they can transmit an instruction of the form &lt;Insert Old Testament&gt;&lt;Insert New Testament&gt;. That instruction will just be a few bytes long.</p><p>Clearly, that's an extreme and unusual case but it highlights the usefulness of external shared dictionaries of common content that can be used to reconstruct an original, uncompressed document. External dictionaries can be applied to dynamically generated web content to achieve compression that exceeds that possible with gzip.</p>
    <div>
      <h3>Caching page parts</h3>
      <a href="#caching-page-parts">
        
      </a>
    </div>
    <p>On the web, shared dictionaries make sense because dynamic web content contains large chunks that's the same for all users and over time. Consider, for example the BBC News homepage which is approximately 116KB of HTML. That page is dynamically generated and the HTTP caching headers are set so that it is not cached. Even though the news stories on the page are frequently updated a large amount of boilerplate HTML does not change from request to request (or even user to user). The first 32KB of the page (28% of the HTML) consists of embedded JavaScript, headers, navigational elements and styles. If that ‘header block' were stored by web browsers in a local dictionary then the BBC would only need to send a small instruction saying &lt;Insert BBC Header&gt; instead of 32KB of data. That would save multiple round-trips. And throughout the BBC News page there are smaller chunks of unchanging content that could be referenced from a dictionary.</p><p>It's not hard to imagine that for any web site there are large parts of the HTML that are the same from request to request and from user to user. Even on a very personalized site like Facebook the HTML is similar from user to user.</p><p>And as more and more applications use HTTP for APIs there's an opportunity to increase API performance through the use of shared dictionaries of JSON or XML. APIs often contain even more common, repeated parts than HTML as they are intended for machine consumption and change slowly over time (whereas the HTML of a page will change more quickly as designers update the look of a page).</p><p>Two different proposals have tried to address this in different ways:</p><p>SDCH and ESI. Neither have achieved acceptance as Internet standards partly because of the added complexity of deploying them.</p>
    <div>
      <h4>SDCH</h4>
      <a href="#sdch">
        
      </a>
    </div>
    <p>In 2008, a group working at Google proposed a protocol for negotiating shared dictionaries of content so that a web server can compress a page in the knowledge that a web browser has chunks of the page in its cache. The proposal is known as <a href="http://en.wikipedia.org/wiki/Shared_Dictionary_Compression_Over_HTTP">SDCH</a> (Shared Dictionary Compression over HTTP). Current versions of Google Chrome use SDCH to compress Google Search results.</p><p>This can be seen in the Developer Tools in Google Chrome. Any search request will contain an HTTP header specifying that the browser accepts SDCH compressed pages:</p>
            <pre><code>Accept-Encoding: gzip,deflate,sdch</code></pre>
            <p>And if SDCH is used then the server responds indicating the dictionary that was used. If necessary Chrome will retrieve the dictionary. Since the dictionary should change infrequently it will be in local web browser cache most of the time. For example, here's a sample HTTP header seen in a real response from a Google Search:</p>
            <pre><code>Get-Dictionary: /sdch/60W93cgP.dct</code></pre>
            <p>The dictionary file simply contains HTML (and JavaScript etc.) and the compressed page contains references to parts of the dictionary file using the <a href="http://en.wikipedia.org/wiki/VCDIFF">VCDIFF</a> format specified in <a href="http://tools.ietf.org/html/rfc3284">RFC 3284</a>. The compressed page consists mostly of COPY and ADD VCDIFF functions. A COPY x, y instruction tells the browser to copy y bytes of data from osition x in the dictionary (this is how common content gets compressed and expanded from the dictionary). The ADD instruction is used to insert uncompressed data (i.e. those parts of the page that are not in the dictionary).</p><p>In a Google Search the dictionary is used to locally cache infrequently changing parts of a page (such as the HTML header, navigation elements and page footer).</p><p>SDCH has not achieved widespread acceptance because of the difficulty of generating the shared dictionaries. Three problems arise: when to update the dictionary, how to update the dictionary and prevention of leakage of private information.</p><p>For maximum effectiveness it's desirable to produce a shared dictionary that will be useful in reducing page sizes across a large number of page views. To do this it's necessary to either implement an automatic technique that samples real web traffic and identifies common blocks of HTML, or to determine which pages are most viewed and compute dictionaries for them (perhaps based on specialised knowledge of what parts of the page are common across requests).</p><p>When automated techniques are used it's important to ensure that when sampling traffic that contains personal information (such as for a logged in user) that personal information does not end up in the dictionary.</p><p>Although SDCH is powerful when used, these dictionary generation difficulties have prevented its widespread use. The Apache mod_sdch project is inactive and the Google SDCH group has been largely inactive since 2011.</p>
    <div>
      <h4>ESI</h4>
      <a href="#esi">
        
      </a>
    </div>
    <p>In 2001 a consortium of companies proposed addressing both latency and common content with <a href="http://en.wikipedia.org/wiki/Edge_Side_Includes">ESI</a> (Edge Side Includes). Edge Side Includes work by having a web page creator identify unchanging parts of the page and then making these available as separate mini-pages using HTTP.</p><p>For example, if a page contains a common header and navigation, a web page author might place that in a separate nav.html file and then in a page they are authoring enter the following XML in place of the header and navigation HTML:</p>
            <pre><code>\&amp;lt;esi:include src=&amp;quot;http://example.com/nav.html&amp;quot; &amp;quot;continue&amp;quot;/&amp;gt;</code></pre>
            <p>ESI is intended for use with HTML content that is delivered via a Content Delivery Network and major CDNs were the sponsor of the original proposal.</p><p>When a user retrieves a CDN managed page that contains ESI components the <a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/">CDN</a> reconstructs the complete page from the component parts (which the CDN will either have to retrieve, or, more likely, have in cache since they change infrequently).</p><p>The CDN delivers the complete, normal HTML to the end user, but because the CDN has access nodes all over the world the latency between the end user web browser and the CDN is minimized. ESI tries to minimize the amount of data sent between the origin web server and the CDN (where the latency may be high) while being transparent to the browser.</p><p>The biggest problem with adoption of ESI is that it forces web page authors to break pages up into blocks that can be safely cached by a CDN adding to the complexity of web page authoring. In addition, a CDN has to be used to deliver the pages as web browsers do not understand the ESI directives.</p>
    <div>
      <h3>The time dimension</h3>
      <a href="#the-time-dimension">
        
      </a>
    </div>
    <p>The SDCH and ESI approaches rely on identifying parts of pages that are known to be unchanging and can be cached either at the edge of a CDN or in a shared dictionary in a web browser.</p><p>Another approach is to consider how web pages evolve over time. It is common for web users to visit the same web pages frequently (such as news sites, online email, social media and major retailers). This maymean that a user's web browser has some previous version of the web page they are loading in its local cache. Even though that web page may be out of date it could still be used as a shared dictionary as components of it are likely to appear in the latest version of the page.</p><p>For example, a daily visit to a news web site could be speeded up if a web server were only able to send the differences between yesterday's news and today's. It's likely that most of the HTML of a page like the BBC News homepage will have remained unchanged; only the stories will be new and they will only make up a small portion of the page.</p><p>CloudFlare looked at how much dynamically generated pages change over time and found that, for example, reddit.com changes by about 2.15% over five minutes and 3.16% over an hour. The New York Times home page changes by about 0.6% over five minutes and 3% over an hour. BBC News changes by about 0.4% over five minutes and 2% over an hour. With delta compression it would be possible to turn those figures directly into a compression ratio by only sending the tiny percentage of the page that has changed. Compressing the BBC News web site to 0.4% is an enormous improvement compared to gzip's 20-25% compression ratio meaning that 116KB would result in just 464 bytes transmitted (which would likely all fit in a single TCP packet requiring a single round trip).</p><p>This delta method is the essence of <a href="http://www.ietf.org/rfc/rfc3229.txt">RFC 3229</a> which was written in 2002.</p>
    <div>
      <h3>RFC 3229</h3>
      <a href="#rfc-3229">
        
      </a>
    </div>
    <p>This RFC proposes an extension to HTTP where a web browser can indicate to a server that it has a particular version of a page (using the value from the ETag HTTP header that was supplied when the page was previously downloaded). The receiving web server can then apply a delta compression technique (encoded using VCDIFF discussed above) to send only the parts that have changed since that particular version of the page.</p><p>The RFC also proposes that a web browser be able to send the identifiers of multiple versions of a single page so that the web server can choose among them. That way, if the web browser has multiple versions in cache there's an increased chance that the server will have one of the versions available to it for delta compression.</p><p>Although this technique is powerful because it greatly reduces the amount of data to be sent from a web server to browser it has not been widely deployed because of the enormous resources needed on web servers.</p><p>To be effective a web server would need to keep copies of versions of the pages it generates in order that when a request comes in it is able to perform delta compression. For a popular web site that would create a large storage burden; for a site with heavy personalization it would mean keeping a copy of the pages served to every single user. For example, Facebook has around 1 billion active users, just keeping a copy of the HTML of the last time they viewed their timeline would require 250TB of storage.</p>
    <div>
      <h3>CloudFlare's Railgun</h3>
      <a href="#cloudflares-railgun">
        
      </a>
    </div>
    <p>CloudFlare's <a href="https://www.cloudflare.com/railgun">Railgun</a> is a transparent delta compression technology that takes advantage of CloudFlare's CDN network to greatly accelerate the transmission of dynamically generated web pages from origin web servers to the CDN node nearest end user web surfers. Unlike SDCH and ESI it does not require any work on the part of a web site creator and unlike RFC 3229 it does not require caching a version of each page for each end user.</p><p>Railgun consists of two components: the sender and the listener. The sender is installed at every CloudFlare data center around the world. The listener is a software component that customers install on their network.</p><p>The sender and listener establish a permanent TCP connection that's secured by TLS. This TCP connection is used for the Railgun protocol. It's an all binary multiplexing protocol that allows multiple HTTP requests to be run simultaneously and asynchronously across the link. To a web client the Railgun system looks like a proxy server, but instead of being a server it's a wide-area link with special properties. One of those properties is that it performs compression on non-cacheable content by synchronizing page versions.</p><p>Each end of the Railgun link keeps track of the last version of a web page that's been requested. When a new request comes in for a page that Railgun has already seen, only the changes are sent across the link. The listener component make an HTTP request to the real, origin web server for the uncacheable page, makes a comparison with the stored version and sends across the differences.</p><p>The sender then reconstructs the page from its cache and the difference sent by the other side. Because multiple users pass through the same Railgun link only a single cached version of the page is needed for delta compression as opposed to one per end user with techniques like RFC 3229.</p><p>For example, a test on a major news site sent 23,529 bytes of gzipped data which when decompressed become 92,516 bytes of page (so the page is compressed to 25.25% of its original size). Railgun compression between two version of the page at a five minute interval resulted in just 266 bytes of difference data being sent (a compression to 0.29% of the original page size). The one hour difference is 2,885 bytes (a compression to 3% of the original page size). Clearly, Railgun delta compression outperforms gzip enormously.</p><p>For pages that are frequently accessed the deltas are often so small that they fit inside a single TCP packet, and because the connection between the two parts of Railgun is kept active problems with TCP congestion avoidance are eliminated.</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>The use of external dictionaries of content is a powerful technique that can achieve much larger compression ratios that the self-contained gzip method. But only CloudFlare's Railgun implements delta compression in a manner that is completely transparent to end users and website owners.</p> ]]></content:encoded>
            <category><![CDATA[Tech Talks]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Compression]]></category>
            <category><![CDATA[Cache]]></category>
            <category><![CDATA[Deep Dive]]></category>
            <guid isPermaLink="false">39TxvNcWsuE7mZgsb8eRZi</guid>
            <dc:creator>John Graham-Cumming</dc:creator>
        </item>
    </channel>
</rss>