
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Wed, 15 Apr 2026 19:40:53 GMT</lastBuildDate>
        <item>
            <title><![CDATA[How we built the most efficient inference engine for Cloudflare’s network ]]></title>
            <link>https://blog.cloudflare.com/cloudflares-most-efficient-ai-inference-engine/</link>
            <pubDate>Wed, 27 Aug 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ Infire is an LLM inference engine that employs a range of techniques to maximize resource utilization, allowing us to serve AI models more efficiently with better performance for Cloudflare workloads. ]]></description>
            <content:encoded><![CDATA[ <p>Inference powers some of today’s most powerful AI products: chat bot replies, <a href="https://www.cloudflare.com/learning/ai/what-is-agentic-ai/"><u>AI agents</u></a>, autonomous vehicle decisions, and fraud detection. The problem is, if you’re building one of these products on top of a hyperscaler, you’ll likely need to rent expensive GPUs from large centralized data centers to run your inference tasks. That model doesn’t work for Cloudflare — there’s a mismatch between Cloudflare’s globally-distributed network and a typical centralized AI deployment using large multi-GPU nodes. As a company that operates our own compute on a lean, fast, and widely distributed network within 50ms of 95% of the world’s Internet-connected population, we need to be running inference tasks more efficiently than anywhere else.</p><p>This is further compounded by the fact that AI models are getting larger and more complex. As we started to support these models, like the Llama 4 herd and gpt-oss, we realized that we couldn’t just throw money at the scaling problems by buying more GPUs. We needed to utilize every bit of idle capacity and be agile with where each model is deployed. </p><p>After running most of our models on the widely used open source inference and serving engine <a href="https://github.com/vllm-project/vllm"><u>vLLM</u></a>, we figured out it didn’t allow us to fully utilize the GPUs at the edge. Although it can run on a very wide range of hardware, from personal devices to data centers, it is best optimized for large data centers. When run as a dedicated inference server on powerful hardware serving a specific model, vLLM truly shines. However, it is much less optimized for dynamic workloads, distributed networks, and for the unique security constraints of running inference at the edge alongside other services.</p><p>That’s why we decided to build something that will be able to meet the needs of Cloudflare inference workloads for years to come. Infire is an LLM inference engine, written in Rust, that employs a range of techniques to maximize memory, network I/O, and GPU utilization. It can serve more requests with fewer GPUs and significantly lower CPU overhead, saving time, resources, and energy across our network. </p><p>Our initial benchmarking has shown that Infire completes inference tasks up to 7% faster than vLLM 0.10.0 on unloaded machines equipped with an H100 NVL GPU. On infrastructure under real load, it performs significantly better. </p><p>Currently, Infire is powering the Llama 3.1 8B model for <a href="https://developers.cloudflare.com/workers-ai/"><u>Workers AI</u></a>, and you can test it out today at <a href="https://developers.cloudflare.com/workers-ai/models/llama-3.1-8b-instruct-fast/"><u>@cf/meta/llama-3.1-8b-instruct</u></a>!</p>
    <div>
      <h2>The Architectural Challenge of LLM Inference at Cloudflare </h2>
      <a href="#the-architectural-challenge-of-llm-inference-at-cloudflare">
        
      </a>
    </div>
    <p>Thanks to industry efforts, inference has improved a lot over the past few years. vLLM has led the way here with the recent release of the vLLM V1 engine with features like an optimized KV cache, improved batching, and the implementation of Flash Attention 3. vLLM is great for most inference workloads — we’re currently using it for several of the models in our <a href="https://developers.cloudflare.com/workers-ai/models/"><u>Workers AI catalog</u></a> — but as our AI workloads and catalog has grown, so has our need to optimize inference for the exact hardware and performance requirements we have. </p><p>Cloudflare is writing much of our <a href="https://blog.cloudflare.com/rust-nginx-module/"><u>new infrastructure in Rust</u></a>, and vLLM is written in Python. Although Python has proven to be a great language for prototyping ML workloads, to maximize efficiency we need to control the low-level implementation details. Implementing low-level optimizations through multiple abstraction layers and Python libraries adds unnecessary complexity and leaves a lot of CPU performance on the table, simply due to the inefficiencies of Python as an interpreted language.</p><p>We love to contribute to open-source projects that we use, but in this case our priorities may not fit the goals of the vLLM project, so we chose to write a server for our needs. For example, vLLM does not support co-hosting multiple models on the same GPU without using Multi-Instance GPU (MIG), and we need to be able to dynamically schedule multiple models on the same GPU to minimize downtime. We also have an in-house AI Research team exploring unique features that are difficult, if not impossible, to upstream to vLLM. </p><p>Finally, running code securely is our top priority across our platform and <a href="https://www.cloudflare.com/developer-platform/products/workers-ai/"><u>Workers AI</u></a> is no exception. We simply can’t trust a 3rd party Python process to run on our edge nodes alongside the rest of our services without strong sandboxing. We are therefore forced to run vLLM via <a href="https://gvisor.dev"><u>gvisor</u></a>. Having an extra virtualization layer adds another performance overhead to vLLM. More importantly, it also increases the startup and tear downtime for vLLM instances — which are already pretty long. Under full load on our edge nodes, vLLM running via gvisor consumes as much as 2.5 CPU cores, and is forced to compete for CPU time with other crucial services, that in turn slows vLLM down and lowers GPU utilization as a result.</p><p>While developing Infire, we’ve been incorporating the latest research in inference efficiency — let’s take a deeper look at what we actually built.</p>
    <div>
      <h2>How Infire works under the hood </h2>
      <a href="#how-infire-works-under-the-hood">
        
      </a>
    </div>
    <p>Infire is composed of three major components: an OpenAI compatible HTTP server, a batcher, and the Infire engine itself.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3BypYSG9QFsPjPFhjlOEsa/6ef5d4ccaabcd96da03116b7a14e8439/image2.png" />
          </figure><p><i><sup>An overview of Infire’s architecture </sup></i></p>
    <div>
      <h2>Platform startup</h2>
      <a href="#platform-startup">
        
      </a>
    </div>
    <p>When a model is first scheduled to run on a specific node in one of our data centers by our auto-scaling service, the first thing that has to happen is for the model weights to be fetched from our <a href="https://www.cloudflare.com/developer-platform/products/r2/"><u>R2 object storage</u></a>. Once the weights are downloaded, they are cached on the edge node for future reuse.</p><p>As the weights become available either from cache or from R2, Infire can begin loading the model onto the GPU. </p><p>Model sizes vary greatly, but most of them are <b>large, </b>so transferring them into GPU memory can be a time-consuming part of Infire’s startup process. For example, most non-quantized models store their weights in the BF16 floating point format. This format has the same dynamic range as the 32-bit floating format, but with reduced accuracy. It is perfectly suited for inference providing the sweet spot of size, performance and accuracy. As the name suggests, the BF16 format requires 16 bits, or 2 bytes per weight. The approximate in-memory size of a given model is therefore double the size of its parameters. For example, LLama3.1 8B has approximately 8B parameters, and its memory footprint is about 16 GB. A larger model, like LLama4 Scout, has 109B parameters, and requires around 218 GB of memory. Infire utilizes a combination of <a href="https://developer.nvidia.com/blog/how-optimize-data-transfers-cuda-cc/#pinned_host_memory"><u>Page Locked</u></a> memory with CUDA asynchronous copy mechanism over multiple streams to speed up model transfer into GPU memory.</p><p>While loading the model weights, Infire begins just-in-time compiling the required kernels based on the model's parameters, and loads them onto the device. Parallelizing the compilation with model loading amortizes the latency of both processes. The startup time of Infire when loading the Llama-3-8B-Instruct model from disk is just under 4 seconds. </p>
    <div>
      <h3>The HTTP server</h3>
      <a href="#the-http-server">
        
      </a>
    </div>
    <p>The Infire server is built on top of <a href="https://docs.rs/hyper/latest/hyper/"><u>hyper</u></a>, a high performance HTTP crate, which makes it possible to handle hundreds of connections in parallel – while consuming a modest amount of CPU time. Because of ChatGPT’s ubiquity, vLLM and many other services offer OpenAI compatible endpoints out of the box. Infire is no different in that regard. The server is responsible for handling communication with the client: accepting connections, handling prompts and returning responses. A prompt will usually consist of some text, or a "transcript" of a chat session along with extra parameters that affect how the response is generated. Some parameters that come with a prompt include the temperature, which affects the randomness of the response, as well as other parameters that affect the randomness and length of a possible response.</p><p>After a request is deemed valid, Infire will pass it to the tokenizer, which transforms the raw text into a series of tokens, or numbers that the model can consume. Different models use different kinds of tokenizers, but the most popular ones use byte-pair encoding. For tokenization, we use HuggingFace's tokenizers crate. The tokenized prompts and params are then sent to the batcher, and scheduled for processing on the GPU, where they will be processed as vectors of numbers, called <a href="https://www.cloudflare.com/learning/ai/what-are-embeddings/"><u>embeddings</u></a>.</p>
    <div>
      <h2>The batcher</h2>
      <a href="#the-batcher">
        
      </a>
    </div>
    <p>The most important part of Infire is in how it does batching: by executing multiple requests in parallel. This makes it possible to better utilize memory bandwidth and caches. </p><p>In order to understand why batching is so important, we need to understand how the inference algorithm works. The weights of a model are essentially a bunch of two-dimensional matrices (also called tensors). The prompt represented as vectors is passed through a series of transformations that are largely dominated by one operation: vector-by-matrix multiplication. The model weights are so large, that the cost of the multiplication is dominated by the time it takes to fetch it from memory. In addition, modern GPUs have hardware units dedicated to matrix-by-matrix multiplications (called Tensor Cores on Nvidia GPUs). In order to amortize the cost of memory access and take advantage of the Tensor Cores, it is necessary to aggregate multiple operations into a larger matrix multiplication.</p><p>Infire utilizes two techniques to increase the size of those matrix operations. The first one is called prefill: this technique is applied to the prompt tokens. Because all the prompt tokens are available in advance and do not require decoding, they can all be processed in parallel. This is one reason why input tokens are often cheaper (and faster) than output tokens.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1pqyNSzgWLcgrV3urpCvA0/e204ac477992d591a7368632c36e97eb/image1.png" />
          </figure><p><sup><i>How Infire enables larger matrix multiplications via batching</i></sup></p><p>The other technique is called batching: this technique aggregates multiple prompts into a single decode operation.</p><p>Infire mixes both techniques. It attempts to process as many prompts as possible in parallel, and fills the remaining slots in a batch with prefill tokens from incoming prompts. This is also known as continuous batching with chunked prefill.</p><p>As tokens get decoded by the Infire engine, the batcher is also responsible for retiring prompts that reach an End of Stream token, and sending tokens back to the decoder to be converted into text. </p><p>Another job the batcher has is handling the KV cache. One demanding operation in the inference process is called <i>attention</i>. Attention requires going over the KV values computed for all the tokens up to the current one. If we had to recompute those previously encountered KV values for every new token we decode, the runtime of the process would explode for longer context sizes. However, using a cache, we can store all the previous values and re-read them for each consecutive token. Potentially the KV cache for a prompt can store KV values for as many tokens as the context window allows. In LLama 3, the maximal context window is 128K tokens. If we pre-allocated the KV cache for each prompt in advance, we would only have enough memory available to execute 4 prompts in parallel on H100 GPUs! The solution for this is paged KV cache. With paged KV caching, the cache is split into smaller chunks called pages. When the batcher detects that a prompt would exceed its KV cache, it simply assigns another page to that prompt. Since most prompts rarely hit the maximum context window, this technique allows for essentially unlimited parallelism under typical load.</p><p>Finally, the batcher drives the Infire forward pass by scheduling the needed kernels to run on the GPU.</p>
    <div>
      <h2>CUDA kernels</h2>
      <a href="#cuda-kernels">
        
      </a>
    </div>
    <p>Developing Infire gives us the luxury of focusing on the exact hardware we use, which is currently Nvidia Hopper GPUs. This allowed us to improve performance of specific compute kernels using low-level PTX instructions for this specific architecture.</p><p>Infire just-in-time compiles its kernel for the specific model it is running, optimizing for the model’s parameters, such as the hidden state size, dictionary size and the GPU it is running on. For some operations, such as large matrix multiplications, Infire will utilize the high performance cuBLASlt library, if it would deem it faster.</p><p>Infire also makes use of very fine-grained CUDA graphs, essentially creating a dedicated CUDA graph for every possible batch size on demand. It then stores it for future launch. Conceptually, a CUDA graph is another form of just-in-time compilation: the CUDA driver replaces a series of kernel launches with a single construct (the graph) that has a significantly lower amortized kernel launch cost, thus kernels executed back to back will execute faster when launched as a single graph as opposed to individual launches.</p>
    <div>
      <h2>How Infire performs in the wild </h2>
      <a href="#how-infire-performs-in-the-wild">
        
      </a>
    </div>
    <p>We ran synthetic benchmarks on one of our edge nodes with an H100 NVL GPU.</p><p>The benchmark we ran was on the widely used ShareGPT v3 dataset. We ran the benchmark on a set of 4,000 prompts with a concurrency of 200. We then compared Infire and vLLM running on bare metal as well as vLLM running under gvisor, which is the way we currently run in production. In a production traffic scenario, an edge node would be competing for resources with other traffic. To simulate this, we benchmarked vLLM running in gvisor with only one CPU available.</p><table><tr><td><p>
</p></td><td><p>requests/s</p></td><td><p>tokens/s</p></td><td><p>CPU load</p></td></tr><tr><td><p>Infire</p></td><td><p>40.91</p></td><td><p>17224.21</p></td><td><p>25%</p></td></tr><tr><td><p>vLLM 0.10.0</p></td><td><p>38.38</p></td><td><p>16164.41</p></td><td><p>140%</p></td></tr><tr><td><p>vLLM under gvisor</p></td><td><p>37.13</p></td><td><p>15637.32</p></td><td><p>250%</p></td></tr><tr><td><p>vLLM under gvisor with CPU constraints</p></td><td><p>22.04</p></td><td><p>9279.25</p></td><td><p>100%</p></td></tr></table><p>As evident from the benchmarks we achieved our initial goal of matching and even slightly surpassing vLLM performance, but more importantly, we’ve done so at a significantly lower CPU usage, in large part because we can run Infire as a trusted bare-metal process. Inference no longer takes away precious resources from our other services and we see GPU utilization upward of 80%, reducing our operational costs.</p><p>This is just the beginning. There are still multiple proven performance optimizations yet to be implemented in Infire – for example, we’re integrating Flash Attention 3, and most of our kernels don’t utilize kernel fusion. Those and other optimizations will allow us to unlock even faster inference in the near future.</p>
    <div>
      <h2>What’s next </h2>
      <a href="#whats-next">
        
      </a>
    </div>
    <p>Running AI inference presents novel challenges and demands to our infrastructure. Infire is how we’re running AI efficiently — close to users around the world. By building upon techniques like continuous batching, a paged KV-cache, and low-level optimizations tailored to our hardware, Infire maximizes GPU utilization while minimizing overhead. Infire completes inference tasks faster and with a fraction of the CPU load of our previous vLLM-based setup, especially under the strict security constraints we require. This allows us to serve more requests with fewer resources, making requests served via Workers AI faster and more efficient.</p><p>However, this is just our first iteration — we’re excited to build in multi-GPU support for larger models, quantization, and true multi-tenancy into the next version of Infire. This is part of our goal to make Cloudflare the best possible platform for developers to build AI applications.</p><p>Want to see if your AI workloads are faster on Cloudflare? <a href="https://developers.cloudflare.com/workers-ai/"><u>Get started</u></a> with Workers AI today. </p> ]]></content:encoded>
            <category><![CDATA[AI Week]]></category>
            <category><![CDATA[LLM]]></category>
            <category><![CDATA[Workers AI]]></category>
            <guid isPermaLink="false">7Li4fkq9b4B8QlgwSmZrqE</guid>
            <dc:creator>Vlad Krasnov</dc:creator>
            <dc:creator>Mari Galicer</dc:creator>
        </item>
        <item>
            <title><![CDATA[Sippy helps you avoid egress fees while incrementally migrating data from S3 to R2]]></title>
            <link>https://blog.cloudflare.com/sippy-incremental-migration-s3-r2/</link>
            <pubDate>Tue, 26 Sep 2023 13:00:44 GMT</pubDate>
            <description><![CDATA[ Use Sippy to incrementally migrate data from S3 to R2 as it’s requested and avoid migration-specific egress fees ]]></description>
            <content:encoded><![CDATA[ <p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4whKfl7szHhdKNWCiCETR3/d65f22ad8c25598a41426bd1de59188e/image1-16.png" />
            
            </figure><p>Earlier in 2023, we announced <a href="/r2-super-slurper-ga/">Super Slurper</a>, a data migration tool that makes it easy to copy large amounts of data to <a href="https://developers.cloudflare.com/r2/">R2</a> from other <a href="https://www.cloudflare.com/developer-platform/products/r2/">cloud object storage</a> providers. Since the announcement, developers have used Super Slurper to run thousands of successful migrations to R2!</p><p>While Super Slurper is perfect for cases where you want to move all of your data to R2 at once, there are scenarios where you may want to migrate your data incrementally over time. Maybe you want to avoid the one time upfront <a href="https://www.cloudflare.com/learning/cloud/what-is-aws-data-transfer-pricing/">AWS data transfer bill</a>? Or perhaps you have legacy data that may never be accessed, and you only want to migrate what’s required?</p><p>Today, we’re announcing the open beta of <a href="https://developers.cloudflare.com/r2/data-migration/sippy/">Sippy</a>, an incremental migration service that copies data from S3 (other cloud providers coming soon!) to R2 as it’s requested, without paying unnecessary cloud egress fees typically associated with moving large amounts of data. On top of addressing <a href="https://www.cloudflare.com/learning/cloud/what-is-vendor-lock-in/">vendor lock-in</a>, Sippy makes stressful, time-consuming migrations a thing of the past. All you need to do is replace the S3 endpoint in your application or attach your domain to your new R2 bucket and data will start getting copied over.</p>
    <div>
      <h2>How does it work?</h2>
      <a href="#how-does-it-work">
        
      </a>
    </div>
    <p>Sippy is an incremental migration service built directly into your R2 bucket. Migration-specific <a href="https://www.cloudflare.com/learning/cloud/what-are-data-egress-fees/">egress fees</a> are reduced by leveraging requests within the flow of your application where you’d already be paying egress fees to simultaneously copy objects to R2. Here is how it works:</p><p>When an object is requested from <a href="https://developers.cloudflare.com/r2/api/workers/">Workers</a>, <a href="https://developers.cloudflare.com/r2/api/s3/">S3 API</a>, or <a href="https://developers.cloudflare.com/r2/buckets/public-buckets/">public bucket</a>, it is served from your R2 bucket if it is found.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2QSl0q22LEdoOUp6BzkWhE/12f69030f3c5a4d53edf0a7d62af8b43/image2-16.png" />
            
            </figure><p>If the object is not found in R2, it will simultaneously be returned from your S3 bucket and copied to R2.</p><p>Note: Some large objects may take multiple requests to copy.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7ok5dS498ocJgCBRR1V5HU/683129c203b3343af7a4aa1c29e15b8a/image3-22.png" />
            
            </figure><p>That means after objects are copied, subsequent requests will be served from R2, and you’ll begin saving on egress fees immediately.</p>
    <div>
      <h2>Start incrementally migrating data from S3 to R2</h2>
      <a href="#start-incrementally-migrating-data-from-s3-to-r2">
        
      </a>
    </div>
    
    <div>
      <h3>Create an R2 bucket</h3>
      <a href="#create-an-r2-bucket">
        
      </a>
    </div>
    <p>To get started with incremental migration, you’ll first need to create an R2 bucket if you don’t already have one. To create a new R2 bucket from the Cloudflare dashboard:</p><ol><li><p>Log in to the <a href="https://dash.cloudflare.com/">Cloudflare dashboard</a> and select <b>R2</b>.</p></li><li><p>Select <b>Create bucket</b>.</p></li><li><p>Give your bucket a name and select <b>Create bucket</b>.</p></li></ol><p>​​To learn more about other ways to create R2 buckets refer to the documentation on <a href="https://developers.cloudflare.com/r2/buckets/create-buckets/">creating buckets</a>.</p>
    <div>
      <h3>Enable Sippy on your R2 bucket</h3>
      <a href="#enable-sippy-on-your-r2-bucket">
        
      </a>
    </div>
    <p>Next, you’ll enable Sippy for the R2 bucket you created. During the beta, you can do this by using the API. Here’s an example of how to enable Sippy for an R2 bucket with cURL:</p>
            <pre><code>curl -X PUT https://api.cloudflare.com/client/v4/accounts/{account_id}/r2/buckets/{bucket_name}/sippy \
--header "Authorization: Bearer &lt;API_TOKEN&gt;" \
--data '{"provider": "AWS", "bucket": "&lt;AWS_BUCKET_NAME&gt;", "zone": "&lt;AWS_REGION&gt;","key_id": "&lt;AWS_ACCESS_KEY_ID&gt;", "access_key":"&lt;AWS_SECRET_ACCESS_KEY&gt;", "r2_key_id": "&lt;R2_ACCESS_KEY_ID&gt;", "r2_access_key": "&lt;R2_SECRET_ACCESS_KEY&gt;"}'</code></pre>
            <p>For more information on getting started, please refer to the <a href="https://developers.cloudflare.com/r2/data-migration/sippy/">documentation</a>. Once enabled, requests to your bucket will now start copying data over from S3 if it’s not already present in your R2 bucket.</p>
    <div>
      <h3>Finish your migration with Super Slurper</h3>
      <a href="#finish-your-migration-with-super-slurper">
        
      </a>
    </div>
    <p>You can run your incremental migration for as long as you want, but eventually you may want to complete the migration to R2. To do this, you can pair Sippy with <a href="https://developers.cloudflare.com/r2/data-migration/super-slurper/">Super Slurper</a> to easily migrate your remaining data that hasn’t been accessed to R2.</p>
    <div>
      <h2>What’s next?</h2>
      <a href="#whats-next">
        
      </a>
    </div>
    <p>We’re excited about open beta, but it’s only the starting point. Next, we plan on making incremental migration configurable from the Cloudflare dashboard, complete with analytics that show you the progress of your migration and how much you are saving by not paying egress fees for objects that have been copied over so far.</p><p>If you are looking to start incrementally migrating your data to R2 and have any questions or feedback on what we should build next, we encourage you to join our <a href="https://discord.com/invite/cloudflaredev">Discord community</a> to share!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5OxNi1UZsYR9ITa7KbaQ11/753b4c01ddbed65d637c55b0b91a47de/Kfb7dwYUUzfrLKPH_ukrJRTvRlfl4E8Uy00vwEQPCTiW0IQ--fxpikjv1p0afm4A5J3JfVjQiOVjN3RMNeMcu3vhnz97pEmENCkNIuwdW_m-aW7ABfZnmUpJB_jh.png" />
            
            </figure><p></p> ]]></content:encoded>
            <category><![CDATA[Birthday Week]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Storage]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Connectivity Cloud]]></category>
            <category><![CDATA[R2]]></category>
            <guid isPermaLink="false">4Oc5iRahgLMh31qeAxVZPt</guid>
            <dc:creator>Phillip Jones</dc:creator>
            <dc:creator>Vlad Krasnov</dc:creator>
        </item>
        <item>
            <title><![CDATA[BoringTun, a userspace WireGuard implementation in Rust]]></title>
            <link>https://blog.cloudflare.com/boringtun-userspace-wireguard-rust/</link>
            <pubDate>Wed, 27 Mar 2019 13:43:27 GMT</pubDate>
            <description><![CDATA[ Today we are happy to release the source code of a project we’ve been working on for the past few months. It is called BoringTun, and is a userspace implementation of the WireGuard® protocol written in Rust. ]]></description>
            <content:encoded><![CDATA[ <p>Today we are happy to release the <a href="https://github.com/cloudflare/boringtun">source code</a> of a project we’ve been working on for the past few months. It is called BoringTun, and is a <a href="https://www.wireguard.com/xplatform/">userspace</a> implementation of the <a href="https://www.wireguard.com/">WireGuard®</a> protocol written in Rust.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/265ROm9gzfd6Fg7Qg5eL22/a321265e139e299f61ec368fb86578eb/boring-tun-logo.png" />
            
            </figure>
    <div>
      <h3>A Bit About WireGuard</h3>
      <a href="#a-bit-about-wireguard">
        
      </a>
    </div>
    <p>WireGuard is relatively new project that attempts to replace old VPN protocols, with a simple, fast, and safe protocol. Unlike legacy VPNs, WireGuard is built around the <a href="http://www.noiseprotocol.org/">Noise Protocol Framework</a> and relies only on a select few, modern, cryptographic primitives: X25519 for public key operations, <a href="/do-the-chacha-better-mobile-performance-with-cryptography/">ChaCha20-Poly1305</a> for authenticated encryption, and Blake2s for message authentication.</p><p>Like <a href="/http-3-from-root-to-tip/">QUIC</a>, WireGuard works over UDP, but its only goal is to securely encapsulate IP packets. As a result, it does not guarantee the delivery of packets, or that packets are delivered in the order they are sent.</p><p>The simplicity of the protocol means it is more robust than old, unmaintainable codebases, and can also be implemented relatively quickly. Despite its relatively young age, WireGuard is <a href="https://arstechnica.com/gadgets/2018/08/wireguard-vpn-review-fast-connections-amaze-but-windows-support-needs-to-happen/">quickly gaining in popularity</a>.</p>
    <div>
      <h3>Starting From Scratch</h3>
      <a href="#starting-from-scratch">
        
      </a>
    </div>
    <p>While evaluating the potential value WireGuard could provide us, we first considered the existing implementations. Currently, there are three usable implementations</p><ul><li><p>The WireGuard kernel module - written in C, it is tightly integrated with the Linux kernel, and is not usable outside of it. Due to its integration with the kernel it provides the best possible performance. It is licensed under the GPL-2.0 license.</p></li><li><p>wireguard-go - this is the only compliant userspace implementation of WireGuard. As its name suggests it is written in Go, a language that we love, and is licensed under the permissive MIT license.</p></li><li><p>TunSafe - written in C++, it does not implement the userspace protocol exactly, but rather a deviation of it. It supports several platforms, but by design it supports only a single peer. TunSafe uses the AGPL-1.0 license.</p></li></ul><p>Whereas we were looking for:</p><ul><li><p><a href="https://en.wikipedia.org/wiki/User_space">Userspace</a></p></li><li><p>Cross-platform - including Linux, Windows, macOS, iOS and Android</p></li><li><p>Fast</p></li></ul><p>Clearly we thought, only one of those fits the bill, and that is wireguard-go. However, benchmarks quickly showed that wireguard-go falls very short of the performance offered by the kernel module. This is because while the Go language is very good for writing servers, it is not so good for raw packet processing, which a VPN essentially does.</p>
    <div>
      <h3>Choosing <a href="https://www.rust-lang.org/">Rust</a></h3>
      <a href="#choosing">
        
      </a>
    </div>
    <p>After we decided to create a userspace WireGuard implementation, there was the small matter of choosing the right language. While C and C++ are both high performance, low level languages, <a href="/incident-report-on-memory-leak-caused-by-cloudflare-parser-bug/">recent history</a> has demonstrated that their memory model was too fragile for a modern cryptography and security-oriented project. Go was shown to be suboptimal for this use case by wireguard-go.</p><p>The obvious answer was Rust. Rust is a modern, safe language that is both as fast as C++ and is arguably safer than Go (it is memory safe and also imposes rules that allow for safer concurrency), while supporting a huge selection of <a href="https://forge.rust-lang.org/platform-support.html">platforms</a>. We also have some of the best Rust talent in the industry working at the company.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4JAfjcdBRke6buHmC9MV0h/a062037c4e17784546c6f974f9dac476/Screen-Shot-2019-03-26-at-4.13.07-PM-2.png" />
            
            </figure><p>In fact, another Rust implementation of WireGuard, wireguard-rs, exists. But wireguard-rs is very immature, and we strongly felt that it would benefit the WireGuard ecosystem if there was a completely independent implementation under a permissive license.</p><p>Thus BoringTun was born.</p><p>The name might sound a bit boring but there's a reason for it: BoringTun creates a tunnel by 'boring' it. And it’s a nod to Google’s <a href="https://boringssl.googlesource.com/boringssl/">BoringSSL</a> which strips the complexity out of OpenSSL. We think WireGuard has the opportunity to do the same for legacy VPN protocols like OpenVPN. And we hope BoringTun can be a valuable tool as part of that ecosystem.</p><p>WireGuard is an incredible tool and we believe it has a chance of being the defacto standard for VPN-like technologies going forward. We're adding our Rust implementation of WireGuard to the ecosystem and hope people find it useful.</p>
    <div>
      <h3>Next steps</h3>
      <a href="#next-steps">
        
      </a>
    </div>
    <p>BoringTun is currently under internal security review, and it is probably not fully ready to be used in mission critical tasks. We will fix issues as we find them, and we also welcome contributions from the community on the projects <a href="https://github.com/cloudflare/boringtun">Github page</a>. The project is licensed under the open source <a href="https://opensource.org/licenses/BSD-3-Clause">3-Clause BSD License</a>.</p><p>Note: WireGuard is a registered trademark of <a href="https://www.zx2c4.com/">Jason A. Donenfeld</a>.</p> ]]></content:encoded>
            <category><![CDATA[Rust]]></category>
            <category><![CDATA[Programming]]></category>
            <guid isPermaLink="false">4LsskKWd8AfwLuPW5Ej5VS</guid>
            <dc:creator>Vlad Krasnov</dc:creator>
        </item>
        <item>
            <title><![CDATA[Know your SCM_RIGHTS]]></title>
            <link>https://blog.cloudflare.com/know-your-scm_rights/</link>
            <pubDate>Thu, 29 Nov 2018 09:54:22 GMT</pubDate>
            <description><![CDATA[ As TLS 1.3 was ratified earlier this year, I was recollecting how we got started with it here at Cloudflare. We made the decision to be early adopters of TLS 1.3 a little over two years ago. It was a very important decision, and we took it very seriously. ]]></description>
            <content:encoded><![CDATA[ <p>As TLS 1.3 was ratified earlier <a href="/rfc-8446-aka-tls-1-3/">this year</a>, I was recollecting how we got started with it here at Cloudflare. We made the decision to be early adopters of <a href="/introducing-tls-1-3/">TLS 1.3</a> a little over two years ago. It was a very important decision, and we took it very seriously.</p><p>It is no secret that Cloudflare uses <a href="/end-of-the-road-for-cloudflare-nginx/">nginx</a> to handle user traffic. A little less known fact, is that we have several instances of nginx running. I won’t go into detail, but there is one instance whose job is to accept connections on port 443, and proxy them to another instance of nginx that actually handles the requests. It has pretty limited functionality otherwise. We fondly call it nginx-ssl.</p><p>Back then we were using OpenSSL for TLS and Crypto in nginx, but OpenSSL (and BoringSSL) had yet to announce a timeline for TLS 1.3 support, therefore we had to implement our own TLS 1.3 stack. Obviously we wanted an implementation that would not affect any customer or client that would not enable TLS 1.3. We also needed something that we could iterate on quickly, because the spec was very fluid back then, and also something that we can release frequently without worrying about the rest of the Cloudflare stack.</p><p>The obvious solution was to implement it on top of OpenSSL. The OpenSSL version we were using was 1.0.2, but not only were we looking ahead to replace it with version 1.1.0 or with <a href="/make-ssl-boring-again/">BoringSSL</a> (which we eventually did), it was so ingrained in our stack and so fragile that we wouldn’t be able to achieve our stated goals, without risking serious bugs.</p><p>Instead, Filippo Valsorda and Brendan McMillion suggested that the easier path would be to implement TLS 1.3 on top of the Go TLS library and make a Go replica of nginx-ssl (go-ssl). Go is very easy to iterate and prototype, with a powerful standard library, and we had a great pool of Go talent to use, so it made a lot of sense. Thus <a href="https://github.com/cloudflare/tls-tris">tls-tris</a> was born.</p><p>The question remained how would we have Go handle only TLS 1.3 while letting nginx handling all prior versions of TLS?</p><p>And herein lies the problem. Both TLS 1.3 and older versions of TLS communicate on port 443, and it is common knowledge that only one application can listen on a given TCP port, and that application is nginx, that would still handle the bulk of the TLS traffic. We could pipe all the TCP data into another connection in Go, effectively creating an additional proxy layer, but where is the fun in that? Also it seemed a little inefficient.</p>
    <div>
      <h2>Meet SCM_RIGHTS</h2>
      <a href="#meet-scm_rights">
        
      </a>
    </div>
    <p>So how do you make two different processes, written in two different programming languages, share the same TCP socket?</p><p>Fortunately, Linux (or rather UNIX) provides us with just the tool that we need. You can use UNIX-domain sockets to pass file descriptors between applications, and like everything else in UNIX connections are files.Looking at <code>man 7 unix</code> we see the following:</p>
            <pre><code>   Ancillary messages
       Ancillary  data  is  sent and received using sendmsg(2) and recvmsg(2).
       For historical reasons the ancillary message  types  listed  below  are
       specified with a SOL_SOCKET type even though they are AF_UNIX specific.
       To send them  set  the  cmsg_level  field  of  the  struct  cmsghdr  to
       SOL_SOCKET  and  the cmsg_type field to the type.  For more information
       see cmsg(3).

       SCM_RIGHTS
              Send or receive a set of  open  file  descriptors  from  another
              process.  The data portion contains an integer array of the file
              descriptors.  The passed file descriptors behave as though  they
              have been created with dup(2).</code></pre>
            <blockquote><p>Technically you do not send “file descriptors”. The “file descriptors” you handle in the code are simply indices into the processes' local file descriptor table, which in turn points into the OS' open file table, that finally points to the vnode representing the file. Thus the “file descriptor” observed by the other process will most likely have a different numeric value, despite pointing to the same file.</p></blockquote><p>We can also check <code>man 3 cmsg</code> as suggested, to find a handy example on how to use SCM_RIGHTS:</p>
            <pre><code>   struct msghdr msg = { 0 };
   struct cmsghdr *cmsg;
   int myfds[NUM_FD];  /* Contains the file descriptors to pass */
   int *fdptr;
   union {         /* Ancillary data buffer, wrapped in a union
                      in order to ensure it is suitably aligned */
       char buf[CMSG_SPACE(sizeof(myfds))];
       struct cmsghdr align;
   } u;

   msg.msg_control = u.buf;
   msg.msg_controllen = sizeof(u.buf);
   cmsg = CMSG_FIRSTHDR(&amp;msg);
   cmsg-&gt;cmsg_level = SOL_SOCKET;
   cmsg-&gt;cmsg_type = SCM_RIGHTS;
   cmsg-&gt;cmsg_len = CMSG_LEN(sizeof(int) * NUM_FD);
   fdptr = (int *) CMSG_DATA(cmsg);    /* Initialize the payload */
   memcpy(fdptr, myfds, NUM_FD * sizeof(int));</code></pre>
            <p>And that is what we decided to use. We let OpenSSL read the “Client Hello” message from an established TCP connection. If the “Client Hello” indicated TLS version 1.3, we would use SCM_RIGHTS to send it to the Go process. The Go process would in turn try to parse the rest of the “Client Hello”, if it were successful it would proceed with TLS 1.3 connection, and upon failure it would give the file descriptor back to OpenSSL, to handle regularly.</p><p>So how exactly do you implement something like that?</p><p>Since in our case we established that the C process will listen for TCP connections, our other process will have to listen on a UNIX socket, for connections C will want to forward.</p><p>For example in Go:</p>
            <pre><code>type scmListener struct {
	*net.UnixListener
}

type scmConn struct {
	*net.UnixConn
}

var path = "/tmp/scm_example.sock"

func listenSCM() (*scmListener, error) {
	syscall.Unlink(path)

	addr, err := net.ResolveUnixAddr("unix", path)
	if err != nil {
		return nil, err
	}

	ul, err := net.ListenUnix("unix", addr)
	if err != nil {
		return nil, err
	}

	err = os.Chmod(path, 0777)
	if err != nil {
		return nil, err
	}

	return &amp;scmListener{ul}, nil
}

func (l *scmListener) Accept() (*scmConn, error) {
	uc, err := l.AcceptUnix()
	if err != nil {
		return nil, err
	}
	return &amp;scmConn{uc}, nil
}</code></pre>
            <p>Then in the C process, for each connection we want to pass, we will connect to that socket first:</p>
            <pre><code>int connect_unix()
{
    struct sockaddr_un addr = {.sun_family = AF_UNIX,
                               .sun_path = "/tmp/scm_example.sock"};

    int unix_sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (unix_sock == -1)
        return -1;

    if (connect(unix_sock, (struct sockaddr *)&amp;addr, sizeof(addr)) == -1)
    {
        close(unix_sock);
        return -1;
    }

    return unix_sock;
}</code></pre>
            <p>To actually pass a file descriptor we utilize the example from <code>man 3 cmsg</code>:</p>
            <pre><code>int send_fd(int unix_sock, int fd)
{
    struct iovec iov = {.iov_base = ":)", // Must send at least one byte
                        .iov_len = 2};

    union {
        char buf[CMSG_SPACE(sizeof(fd))];
        struct cmsghdr align;
    } u;

    struct msghdr msg = {.msg_iov = &amp;iov,
                         .msg_iovlen = 1,
                         .msg_control = u.buf,
                         .msg_controllen = sizeof(u.buf)};

    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&amp;msg);
    *cmsg = (struct cmsghdr){.cmsg_level = SOL_SOCKET,
                             .cmsg_type = SCM_RIGHTS,
                             .cmsg_len = CMSG_LEN(sizeof(fd))};

    memcpy(CMSG_DATA(cmsg), &amp;fd, sizeof(fd));

    return sendmsg(unix_sock, &amp;msg, 0);
}</code></pre>
            <p>Then to receive the file descriptor in Go:</p>
            <pre><code>func (c *scmConn) ReadFD() (*os.File, error) {
	msg, oob := make([]byte, 2), make([]byte, 128)

	_, oobn, _, _, err := c.ReadMsgUnix(msg, oob)
	if err != nil {
		return nil, err
	}

	cmsgs, err := syscall.ParseSocketControlMessage(oob[0:oobn])
	if err != nil {
		return nil, err
	} else if len(cmsgs) != 1 {
		return nil, errors.New("invalid number of cmsgs received")
	}

	fds, err := syscall.ParseUnixRights(&amp;cmsgs[0])
	if err != nil {
		return nil, err
	} else if len(fds) != 1 {
		return nil, errors.New("invalid number of fds received")
	}

	fd := os.NewFile(uintptr(fds[0]), "")
	if fd == nil {
		return nil, errors.New("could not open fd")
	}

	return fd, nil
}</code></pre>
            
    <div>
      <h2>Rust</h2>
      <a href="#rust">
        
      </a>
    </div>
    <p>We can also do this in Rust, although the standard library in Rust does not yet support UNIX sockets, but it does let you address the C library via the <a href="https://rust-lang.github.io/libc/x86_64-unknown-linux-gnu/libc/">libc</a> crate. Warning, unsafe code ahead!</p><p>First we want to implement some UNIX socket functionality in Rust:</p>
            <pre><code>use libc::*;
use std::io::prelude::*;
use std::net::TcpStream;
use std::os::unix::io::FromRawFd;
use std::os::unix::io::RawFd;

fn errno_str() -&gt; String {
    let strerr = unsafe { strerror(*__error()) };
    let c_str = unsafe { std::ffi::CStr::from_ptr(strerr) };
    c_str.to_string_lossy().into_owned()
}

pub struct UNIXSocket {
    fd: RawFd,
}

pub struct UNIXConn {
    fd: RawFd,
}

impl Drop for UNIXSocket {
    fn drop(&amp;mut self) {
        unsafe { close(self.fd) };
    }
}

impl Drop for UNIXConn {
    fn drop(&amp;mut self) {
        unsafe { close(self.fd) };
    }
}

impl UNIXSocket {
    pub fn new() -&gt; Result&lt;UNIXSocket, String&gt; {
        match unsafe { socket(AF_UNIX, SOCK_STREAM, 0) } {
            -1 =&gt; Err(errno_str()),
            fd @ _ =&gt; Ok(UNIXSocket { fd }),
        }
    }

    pub fn bind(self, address: &amp;str) -&gt; Result&lt;UNIXSocket, String&gt; {
        assert!(address.len() &lt; 104);

        let mut addr = sockaddr_un {
            sun_len: std::mem::size_of::&lt;sockaddr_un&gt;() as u8,
            sun_family: AF_UNIX as u8,
            sun_path: [0; 104],
        };

        for (i, c) in address.chars().enumerate() {
            addr.sun_path[i] = c as i8;
        }

        match unsafe {
            unlink(&amp;addr.sun_path as *const i8);
            bind(
                self.fd,
                &amp;addr as *const sockaddr_un as *const sockaddr,
                std::mem::size_of::&lt;sockaddr_un&gt;() as u32,
            )
        } {
            -1 =&gt; Err(errno_str()),
            _ =&gt; Ok(self),
        }
    }

    pub fn listen(self) -&gt; Result&lt;UNIXSocket, String&gt; {
        match unsafe { listen(self.fd, 50) } {
            -1 =&gt; Err(errno_str()),
            _ =&gt; Ok(self),
        }
    }

    pub fn accept(&amp;self) -&gt; Result&lt;UNIXConn, String&gt; {
        match unsafe { accept(self.fd, std::ptr::null_mut(), std::ptr::null_mut()) } {
            -1 =&gt; Err(errno_str()),
            fd @ _ =&gt; Ok(UNIXConn { fd }),
        }
    }
}</code></pre>
            <p>And the code to extract the file desciptor:</p>
            <pre><code>#[repr(C)]
pub struct ScmCmsgHeader {
    cmsg_len: c_uint,
    cmsg_level: c_int,
    cmsg_type: c_int,
    fd: c_int,
}

impl UNIXConn {
    pub fn recv_fd(&amp;self) -&gt; Result&lt;RawFd, String&gt; {
        let mut iov = iovec {
            iov_base: std::ptr::null_mut(),
            iov_len: 0,
        };

        let mut scm = ScmCmsgHeader {
            cmsg_len: 0,
            cmsg_level: 0,
            cmsg_type: 0,
            fd: 0,
        };

        let mut mhdr = msghdr {
            msg_name: std::ptr::null_mut(),
            msg_namelen: 0,
            msg_iov: &amp;mut iov as *mut iovec,
            msg_iovlen: 1,
            msg_control: &amp;mut scm as *mut ScmCmsgHeader as *mut c_void,
            msg_controllen: std::mem::size_of::&lt;ScmCmsgHeader&gt;() as u32,
            msg_flags: 0,
        };

        let n = unsafe { recvmsg(self.fd, &amp;mut mhdr, 0) };

        if n == -1
            || scm.cmsg_len as usize != std::mem::size_of::&lt;ScmCmsgHeader&gt;()
            || scm.cmsg_level != SOL_SOCKET
            || scm.cmsg_type != SCM_RIGHTS
        {
            Err("Invalid SCM message".to_string())
        } else {
            Ok(scm.fd)
        }
    }
}</code></pre>
            
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>SCM_RIGHTS is a very powerful tool that can be used for many purposes. In our case we used to to introduce a new service in a non-obtrusive fashion. Other uses may be:</p><ul><li><p>A/B testing</p></li><li><p>Phasing out of an old C based service in favor of new Go or Rust one</p></li><li><p>Passing connections from a privileged process to an unprivileged one</p></li></ul><p>And more</p><p>You can find the full example <a href="https://github.com/vkrasnov/scm_sample">here</a>.</p> ]]></content:encoded>
            <category><![CDATA[TLS 1.3]]></category>
            <category><![CDATA[TLS]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Linux]]></category>
            <category><![CDATA[TCP]]></category>
            <category><![CDATA[OpenSSL]]></category>
            <category><![CDATA[Go]]></category>
            <category><![CDATA[NGINX]]></category>
            <guid isPermaLink="false">6FPYG7UeMVoKyzz6mA3tpB</guid>
            <dc:creator>Vlad Krasnov</dc:creator>
        </item>
        <item>
            <title><![CDATA[NEON is the new black: fast JPEG optimization on ARM server]]></title>
            <link>https://blog.cloudflare.com/neon-is-the-new-black/</link>
            <pubDate>Fri, 13 Apr 2018 16:38:47 GMT</pubDate>
            <description><![CDATA[ Cloudflare's jpegtran implementation was optimized for Intel CPUs. Now that we intend to integrate ARMv8 processors, new optimizations for those are required. ]]></description>
            <content:encoded><![CDATA[ <p>As engineers at Cloudflare quickly adapt our software stack to run on ARM, a few parts of our software stack have not been performing as well on ARM processors as they currently do on our Xeon® Silver 4116 CPUs. For the most part this is a matter of Intel specific optimizations some of which utilize SIMD or other special instructions.</p><p>One such example is the venerable jpegtran, one of the workhorses behind our Polish image optimization service.</p><p>A while ago I <a href="/doubling-the-speed-of-jpegtran/">optimized</a> our version of jpegtran for Intel processors. So when I ran a comparison on my <a href="/content/images/2015/10/print_poster_0025.jpg">test image</a>, I was expecting that the Xeon would outperform ARM:</p>
            <pre><code>vlad@xeon:~$ time  ./jpegtran -outfile /dev/null -progressive -optimise -copy none test.jpg

real    0m2.305s
user    0m2.059s
sys     0m0.252s</code></pre>
            
            <pre><code>vlad@arm:~$ time ./jpegtran -outfile /dev/null -progressive -optimise -copy none test.jpg

real    0m8.654s
user    0m8.433s
sys     0m0.225s</code></pre>
            <p>Ideally we want to have the ARM performing at or above 50% of the Xeon performance per core. This would make sure we have no performance regressions, and net performance gain, since the ARM CPUs have double the core count as our current 2 socket setup.</p><p>In this case, however, I was disappointed to discover an almost 4X slowdown.</p><p>Not one to despair, I figured out that applying the same optimizations I did for Intel would be trivial. Surely the NEON instructions map neatly to the SSE instructions I used before?</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3P8ertRFNQ4EZJnWHFTahh/c5b5e7013434c25e9d7816d7556e4d08/2535574361_30730d9a7b_o_d.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/vizzzual-dot-com/2535574361/in/photolist-4S4tVk-8yutL-ge8Q-5fXXRQ-wJcRY-yrvgf-9vXGvq-hGx4N-4NZ4L-5cjA7-iJrnwJ-7VXhaz-866BCb-auGuG-68BjeT-92L4-9rXsu-3Pfaz-5GZs6n-oAKSY-LhdKa-7BLZ96-VRGZ2H-ofm5ZJ-8xC1bv-5DePNQ-ZouKg-afu4r-49ThBC-7VyeQT-qfr6P3-4zpUM8-hgPbs-naTubk-S7khvM-6hTftH-ByLAg-9sNftz-8G4os2-zsq8-oBEf6L-5y1nxR-7aTZfD-9fYMH-wJcnp-8yhgk-wJcDS-qsPXVn-kbW5A6-bPx3gX">image</a> by <a href="https://www.flickr.com/photos/vizzzual-dot-com/">viZZZual.com</a></p>
    <div>
      <h3>What is NEON</h3>
      <a href="#what-is-neon">
        
      </a>
    </div>
    <p>NEON is the ARMv8 version of SIMD, Single Instruction Multiple Data instruction set, where a single operation performs (generally) the same operation on several operands.</p><p>NEON operates on 32 dedicated 128-bit registers, similarly to Intel SSE. It can perform operations on 32-bit and 64-bit floating point numbers, or 8-bit, 16-bit, 32-bit and 64-bit signed or unsigned integers.</p><p>As with <a href="https://www.cloudflare.com/learning/access-management/security-service-edge-sse/">SSE</a> you can program either in the assembly language, or in C using intrinsics. The intrinsics are usually easier to use, and depending on the application and the compiler can provide better performance, however intrinsics based code tends to be quite verbose.</p><p>If you opt to use the NEON intrinsics you have to include <code>&lt;arm_neon.h&gt;</code>. While SSE intrinsic use __m128i for all SIMD integer operations, the intrinsics for NEON have distinct type for each integer and float width. For example operations on signed 16-bit integers use the int16x8_t type, which we are going to use. Similarly there is a uint16x8_t type for unsigned integer, as well as int8x16_t, int32x4_t and int64x2_t and their uint derivatives, that are self explanatory.</p>
    <div>
      <h3>Getting started</h3>
      <a href="#getting-started">
        
      </a>
    </div>
    <p>Running perf tells me that the same two culprits are responsible for most of the CPU time spent:</p>
            <pre><code>perf record ./jpegtran -outfile /dev/null -progressive -optimise -copy none test.jpeg
perf report
  71.24%  lt-jpegtran  libjpeg.so.9.1.0   [.] encode_mcu_AC_refine
  15.24%  lt-jpegtran  libjpeg.so.9.1.0   [.] encode_mcu_AC_first</code></pre>
            <p>Aha, <code>encode_mcu_AC_refine</code> and <code>encode_mcu_AC_first</code>, my old nemeses!</p>
    <div>
      <h3>The straightforward approach</h3>
      <a href="#the-straightforward-approach">
        
      </a>
    </div>
    
    <div>
      <h4>encode_mcu_AC_refine</h4>
      <a href="#encode_mcu_ac_refine">
        
      </a>
    </div>
    <p>Let's recoup the optimizations we applied to <code>encode_mcu_AC_refine</code> previously. The function has two loops, with the heavier loop performing the following operation:</p>
            <pre><code>for (k = cinfo-&gt;Ss; k &lt;= Se; k++) {
  temp = (*block)[natural_order[k]];
  if (temp &lt; 0)
    temp = -temp;      /* temp is abs value of input */
  temp &gt;&gt;= Al;         /* apply the point transform */
  absvalues[k] = temp; /* save abs value for main pass */
  if (temp == 1)
    EOB = k;           /* EOB = index of last newly-nonzero coef */
}</code></pre>
            <p>And the SSE solution to this problem was:</p>
            <pre><code>__m128i x1 = _mm_setzero_si128(); // Load 8 16-bit values sequentially
x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+0]], 0);
x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+1]], 1);
x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+2]], 2);
x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+3]], 3);
x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+4]], 4);
x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+5]], 5);
x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+6]], 6);
x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+7]], 7);

x1 = _mm_abs_epi16(x1);       // Get absolute value of 16-bit integers
x1 = _mm_srli_epi16(x1, Al);  // &gt;&gt; 16-bit integers by Al bits

_mm_storeu_si128((__m128i*)&amp;absvalues[k], x1);   // Store

x1 = _mm_cmpeq_epi16(x1, _mm_set1_epi16(1));     // Compare to 1
unsigned int idx = _mm_movemask_epi8(x1);        // Extract byte mask
EOB = idx? k + 16 - __builtin_clz(idx)/2 : EOB;  // Compute index</code></pre>
            <p>For the most part the transition to NEON is indeed straightforward.</p><p>To initialize a register to all zeros, we can use the <code>vdupq_n_s16</code> intrinsic, that duplicates a given value across all lanes of a register. The insertions are performed with the <code>vsetq_lane_s16</code> intrinsic. Use <code>vabsq_s16</code> to get the absolute values.</p><p>The shift right instruction made me pause for a while. I simply couldn't find an instruction that can shift right by a non constant integer value. It doesn't exist. However the solution is very simple, you shift left by a negative amount! The intrinsic for that is <code>vshlq_s16</code>.</p><blockquote><p>The absence of a right shift instruction is no coincidence. Unlike the x86 instruction set, that can theoretically support arbitrarily long instructions, and thus don't have to think twice before adding a new instruction, no matter how specialized or redundant it is, ARMv8 instruction set can only support 32-bit long instructions, and have a very limited opcode space. For this reason the instruction set is much more concise, and many instructions are in fact aliases to other instruction. Even the most basic MOV instruction is an alias for ORR (binary or). That means that programming for ARM and NEON sometimes requires greater creativity.</p></blockquote><p>The final step of the loop, is comparing each element to 1, then getting the mask. Comparing for equality is performed with <code>vceqq_s16</code>. But again there is no operation to extract the mask. That is a problem. However, instead of getting a bitmask, it is possible to extract a whole byte from every lane into a 64-bit value, by first applying <code>vuzp1q_u8</code> to the comparison result. <code>vuzp1q_u8</code> interleaves the even indexed bytes of two vectors (whereas <code>vuzp2q_u8</code> interleaves the odd indexes). So the solution would look something like that:</p>
            <pre><code>int16x8_t zero = vdupq_n_s16(0);
int16x8_t al_neon = vdupq_n_s16(-Al);
int16x8_t x0 = zero;
int16x8_t x1 = zero;

// Load 8 16-bit values sequentially
x1 = vsetq_lane_s16((*block)[natural_order[k+0]], x1, 0);
// Interleave the loads to compensate for latency
x0 = vsetq_lane_s16((*block)[natural_order[k+1]], x0, 1);
x1 = vsetq_lane_s16((*block)[natural_order[k+2]], x1, 2);
x0 = vsetq_lane_s16((*block)[natural_order[k+3]], x0, 3);
x1 = vsetq_lane_s16((*block)[natural_order[k+4]], x1, 4);
x0 = vsetq_lane_s16((*block)[natural_order[k+5]], x0, 5);
x1 = vsetq_lane_s16((*block)[natural_order[k+6]], x1, 6);
x0 = vsetq_lane_s16((*block)[natural_order[k+7]], x0, 7);
int16x8_t x = vorrq_s16(x1, x0);

x = vabsq_s16(x);            // Get absolute value of 16-bit integers
x = vshlq_s16(x, al_neon);   // &gt;&gt; 16-bit integers by Al bits

vst1q_s16(&amp;absvalues[k], x); // Store
uint8x16_t is_one = vreinterpretq_u8_u16(vceqq_s16(x, one));  // Compare to 1
is_one = vuzp1q_u8(is_one, is_one);  // Compact the compare result into 64 bits

uint64_t idx = vgetq_lane_u64(vreinterpretq_u64_u8(is_one), 0); // Extract
EOB = idx ? k + 8 - __builtin_clzl(idx)/8 : EOB;                // Get the index</code></pre>
            <p>Note the intrinsics for explicit type casts. They don't actually emit any instructions, since regardless of the type the operands always occupy the same registers.</p><p>On to the second loop:</p>
            <pre><code>if ((temp = absvalues[k]) == 0) {
  r++;
  continue;
}</code></pre>
            <p>The SSE solution was:</p>
            <pre><code>__m128i t = _mm_loadu_si128((__m128i*)&amp;absvalues[k]);
t = _mm_cmpeq_epi16(t, _mm_setzero_si128()); // Compare to 0
int idx = _mm_movemask_epi8(t);              // Extract byte mask
if (idx == 0xffff) {                         // Skip all zeros
  r += 8;
  k += 8;
  continue;
} else {                                     // Skip up to the first nonzero
  int skip = __builtin_ctz(~idx)/2;
  r += skip;
  k += skip;
  if (k&gt;Se) break;      // Stop if gone too far
}
temp = absvalues[k];    // Load the next nonzero value</code></pre>
            <p>But we already know that there is no way to extract the byte mask. Instead of using NEON I chose to simply skip four zero values at a time, using 64-bit integers, like so:</p>
            <pre><code>uint64_t tt, *t = (uint64_t*)&amp;absvalues[k];
if ( (tt = *t) == 0) while ( (tt = *++t) == 0); // Skip while all zeroes
int skip = __builtin_ctzl(tt)/16 + ((int64_t)t - 
           (int64_t)&amp;absvalues[k])/2;           // Get index of next nonzero
k += skip;
r += skip;
temp = absvalues[k];</code></pre>
            <p>How fast are we now?</p>
            <pre><code>vlad@arm:~$ time ./jpegtran -outfile /dev/null -progressive -optimise -copy none test.jpg

real    0m4.008s
user    0m3.770s
sys     0m0.241s</code></pre>
            <p>Wow, that is incredible. Over 2X speedup!</p>
    <div>
      <h4>encode_mcu_AC_first</h4>
      <a href="#encode_mcu_ac_first">
        
      </a>
    </div>
    <p>The other function is quite similar, but the logic slightly differs on the first pass:</p>
            <pre><code>temp = (*block)[natural_order[k]];
if (temp &lt; 0) {
  temp = -temp;             // Temp is abs value of input
  temp &gt;&gt;= Al;              // Apply the point transform
  temp2 = ~temp;
} else {
  temp &gt;&gt;= Al;              // Apply the point transform
  temp2 = temp;
}
t1[k] = temp;
t2[k] = temp2;</code></pre>
            <p>Here it is required to assign the absolute value of temp to <code>t1[k]</code>, and its inverse to <code>t2[k]</code> if temp is negative, otherwise <code>t2[k]</code> assigned the same value as <code>t1[k]</code>.</p><p>To get the inverse of a value, we use the <code>vmvnq_s16</code> intrinsic, to check if the values are negative we need to compare with zero using the <code>vcgezq_s16</code> and finally selecting based on the mask using <code>vbslq_s16</code>.</p>
            <pre><code>int16x8_t zero = vdupq_n_s16(0);
int16x8_t al_neon = vdupq_n_s16(-Al);

int16x8_t x0 = zero;
int16x8_t x1 = zero;

// Load 8 16-bit values sequentially
x1 = vsetq_lane_s16((*block)[natural_order[k+0]], x1, 0);
// Interleave the loads to compensate for latency
x0 = vsetq_lane_s16((*block)[natural_order[k+1]], x0, 1);
x1 = vsetq_lane_s16((*block)[natural_order[k+2]], x1, 2);
x0 = vsetq_lane_s16((*block)[natural_order[k+3]], x0, 3);
x1 = vsetq_lane_s16((*block)[natural_order[k+4]], x1, 4);
x0 = vsetq_lane_s16((*block)[natural_order[k+5]], x0, 5);
x1 = vsetq_lane_s16((*block)[natural_order[k+6]], x1, 6);
x0 = vsetq_lane_s16((*block)[natural_order[k+7]], x0, 7);
int16x8_t x = vorrq_s16(x1, x0);

uint16x8_t is_positive = vcgezq_s16(x); // Get positive mask

x = vabsq_s16(x);                 // Get absolute value of 16-bit integers
x = vshlq_s16(x, al_neon);        // &gt;&gt; 16-bit integers by Al bits
int16x8_t n = vmvnq_s16(x);       // Binary inverse
n = vbslq_s16(is_positive, x, n); // Select based on positive mask

vst1q_s16(&amp;t1[k], x); // Store
vst1q_s16(&amp;t2[k], n);</code></pre>
            <p>And the moment of truth:</p>
            <pre><code>vlad@arm:~$ time ./jpegtran -outfile /dev/null -progressive -optimise -copy none test.jpg

real    0m3.480s
user    0m3.243s
sys     0m0.241s</code></pre>
            <p>Overall 2.5X speedup from the original C implementation, but still 1.5X slower than Xeon.</p>
    <div>
      <h3>Batch benchmark</h3>
      <a href="#batch-benchmark">
        
      </a>
    </div>
    <p>While the improvement for the single image was impressive, it is not necessarily representative of all jpeg files. To understand the impact on overall performance I ran jpegtran over a set of 34,159 actual images from one of our caches. The total size of those images was 3,325,253KB. The total size after jpegtran was 3,067,753KB, or 8% improvement on average.</p><p>Using one thread, the Intel Xeon managed to process all those images in 14 minutes and 43 seconds. The original jpegtran on our ARM server took 29 minutes and 34 seconds. The improved jpegtran took only 13 minutes and 52 seconds, slightly outperforming even the Xeon processor, despite losing on the test image.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/koIIgyZHWMqH2zFQlXE4Q/9f9de84ab448b9a0e75568d8c71d82d5/jpegtran.png" />
            
            </figure>
    <div>
      <h3>Going deeper</h3>
      <a href="#going-deeper">
        
      </a>
    </div>
    <p>3.48 seconds, down from 8.654 represents a respectful 2.5X speedup.</p><p>It definitely meets the goal of being at least 50% as fast as Xeon, and it is faster in the batch benchmark, but it still feels like it is slower than it could be.</p><p>While going over the ARMv8 NEON instruction set, I found several unique instructions, that have no equivalent in SSE.</p><p>The first such instruction is <code>TBL</code>. It works as a lookup table, that can lookup 8 or 16 bytes from one to four consecutive registers. In the single register variant it is similar to the <code>pshufb</code> SSE instruction. In the four register variant, however, it can simultaneously lookup 16 bytes in a 64 byte table! What sorcery is that?</p><p>The intrinsic to use the 4 register variant is <code>vqtbl4q_u8</code>. Interestingly there is an instruction that can lookup 64 bytes in AVX-512, but we don't want to <a href="/on-the-dangers-of-intels-frequency-scaling/">use that</a>.</p><p>The next interesting thing I found, are instructions that can load or store and de/interleave data at the same time. They can load or store up to four registers simultaneously, while de/interleaving two, three or even four elements, of any supported width. The specifics are well presented in <a href="https://community.arm.com/processors/b/blog/posts/coding-for-neon---part-1-load-and-stores">here</a>. The load intrinsics used are of the form: <code>vldNq_uW</code>, where N can be 1,2,3,4 to indicate the interleave factor and W can be 8, 16, 32 or 64. Similarly <code>vldNq_sW</code> is used for signed types.</p><p>Finally very interesting instructions are the shift left/right and insert <code>SLI</code> and <code>SRI</code>. What they do is they shift the elements left or right, like a regular shift would, however instead of shifting in zero bits, the zeros are replaced with the original bits of the destination register! An intrinsic for that would look like <code>vsliq_n_u16</code> or <code>vsriq_n_u32</code>.</p>
    <div>
      <h4>Applying the new instructions</h4>
      <a href="#applying-the-new-instructions">
        
      </a>
    </div>
    <p>It might not be visible at first how those new instruction can help. Since I didn't have much time to dig into libjpeg or the jpeg spec, I had to resolve to heuristics.</p><p>From a quick look it became apparent that <code>*block</code> is defined as an array of 64 16-bit values. <code>natural_order</code> is an array of 32-bit integers that varies in length depending on the real block size, but is always padded with 16 entries. Also, despite the fact that it uses integers, the values are indexes in the range [0..63].</p><p>Another interesting observation is that blocks of size 64 are the most common by far for both <code>encode_mcu_AC_refine</code> and <code>encode_mcu_AC_first</code>. And it always makes sense to optimize for the most common case.</p><p>So essentially what we have here, is a 64 entry lookup table <code>*block</code> that uses <code>natural_order</code> as indices. Hmm, 64 entry lookup table, where did I see that before? Of course, the <code>TBL</code> instruction. Although <code>TBL</code> looks up bytes, and we need to lookup shorts, it is easy to do, since NEON lets us load and deinterleave the short into bytes in a single instruction using <code>LD2</code>, then we can use two lookups for each byte individually, and finally interleave again with <code>ZIP1</code> and <code>ZIP2</code>. Similarly despite the fact that the indices are integers, and we only need the least significant byte of each, we can use <code>LD4</code> to deinterleave them into bytes (the kosher way of course would be to rewrite the library to use bytes, but I wanted to avoid big changes).</p><p>After the data loading step is done, the point transforms for both functions remain the same, but in the end, to get a single bitmask for all 64 values we can use <code>SLI</code> and <code>SRI</code> to intelligently align the bits such that only one bit of each comparison mask remains, using <code>TBL</code> again to combine them.</p><p>For whatever reason, the compiler in that case produces somewhat suboptimal code, so I had to revert to assembly language for this specific optimization.</p><p>The code for <code>encode_mcu_AC_refine</code>:</p>
            <pre><code>    # Load and deintreleave the block
    ld2 {v0.16b - v1.16b}, [x0], 32
    ld2 {v16.16b - v17.16b}, [x0], 32
    ld2 {v18.16b - v19.16b}, [x0], 32
    ld2 {v20.16b - v21.16b}, [x0]

    mov v4.16b, v1.16b
    mov v5.16b, v17.16b
    mov v6.16b, v19.16b
    mov v7.16b, v21.16b
    mov v1.16b, v16.16b
    mov v2.16b, v18.16b
    mov v3.16b, v20.16b
    # Load the order 
    ld4 {v16.16b - v19.16b}, [x1], 64
    ld4 {v17.16b - v20.16b}, [x1], 64
    ld4 {v18.16b - v21.16b}, [x1], 64
    ld4 {v19.16b - v22.16b}, [x1]
    # Table lookup, LSB and MSB independently
    tbl v20.16b, {v0.16b - v3.16b}, v16.16b
    tbl v16.16b, {v4.16b - v7.16b}, v16.16b
    tbl v21.16b, {v0.16b - v3.16b}, v17.16b
    tbl v17.16b, {v4.16b - v7.16b}, v17.16b
    tbl v22.16b, {v0.16b - v3.16b}, v18.16b
    tbl v18.16b, {v4.16b - v7.16b}, v18.16b
    tbl v23.16b, {v0.16b - v3.16b}, v19.16b
    tbl v19.16b, {v4.16b - v7.16b}, v19.16b
    # Interleave MSB and LSB back
    zip1 v0.16b, v20.16b, v16.16b
    zip2 v1.16b, v20.16b, v16.16b
    zip1 v2.16b, v21.16b, v17.16b
    zip2 v3.16b, v21.16b, v17.16b
    zip1 v4.16b, v22.16b, v18.16b
    zip2 v5.16b, v22.16b, v18.16b
    zip1 v6.16b, v23.16b, v19.16b
    zip2 v7.16b, v23.16b, v19.16b
    # -Al
    neg w3, w3
    dup v16.8h, w3
    # Absolute then shift by Al
    abs v0.8h, v0.8h
    sshl v0.8h, v0.8h, v16.8h
    abs v1.8h, v1.8h
    sshl v1.8h, v1.8h, v16.8h
    abs v2.8h, v2.8h
    sshl v2.8h, v2.8h, v16.8h
    abs v3.8h, v3.8h
    sshl v3.8h, v3.8h, v16.8h
    abs v4.8h, v4.8h
    sshl v4.8h, v4.8h, v16.8h
    abs v5.8h, v5.8h
    sshl v5.8h, v5.8h, v16.8h
    abs v6.8h, v6.8h
    sshl v6.8h, v6.8h, v16.8h
    abs v7.8h, v7.8h
    sshl v7.8h, v7.8h, v16.8h
    # Store
    st1 {v0.16b - v3.16b}, [x2], 64
    st1 {v4.16b - v7.16b}, [x2]
    # Constant 1
    movi v16.8h, 0x1
    # Compare with 0 for zero mask
    cmeq v17.8h, v0.8h, #0
    cmeq v18.8h, v1.8h, #0
    cmeq v19.8h, v2.8h, #0
    cmeq v20.8h, v3.8h, #0
    cmeq v21.8h, v4.8h, #0
    cmeq v22.8h, v5.8h, #0
    cmeq v23.8h, v6.8h, #0
    cmeq v24.8h, v7.8h, #0
    # Compare with 1 for EOB mask
    cmeq v0.8h, v0.8h, v16.8h
    cmeq v1.8h, v1.8h, v16.8h
    cmeq v2.8h, v2.8h, v16.8h
    cmeq v3.8h, v3.8h, v16.8h
    cmeq v4.8h, v4.8h, v16.8h
    cmeq v5.8h, v5.8h, v16.8h
    cmeq v6.8h, v6.8h, v16.8h
    cmeq v7.8h, v7.8h, v16.8h
    # For both masks -&gt; keep only one byte for each comparison
    uzp1 v0.16b, v0.16b, v1.16b
    uzp1 v1.16b, v2.16b, v3.16b
    uzp1 v2.16b, v4.16b, v5.16b
    uzp1 v3.16b, v6.16b, v7.16b

    uzp1 v17.16b, v17.16b, v18.16b
    uzp1 v18.16b, v19.16b, v20.16b
    uzp1 v19.16b, v21.16b, v22.16b
    uzp1 v20.16b, v23.16b, v24.16b
    # Shift left and insert (int16) to get a single bit from even to odd bytes
    sli v0.8h, v0.8h, 15
    sli v1.8h, v1.8h, 15
    sli v2.8h, v2.8h, 15
    sli v3.8h, v3.8h, 15

    sli v17.8h, v17.8h, 15
    sli v18.8h, v18.8h, 15
    sli v19.8h, v19.8h, 15
    sli v20.8h, v20.8h, 15
    # Shift right and insert (int32) to get two bits from off to even indices
    sri v0.4s, v0.4s, 18
    sri v1.4s, v1.4s, 18
    sri v2.4s, v2.4s, 18
    sri v3.4s, v3.4s, 18

    sri v17.4s, v17.4s, 18
    sri v18.4s, v18.4s, 18
    sri v19.4s, v19.4s, 18
    sri v20.4s, v20.4s, 18
    # Regular shift right to align the 4 bits at the bottom of each int64
    ushr v0.2d, v0.2d, 12
    ushr v1.2d, v1.2d, 12
    ushr v2.2d, v2.2d, 12
    ushr v3.2d, v3.2d, 12

    ushr v17.2d, v17.2d, 12
    ushr v18.2d, v18.2d, 12
    ushr v19.2d, v19.2d, 12
    ushr v20.2d, v20.2d, 12
    # Shift left and insert (int64) to combine all 8 bits into one byte
    sli v0.2d, v0.2d, 36
    sli v1.2d, v1.2d, 36
    sli v2.2d, v2.2d, 36
    sli v3.2d, v3.2d, 36

    sli v17.2d, v17.2d, 36
    sli v18.2d, v18.2d, 36
    sli v19.2d, v19.2d, 36
    sli v20.2d, v20.2d, 36
    # Combine all the byte mask insto a bit 64-bit mask for EOB and zero masks
    ldr d4, .shuf_mask
    tbl v5.8b, {v0.16b - v3.16b}, v4.8b
    tbl v6.8b, {v17.16b - v20.16b}, v4.8b
    # Extract lanes
    mov x0, v5.d[0]
    mov x1, v6.d[0]
    # Compute EOB
    rbit x0, x0
    clz x0, x0
    mov x2, 64
    sub x0, x2, x0
    # Not of zero mask (so 1 bits indecates non-zeroes)
    mvn x1, x1
    ret</code></pre>
            <p>If you look carefully at the code, you will see, that I decided that while generating the mask to find EOB is useful, I can use the same method to generate the mask for zero values, and then I can find the next nonzero value, and zero runlength this way:</p>
            <pre><code>uint64_t skip =__builtin_clzl(zero_mask &lt;&lt; k);
r += skip;
k += skip;</code></pre>
            <p>Similarly for <code>encode_mcu_AC_first</code>:</p>
            <pre><code>    # Load the block
    ld2 {v0.16b - v1.16b}, [x0], 32
    ld2 {v16.16b - v17.16b}, [x0], 32
    ld2 {v18.16b - v19.16b}, [x0], 32
    ld2 {v20.16b - v21.16b}, [x0]

    mov v4.16b, v1.16b
    mov v5.16b, v17.16b
    mov v6.16b, v19.16b
    mov v7.16b, v21.16b
    mov v1.16b, v16.16b
    mov v2.16b, v18.16b
    mov v3.16b, v20.16b

    # Load the order 
    ld4 {v16.16b - v19.16b}, [x1], 64
    ld4 {v17.16b - v20.16b}, [x1], 64
    ld4 {v18.16b - v21.16b}, [x1], 64
    ld4 {v19.16b - v22.16b}, [x1]
    # Table lookup, LSB and MSB independently
    tbl v20.16b, {v0.16b - v3.16b}, v16.16b
    tbl v16.16b, {v4.16b - v7.16b}, v16.16b
    tbl v21.16b, {v0.16b - v3.16b}, v17.16b
    tbl v17.16b, {v4.16b - v7.16b}, v17.16b
    tbl v22.16b, {v0.16b - v3.16b}, v18.16b
    tbl v18.16b, {v4.16b - v7.16b}, v18.16b
    tbl v23.16b, {v0.16b - v3.16b}, v19.16b
    tbl v19.16b, {v4.16b - v7.16b}, v19.16b
    # Interleave MSB and LSB back
    zip1 v0.16b, v20.16b, v16.16b
    zip2 v1.16b, v20.16b, v16.16b
    zip1 v2.16b, v21.16b, v17.16b
    zip2 v3.16b, v21.16b, v17.16b
    zip1 v4.16b, v22.16b, v18.16b
    zip2 v5.16b, v22.16b, v18.16b
    zip1 v6.16b, v23.16b, v19.16b
    zip2 v7.16b, v23.16b, v19.16b
    # -Al
    neg w4, w4
    dup v24.8h, w4
    # Compare with 0 to get negative mask
    cmge v16.8h, v0.8h, #0
    # Absolute value and shift by Al
    abs v0.8h, v0.8h
    sshl v0.8h, v0.8h, v24.8h
    cmge v17.8h, v1.8h, #0
    abs v1.8h, v1.8h
    sshl v1.8h, v1.8h, v24.8h
    cmge v18.8h, v2.8h, #0
    abs v2.8h, v2.8h
    sshl v2.8h, v2.8h, v24.8h
    cmge v19.8h, v3.8h, #0
    abs v3.8h, v3.8h
    sshl v3.8h, v3.8h, v24.8h
    cmge v20.8h, v4.8h, #0
    abs v4.8h, v4.8h
    sshl v4.8h, v4.8h, v24.8h
    cmge v21.8h, v5.8h, #0
    abs v5.8h, v5.8h
    sshl v5.8h, v5.8h, v24.8h
    cmge v22.8h, v6.8h, #0
    abs v6.8h, v6.8h
    sshl v6.8h, v6.8h, v24.8h
    cmge v23.8h, v7.8h, #0
    abs v7.8h, v7.8h
    sshl v7.8h, v7.8h, v24.8h
    # ~
    mvn v24.16b, v0.16b
    mvn v25.16b, v1.16b
    mvn v26.16b, v2.16b
    mvn v27.16b, v3.16b
    mvn v28.16b, v4.16b
    mvn v29.16b, v5.16b
    mvn v30.16b, v6.16b
    mvn v31.16b, v7.16b
    # Select
    bsl v16.16b, v0.16b, v24.16b
    bsl v17.16b, v1.16b, v25.16b
    bsl v18.16b, v2.16b, v26.16b
    bsl v19.16b, v3.16b, v27.16b
    bsl v20.16b, v4.16b, v28.16b
    bsl v21.16b, v5.16b, v29.16b
    bsl v22.16b, v6.16b, v30.16b
    bsl v23.16b, v7.16b, v31.16b
    # Store t1
    st1 {v0.16b - v3.16b}, [x2], 64
    st1 {v4.16b - v7.16b}, [x2]
    # Store t2
    st1 {v16.16b - v19.16b}, [x3], 64
    st1 {v20.16b - v23.16b}, [x3]
    # Compute zero mask like before
    cmeq v17.8h, v0.8h, #0
    cmeq v18.8h, v1.8h, #0
    cmeq v19.8h, v2.8h, #0
    cmeq v20.8h, v3.8h, #0
    cmeq v21.8h, v4.8h, #0
    cmeq v22.8h, v5.8h, #0
    cmeq v23.8h, v6.8h, #0
    cmeq v24.8h, v7.8h, #0

    uzp1 v17.16b, v17.16b, v18.16b
    uzp1 v18.16b, v19.16b, v20.16b
    uzp1 v19.16b, v21.16b, v22.16b
    uzp1 v20.16b, v23.16b, v24.16b

    sli v17.8h, v17.8h, 15
    sli v18.8h, v18.8h, 15
    sli v19.8h, v19.8h, 15
    sli v20.8h, v20.8h, 15

    sri v17.4s, v17.4s, 18
    sri v18.4s, v18.4s, 18
    sri v19.4s, v19.4s, 18
    sri v20.4s, v20.4s, 18

    ushr v17.2d, v17.2d, 12
    ushr v18.2d, v18.2d, 12
    ushr v19.2d, v19.2d, 12
    ushr v20.2d, v20.2d, 12

    sli v17.2d, v17.2d, 36
    sli v18.2d, v18.2d, 36
    sli v19.2d, v19.2d, 36
    sli v20.2d, v20.2d, 36

    ldr d4, .shuf_mask
    tbl v6.8b, {v17.16b - v20.16b}, v4.8b

    mov x0, v6.d[0]
    mvn x0, x0
    ret</code></pre>
            
    <div>
      <h2>Final results and power</h2>
      <a href="#final-results-and-power">
        
      </a>
    </div>
    <p>The final version of our jpegtran managed to reduce the test image in 2.756 seconds. Or an extra 1.26X speedup, that gets it incredibly close to the performance of the Xeon on that image. As a bonus batch performance also improved!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4kEvqeb1Ddf7x4WbeyjEx8/f8752cb3a6e2c084ede51509d9e7ca15/jpegtran-asm-1.png" />
            
            </figure><p>Another favorite part of mine, working with the Qualcomm Centriq CPU is the ability to take power readings, and be pleasantly surprised every time.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2j5DWevxhY9nVnAW2Z7g31/fa34949f6f797487efbfbf9da646d20b/jpegtran-power-1.png" />
            
            </figure><p>With the new implementation Centriq outperforms the Xeon at batch reduction for every number of workers. We usually run Polish with four workers, for which Centriq is now 1.3 times faster while also 6.5 times more power efficient.</p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>It is evident that the Qualcomm Centriq is a powerful processor, that definitely provides a good bang for a buck. However, years of Intel leadership in the server and desktop space mean that a lot of software is better optimized for Intel processors.</p><p>For the most part writing optimizations for ARMv8 is not difficult, and we will be adjusting our software as needed, and publishing our efforts as we go.</p><p>You can find the updated code on our <a href="https://github.com/cloudflare/jpegtran">Github</a> page.</p>
    <div>
      <h3>Useful resources</h3>
      <a href="#useful-resources">
        
      </a>
    </div>
    <ul><li><p><a href="https://developer.arm.com/docs/100069/latest">Arm Compiler armasm User Guide</a></p></li><li><p><a href="http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf">Procedure Call Standard for the ARM 64-bit Architecture</a></p></li><li><p><a href="http://infocenter.arm.com/help/topic/com.arm.doc.ihi0073a/IHI0073A_arm_neon_intrinsics_ref.pdf">ARM NEON Intrinsics Reference</a></p></li><li><p><a href="https://community.arm.com/processors/b/blog/posts/coding-for-neon---part-1-load-and-stores">Coding for NEON</a></p></li></ul> ]]></content:encoded>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Cloudflare Polish]]></category>
            <guid isPermaLink="false">3a8OlLzKyPgsfQg8iON6iO</guid>
            <dc:creator>Vlad Krasnov</dc:creator>
        </item>
        <item>
            <title><![CDATA[How "expensive" is crypto anyway?]]></title>
            <link>https://blog.cloudflare.com/how-expensive-is-crypto-anyway/</link>
            <pubDate>Thu, 28 Dec 2017 18:22:17 GMT</pubDate>
            <description><![CDATA[ I wouldn’t be surprised if the title of this post attracts some Bitcoin aficionados, but if you are such, I want to disappoint you. For me crypto means cryptography, not cybermoney, and the price we pay for it is measured in CPU cycles, not USD. ]]></description>
            <content:encoded><![CDATA[ <p>I wouldn’t be surprised if the title of this post attracts some Bitcoin aficionados, but if you are such, I want to disappoint you. For me crypto means cryptography, not cybermoney, and the price we pay for it is measured in CPU cycles, not USD.</p><p>If you got to this second paragraph you probably heard that TLS today is very cheap to deploy. Considerable effort was put to optimize the cryptography stacks of OpenSSL and <a href="/make-ssl-boring-again/">BoringSSL</a>, as well as the hardware that runs them. However, aside for the occasional benchmark, that can tell us how many GB/s a given algorithm can encrypt, or how many signatures a certain elliptic curve can generate, I did not find much information about the cost of crypto in real world TLS deployments.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4GpBJuCjZpnZ4bN5fo6AB5/1d03279839c5d142210fd92278f1c3a0/22100041369_327f9dcfc6_o.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/e-coli/22100041369/in/photolist-zEUum2-ER8BYq-5hXtwb-CxiYwX-5hXtvY-5hXtwj-5hFNbs-64fGf6-5hFLPQ-DSoVkq-bwyd2T-wYRxkw-5qW7d2-Vk3SA3-Kz3mo-KmY6zN-56qFgQ-UTiRvS-oEfFVF-dYqdH3-9z5NFC-64r7NG-ps3sX2-21Lx1pJ-CoqCmg-ZDQmP6-Es7Qyu-ZUU2fs-CmLMxz-ZUTF2w-CoqCPa-DU3hpj-64mTPT-56nEXr-Chbu5y-aXWvtK-56mDWX-Kz3mf-nJmMH7-7u5NGZ-jfFAJZ-afaRHw-HixWdX-Kz3mj-nbMprL-thpMu-4mtfnn-56mN4F-ZUUVs3-DSn1A9">image</a> by <a href="https://www.flickr.com/photos/e-coli/">Michele M. F.</a></p><p>As Cloudflare is the largest provider of TLS on the planet, one would think we perform a lot of cryptography related tasks, and one would be absolutely correct. More than half of our external traffic is now TLS, as well as all of our internal traffic. Being in that position means that crypto performance is critical to our success, and as it happens, every now and then we like to profile our production servers, to identify and fix hot spots.</p><p>In this post I want to share the latest profiling results that relate to crypto.</p><p>The profiled server is located in our Frankfurt data center, and sports 2 Xeon Silver 4116 processors. Every geography has a slightly different use pattern of TLS. In Frankfurt 73% of the requests are TLS, and the negotiated cipher-suites break down like so:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5FwOwwcCOKEmKPXC39AKre/65ef57db90078551f6e6f87356acf7dc/Cloudflare--Frankfurt---negotiated-TLS-cipher-suites.png" />
            
            </figure><p>Processing all of those different ciphersuites, BoringSSL consumes just 1.8% of the CPU time. That’s right, mere 1.8%. And that is not even pure cryptography, there is a considerable overhead involved too.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/10fP5b90Te8C5dgLu9niYm/9eecc466e1d99d5c67407ea53e49da5c/Cloudflare-boringssl-profiling.png" />
            
            </figure><p>Let’s take a deeper dive, shall we?</p>
    <div>
      <h3>Ciphers</h3>
      <a href="#ciphers">
        
      </a>
    </div>
    <p>If we break down the negotiated cipher suites, by the AEAD used, we get the following breakdown:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4xAr8QpgNXYb87XWzOaJcZ/baf15d5818d5e5a9335540da5d0ed87d/Cloudflare-boringssl-ciphers-1.png" />
            
            </figure><p>BoringSSL speed tells us that AES-128-GCM, <a href="/it-takes-two-to-chacha-poly/">ChaCha20-Poly1305</a> and AES-128-CBC-SHA1 can achieve encryption speeds of 3,733.3 MB/s, 1,486.9 MB/s and 387.0 MB/s, but this speed varies greatly as a function of the record size. Indeed we see that GCM uses proportionately less CPU time.</p><p>Still the CPU time consumed by encryption and decryption depends on typical record size, as well as the amount of data processed, both metrics we don’t currently log. We do know that ChaCha20-Poly1305 is usually used by older phones, where the connections are short lived to save power, while AES-CBC is used for … well your guess is as good as mine who still uses AES-CBC and for what, but good thing its usage keeps <a href="/aes-cbc-going-the-way-of-the-dodo/">declining</a>.</p><p>Finally keep in mind that 6.8% of BoringSSL usage in the graph translates into 6.8% x 1.8% = 0.12% of total CPU time.</p>
    <div>
      <h3>Public Key</h3>
      <a href="#public-key">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3J8LQ4EYzEy2w7hFqmQRKU/87a4663fc2b28f0903cdb7dcc0a98dd5/Cloudflare-boringssl-pubkey-1.png" />
            
            </figure><p>Public key algorithms in TLS serve two functions.</p><p>The first function is as a key exchange algorithm, the prevalent algorithm here is ECDHE that uses the NIST P256 curve, the runner up is ECDHE using DJB’s x25519 curve. Finally there is a small fraction that still uses RSA for key exchange, the only key exchange algorithm currently used, that does not provide Forward Secrecy guarantees.</p><p>The second function is that of a signature used to sign the handshake parameters and thus authenticate the server to the client. As a signature RSA is very much alive, present in almost one quarter of the connections, the other three quarters using ECDSA.</p><p>BoringSSL speed reports that a single core on our server can perform 1,120 RSA2048 signatures/s, 120 RSA4096 signatures/s, 18,477 P256 ESDSA signatures/s, 9,394 P256 ECDHE operations/s and 9,278 x25519 ECDHE operations/s.</p><p>Looking at the CPU consumption, it is clear that RSA is <i>very</i> expensive. Roughly half the time BoringSSL performs an operation related to RSA. P256 consumes twice as much CPU time as x25519, but considering that it handles twice as much key-exchanges, while also being used as a signature, that is commendable.</p><p>If you want to make the internet a better place, please get an ECDSA signed certificate next time!</p>
    <div>
      <h3>Hash functions</h3>
      <a href="#hash-functions">
        
      </a>
    </div>
    <p>Only two hash function are currently used in TLS: SHA1 and SHA2 (including SHA384). SHA3 will probably debut with TLS1.3. Hash functions serve several purposes in TLS. First, they are used as part of the signature for both the certificate and the handshake, second they are used for key derivation, finally when using AES-CBC, SHA1 and SHA2 are used in HMAC to authenticate the records.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/KdvAh7bAu4nqYOG51WpYM/d2aa3e1a0b9ede77a8e4cf71b38c8707/Cloudflare-boringssl-hash.png" />
            
            </figure><p>Here we see SHA1 consuming more resources than expected, but that is really because it is used as HMAC, whereas most cipher suites that negotiate SHA256 use AEADs. In terms of benchmarks BoringSSL speed reports 667.7 MB/s for SHA1, 309.0 MB/s for SHA256 and 436.0 MB/s for SHA512 (truncated to SHA384 in TLS, that is not visible in the graphs because its usage approaches 0%).</p>
    <div>
      <h3>Conclusions</h3>
      <a href="#conclusions">
        
      </a>
    </div>
    <p>Using TLS is very cheap, even at the scale of Cloudflare. Modern crypto is very fast, with AES-GCM and P256 being great examples. RSA, once a staple of cryptography, that truly made SSL accessible to everyone, is now a dying dinosaur, replaced by faster and safer algorithms, still consumes a disproportionate amount of resources, but even that is easily manageable.</p><p>The future however is less clear. As we approach the era of Quantum computers it is clear that TLS must adapt sooner rather than later. We already support <a href="/sidh-go/">SIDH</a> as a key exchange algorithm for some services, and there is a <a href="https://csrc.nist.gov/Projects/Post-Quantum-Cryptography/Round-1-Submissions">NIST competition</a> in place, that will determine the most likely Post Quantum candidates for TLS adoption, but none of the candidates can outperform P256. I just hope that when I profile our edge two years from now, my conclusion won’t change to “Whoa, crypto is expensive!”.</p> ]]></content:encoded>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[SSL]]></category>
            <category><![CDATA[TLS]]></category>
            <category><![CDATA[RSA]]></category>
            <category><![CDATA[Cryptography]]></category>
            <guid isPermaLink="false">6OipoHQSeuRdGuvjIXuLra</guid>
            <dc:creator>Vlad Krasnov</dc:creator>
        </item>
        <item>
            <title><![CDATA[Go, don't collect my garbage]]></title>
            <link>https://blog.cloudflare.com/go-dont-collect-my-garbage/</link>
            <pubDate>Mon, 13 Nov 2017 10:31:09 GMT</pubDate>
            <description><![CDATA[ Not long ago I needed to benchmark the performance of Golang on a many-core machine. I took several of the benchmarks that are bundled with the Go source code, copied them, and modified them to run on all available threads. ]]></description>
            <content:encoded><![CDATA[ <p>Not long ago I needed to benchmark the performance of Golang on a many-core machine. I took several of the benchmarks that are bundled with the Go source code, copied them, and modified them to run on all available threads. In that case the machine has 24 cores and 48 threads.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1im9xk1mwSCF71CvOgrQYP/b7fef48c9fd09fe3fa09ff5b3bababfb/36963798223_b4da5151aa_k.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/147079914@N03/36963798223/in/photolist-ZhPe1A-Yjn8pV-ZhPcm3-YfT3xL-ZkG9kP-qLEURD-4rPJbB-uwsT5-9aWgeA-92n4h-5LWz68-92n6p-5gE7TJ-3Scj6p-duNYgz-4rTMKJ-8P3YZ3-8QLKYc-CHFrLH-MuQT2E">image</a> by <a href="https://www.flickr.com/photos/147079914@N03/">sponki25</a></p><p>I started with ECDSA P256 Sign, probably because I have warm feeling for that function, since I <a href="/go-crypto-bridging-the-performance-gap/">optimized it for amd64</a>.</p><p>First, I ran the benchmark on a single goroutine: <code>ECDSA-P256 Sign,30618.50, op/s</code></p><p>That looks good; next I ran it on 48 goroutines: <code>ECDSA-P256 Sign,78940.67, op/s</code>.</p><p>OK, that is not what I expected. Just over 2X speedup, from 24 physical cores? I must be doing something wrong. Maybe Go only uses two cores? I ran <code>top</code>, it showed 2,266% utilization. That is not the 4,800% I expected, but it is also way above 400%.</p><p>How about taking a step back, and running the benchmark on two goroutines? <code>ECDSA-P256 Sign,55966.40, op/s</code>. Almost double, so pretty good. How about four goroutines? <code>ECDSA-P256 Sign,108731.00, op/s.</code> That is actually faster than 48 goroutines, what is going on?</p><p>I ran the benchmark for every number of goroutines from 1 to 48:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4gTjIIpNxaG4OoM8U4M2V3/0e0c43feb83f2b1e462513ad930651a9/goroutines2.png" />
            
            </figure><p>Looks like the number of signatures per second peaks at 274,622, with 17 goroutines. And starts dropping rapidly after that.</p><p>Time to do some profiling.</p>
            <pre><code>(pprof) top 10
Showing nodes accounting for 47.53s, 50.83% of 93.50s total
Dropped 224 nodes (cum &lt;= 0.47s)
Showing top 10 nodes out of 138
      flat  flat%   sum%        cum   cum%
     9.45s 10.11% 10.11%      9.45s 10.11%  runtime.procyield /state/home/vlad/go/src/runtime/asm_amd64.s
     7.55s  8.07% 18.18%      7.55s  8.07%  runtime.futex /state/home/vlad/go/src/runtime/sys_linux_amd64.s
     6.77s  7.24% 25.42%     19.18s 20.51%  runtime.sweepone /state/home/vlad/go/src/runtime/mgcsweep.go
     4.20s  4.49% 29.91%     16.28s 17.41%  runtime.lock /state/home/vlad/go/src/runtime/lock_futex.go
     3.92s  4.19% 34.11%     12.58s 13.45%  runtime.(*mspan).sweep /state/home/vlad/go/src/runtime/mgcsweep.go
     3.50s  3.74% 37.85%     15.92s 17.03%  runtime.gcDrain /state/home/vlad/go/src/runtime/mgcmark.go
     3.20s  3.42% 41.27%      4.62s  4.94%  runtime.gcmarknewobject /state/home/vlad/go/src/runtime/mgcmark.go
     3.09s  3.30% 44.58%      3.09s  3.30%  crypto/elliptic.p256OrdSqr /state/home/vlad/go/src/crypto/elliptic/p256_asm_amd64.s
     3.09s  3.30% 47.88%      3.09s  3.30%  runtime.(*lfstack).pop /state/home/vlad/go/src/runtime/lfstack.go
     2.76s  2.95% 50.83%      2.76s  2.95%  runtime.(*gcSweepBuf).push /state/home/vlad/go/src/runtime/mgcsweepbuf.go</code></pre>
            <p>Clearly Go spends a disproportionate amount of time collecting garbage. All my benchmark does is generates signatures and then dumps them.</p><p>So what are our options? The Go runtime states the following:</p><blockquote><p>The GOGC variable sets the initial garbage collection target percentage. A collection is triggered when the ratio of freshly allocated data to live data remaining after the previous collection reaches this percentage. The default is GOGC=100. Setting GOGC=off disables the garbage collector entirely. The runtime/debug package's SetGCPercent function allows changing this percentage at run time. See <a href="https://golang.org/pkg/runtime/debug/#SetGCPercent">https://golang.org/pkg/runtime/debug/#SetGCPercent</a>.</p></blockquote><blockquote><p>The GODEBUG variable controls debugging variables within the runtime. It is a comma-separated list of name=val pairs setting these named variables:</p></blockquote><p>Let’s see what setting <code>GODEBUG</code> to <code>gctrace=1</code> does.</p>
            <pre><code>gc 1 @0.021s 0%: 0.15+0.37+0.25 ms clock, 3.0+0.19/0.39/0.60+5.0 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 48 P
gc 2 @0.024s 0%: 0.097+0.94+0.16 ms clock, 0.29+0.21/1.3/0+0.49 ms cpu, 4-&gt;4-&gt;1 MB, 5 MB goal, 48 P
gc 3 @0.027s 1%: 0.10+0.43+0.17 ms clock, 0.60+0.48/1.5/0+1.0 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 48 P
gc 4 @0.028s 1%: 0.18+0.41+0.28 ms clock, 0.18+0.69/2.0/0+0.28 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 48 P
gc 5 @0.031s 1%: 0.078+0.35+0.29 ms clock, 1.1+0.26/2.0/0+4.4 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 48 P
gc 6 @0.032s 1%: 0.11+0.50+0.32 ms clock, 0.22+0.99/2.3/0+0.64 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 48 P
gc 7 @0.034s 1%: 0.18+0.39+0.27 ms clock, 0.18+0.56/2.2/0+0.27 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 48 P
gc 8 @0.035s 2%: 0.12+0.40+0.27 ms clock, 0.12+0.63/2.2/0+0.27 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 48 P
gc 9 @0.036s 2%: 0.13+0.41+0.26 ms clock, 0.13+0.52/2.2/0+0.26 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 48 P
gc 10 @0.038s 2%: 0.099+0.51+0.20 ms clock, 0.19+0.56/1.9/0+0.40 ms cpu, 4-&gt;5-&gt;0 MB, 5 MB goal, 48 P
gc 11 @0.039s 2%: 0.10+0.46+0.20 ms clock, 0.10+0.23/1.3/0.005+0.20 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 48 P
gc 12 @0.040s 2%: 0.066+0.46+0.24 ms clock, 0.93+0.40/1.7/0+3.4 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 48 P
gc 13 @0.041s 2%: 0.099+0.30+0.20 ms clock, 0.099+0.60/1.7/0+0.20 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 48 P
gc 14 @0.042s 2%: 0.095+0.45+0.24 ms clock, 0.38+0.58/2.0/0+0.98 ms cpu, 4-&gt;5-&gt;0 MB, 5 MB goal, 48 P
gc 15 @0.044s 2%: 0.095+0.45+0.21 ms clock, 1.0+0.78/1.9/0+2.3 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 48 P
gc 16 @0.045s 3%: 0.10+0.45+0.23 ms clock, 0.10+0.70/2.1/0+0.23 ms cpu, 4-&gt;5-&gt;0 MB, 5 MB goal, 48 P
gc 17 @0.046s 3%: 0.088+0.40+0.17 ms clock, 0.088+0.45/1.9/0+0.17 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 48 P
.
.
.
.
gc 6789 @9.998s 12%: 0.17+0.91+0.24 ms clock, 0.85+1.8/5.0/0+1.2 ms cpu, 4-&gt;6-&gt;1 MB, 6 MB goal, 48 P
gc 6790 @10.000s 12%: 0.086+0.55+0.24 ms clock, 0.78+0.30/4.2/0.043+2.2 ms cpu, 4-&gt;5-&gt;1 MB, 6 MB goal, 48 P
</code></pre>
            <p>The first round of GC kicks in at 0.021s, then it starts collecting every 3ms and then every 1ms. That is insane, the benchmark runs for 10 seconds, and I saw 6,790 rounds of GC. The number that starts with @ is the time since program start, followed by a percentage that supposedly states the amount of time spent collecting garbage. This number is clearly misleading, because the performance indicates at least 90% of the time is wasted (indirectly) on GC, not 12%. The synchronization overhead is not taken into account. What really is interesting are the three numbers separated by arrows. They show the size of the heap at GC start, GC end, and the live heap size. Remember that a collection is triggered when the ratio of freshly allocated data to live data remaining after the previous collection reaches this percentage, and defaults to 100%.</p><p>I am running a benchmark, where all allocated data is immediately discarded, and collected at the next GC cycle. The only live heap is fixed to the Go runtime, and having more goroutines does not add to the live heap. In contrast the freshly allocated data grows much faster with each additional goroutine, triggering increasingly frequent, and expensive GC cycles.</p><p>Clearly what I needed to do next was to run the benchmark with the GC disabled, by setting <code>GOGC=off</code>. This lead to a dramatic improvement: <code>ECDSA-P256 Sign,413740.30, op/s</code>.</p><p>But still not the number I was looking for, and running an application without garbage collection is unsustainable in the long run. I started playing with the <code>GOGC</code> variable. First I set it to 2,400, which made sense since we have 24 cores, perhaps collecting garbage 24 times less frequently will do the trick: <code>ECDSA-P256 Sign,671538.90, op/s</code>, oh my that is getting better.</p><p>What if I tried 4,800, for the number of threads? <code>ECDSA-P256 Sign,685810.90, op/s</code>. Getting warmer.</p><p>I ran a script to find the best value, from 100 to 20,000, in increments of 100. This is what I got:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2zXh0sUUSQ5Qjuf9RaTPl1/226f072ccfc516fbe92dc0531d0bfa71/gogc.png" />
            
            </figure><p>Looks like the optimal value for <code>GOGC</code> in that case is 11,300 and it gets us 691,054 signatures/second. That is 22.56X times faster than the single core score, and overall pretty good for a 24 core processor. Remember that when running on a single core, the CPU frequency is 3.0GHz, and only 2.1GHz when running on all cores.</p><p>Per goroutine performance when running with <code>GOGC=11300</code> now looks like that:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4Qjl1zChKIwSlMcLjVanpE/66d0e438bb5cd24ba15442ae0484e193/goroutines3.png" />
            
            </figure><p>The scaling looks much better, and even past 24 goroutines, when we run out of physical cores, and start sharing cores with hyper-threading, the overall performance improves.</p><p>The bottom line here is that although this type of benchmarking is definitely an edge case for garbage collection, where 48 threads allocate large amounts of short lived data, this situation can occur in real world scenarios. As many-core CPUs become a commodity, one should be aware of the pitfalls.</p><p>Most languages with garbage collection offer some sort of garbage collection control. Go has the GOGC variable, that can also be controlled with the SetGCPercent function in the runtime/debug package. Don't be afraid to tune the GC to suit your needs.</p><p>We're always looking for Go programmers, so if you found this blog post interesting, why not check out our <a href="https://www.cloudflare.com/careers/">jobs page</a>?</p> ]]></content:encoded>
            <category><![CDATA[Go]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Programming]]></category>
            <guid isPermaLink="false">mPdR2DMS30TQDNQOzb1He</guid>
            <dc:creator>Vlad Krasnov</dc:creator>
        </item>
        <item>
            <title><![CDATA[On the dangers of Intel's frequency scaling]]></title>
            <link>https://blog.cloudflare.com/on-the-dangers-of-intels-frequency-scaling/</link>
            <pubDate>Fri, 10 Nov 2017 11:06:58 GMT</pubDate>
            <description><![CDATA[ While I was writing the post comparing the new Qualcomm server chip, Centriq, to our current stock of Intel Skylake-based Xeons, I noticed a disturbing phenomena. ]]></description>
            <content:encoded><![CDATA[ <p>While I was writing the post <a href="/arm-takes-wing/">comparing the new Qualcomm server chip, Centriq, to our current stock of Intel Skylake-based Xeons</a>, I noticed a disturbing phenomena.</p><p>When benchmarking OpenSSL 1.1.1dev, I discovered that the performance of the cipher <a href="/do-the-chacha-better-mobile-performance-with-cryptography/">ChaCha20-Poly1305</a> does not scale very well. On a single thread, it performed at the speed of approximately 2.89GB/s, whereas on 24 cores, and 48 threads it performed at just over 35 GB/s.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/jlnzEn5W40VfjAjikYdYW/1de112201351e15272d2005504081f9a/3618979283_5d117e956f_o.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/blumblaum/3618979283/in/photolist-6vNdsx-oqxCB4-VPRfyG-93tyBR-F8JBFo-qiRno8-dPuTWR-823ZS4-9hqkuL-9mFdCJ-nho2bN-8SooRN-bEYfa2-VpkZWA-diuHi4-daq1Zg-qiThYa-o9Pnb2-b8G3ND-dPotf8-yEgMt-7GMuJQ-dc3AXG-WqA4iw-fokagU-qTcnqd-csBTnw-efpWJk-e8UN3x-e8UMye-e8UMEe-dgeUKv-54A2Ve-8kh2we-54A2Pa-e91rzh-q7baBG-54A2Kv-umff5-7LUaZQ-ntyDPD-bPcRhV-e8UMKT-e8UMwR-athi9i-8aKsm6-oWFjAF-e4Uvop-69kwK4-e4UvCH">image</a> by <a href="https://www.flickr.com/photos/blumblaum/">blumblaum</a></p><p>Now this is a very high number, but I would like to see something closer to 69GB/s. 35GB/s is just 1.46GB/s/core, or roughly 50% of the single core performance. AES-GCM scales much better, to 80% of single core performance, which is understandable, because the CPU can sustain higher frequency turbo on a single core, but not all cores.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/V6Oda0nbgjQEdLGhXue4A/09c9be06c793682fc218c046974a0b1b/gcm-chacha-core.png" />
            
            </figure><p>Why is the scaling of ChaCha20-Poly1305 so poor? Meet AVX-512. AVX-512 is a new Intel instruction set that adds many new 512-bit wide SIMD instructions and promotes most of the existing ones to 512-bit. The problem with such wide instructions is that they consume power. A lot of power. Imagine a single instruction that does the work of 64 regular byte instructions, or 8 full blown 64-bit instructions.</p><p>To keep power in check Intel introduced something called dynamic frequency scaling. It reduces the base frequency of the processor whenever AVX2 or AVX-512 instructions are used. This is not new, and has existed since Haswell introduced AVX2 three years ago.</p><p>The scaling gets worse when more cores execute AVX-512 and when multiplication is used.</p><p>If you only run AVX-512 code, then everything is good. The frequency is lower, but your overall productivity is higher, because each instruction does more work.</p><p>OpenSSL 1.1.1dev implements several variants of ChaCha20-Poly1305, including AVX2 and AVX-512 variants. BoringSSL implements a different AVX2 version of ChaCha20-Poly1305. It is understandable then why BoringSSL achieves only 1.6GB/s on a single core, compared to the 2.89GB/s OpenSSL does.</p><p>So how does this affect you, if you mix a little AVX-512 with your real workload? We use the Xeon Silver 4116 CPUs, with a base frequency 2.1GHz, in a dual socket configuration. From a figure I found on <a href="https://en.wikichip.org/wiki/intel/xeon_silver/4116">wikichip</a> it seems that running AVX-512 even just on one core on this CPU will reduce the base frequency to 1.8GHz. Running AVX-512 on all cores will reduce it to just 1.4GHz.</p><p>Now imagine you run a webserver with Apache or NGINX. In addition you have many other services, performing some real, important work. What happens if you start encrypting your traffic with ChaCha20-Poly1305 using AVX-512? That is the question I asked myself.</p><p>I compiled two versions of NGINX, one with OpenSSL1.1.1dev and the other with BoringSSL, and installed it on our server with two Xeon Silver 4116 CPUs, for a total of 24 cores.</p><p>I configured the server to serve a medium sized HTML page, and perform some meaningful work on it. I used LuaJIT to remove line breaks and extra spaces, and brotli to compress the file.</p><p>I then monitored the number of requests per second served under full load. This is what I got:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/stzkAbRIIvTH3SI7jw8Cv/40b2d9d0fc3c16717b58066fea0cb824/gcm-chacha-1.png" />
            
            </figure><p>By using ChaCha20-Poly1305 over AES-128-GCM, the server that uses OpenSSL serves 10% fewer requests per second. And that is a huge number! It is equivalent to giving up on two cores, for nothing. One might think that this is due to ChaCha20-Poly1305 being inherently slower. But that is not the case.</p><p>First, BoringSSL performs equivalently well with AES-GCM and ChaCha20-Poly1305.</p><p>Second, even when only 20% of the requests use ChaCha20-Poly1305, the server throughput drops by more than 7%, and by 5.5% when 10% of the requests are ChaCha20-Poly1305. For reference, 15% of the TLS requests Cloudflare handles are ChaCha20-Poly1305.</p><p>Finally, according to <code>perf</code>, the AVX-512 workload consumes only 2.5% of the CPU time when all the requests are ChaCha20-Poly1305, and less then 0.3% when doing ChaCha20-Poly1305 for 10% of the requests. Irregardless the CPU throttles down, because that what it does when it sees AVX-512 running on all cores.</p><p>It is hard to say just how much each core is throttled at any given time, but doing some sampling using <code>lscpu</code>, I found out that when executing the <code>openssl speed -evp chacha20-poly1305 -multi 48</code> benchmark, it shows <code>CPU MHz: 1199.963</code>, for OpenSSL with all AES-GCM connections I got <code>CPU MHz: 2399.926</code> and for OpenSSL with all ChaCha20-Poly1305 connections I saw <code>CPU MHz: 2184.338</code>, which is obviously 9% slower.</p><p>Another interesting distinction is that ChaCha20-Poly1305 with AVX2 is slightly slower in OpenSSL but is the same in BoringSSL. Why might that be? The reason here is that the BoringSSL code does not use AVX2 multiplication instructions for Poly1305, and only uses simple xor, shift and add operations for ChaCha20, which allows it to run at the base frequency.</p><p>OpenSSL 1.1.1dev is still in development, therefore I suspect no one is affected by this issue yet. We switched to BoringSSL months ago, and our server performance is not affected by this issue.</p><p>What the future holds in unclear. Intel announced very cool new ISA extensions for the future generation of CPUs, that are expected to improve crypto performance even further. Those extensions include AVX512+VAES, AVX512+VPCLMULQDQ and AVX512IFMA. But if the frequency scaling issue is not resolved by then, using those for general purpose cryptography libraries will do (much) more harm than good.</p><p>The problem is not with cryptography libraries alone. OpenSSL did nothing wrong by trying to get the best possible performance, on the contrary, I wrote a decent amount of AVX-512 code for OpenSSL myself. The observed behavior is a sad side effect. There are many libraries that use AVX and AVX2 instructions out there, they will probably be updated to AVX-512 at some point, and users are not likely to be aware of the implementation details. If you do not require AVX-512 for some specific high performance tasks, I suggest you disable AVX-512 execution on your server or desktop, to avoid accidental AVX-512 throttling.</p> ]]></content:encoded>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[OpenSSL]]></category>
            <category><![CDATA[SSL]]></category>
            <category><![CDATA[Cryptography]]></category>
            <guid isPermaLink="false">VFpy4PLKXP7Xxo1BX6Kdp</guid>
            <dc:creator>Vlad Krasnov</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[AES-CBC is going the way of the dodo]]></title>
            <link>https://blog.cloudflare.com/aes-cbc-going-the-way-of-the-dodo/</link>
            <pubDate>Fri, 21 Apr 2017 16:44:29 GMT</pubDate>
            <description><![CDATA[ A little over a year ago, Nick Sullivan talked about the beginning of the end for AES-CBC cipher suites, following a plethora of attacks on this cipher mode. ]]></description>
            <content:encoded><![CDATA[ <p>A little over a year ago, Nick Sullivan <a href="/padding-oracles-and-the-decline-of-cbc-mode-ciphersuites/">talked about</a> the beginning of the end for AES-CBC cipher suites, following a plethora of attacks on this cipher mode.</p><p>Today we can safely confirm that this prediction is coming true, as for the first time ever the share of AES-CBC cipher suites on Cloudflare’s edge network dropped below that of <a href="/it-takes-two-to-chacha-poly/">ChaCha20-Poly1305</a> suites, and is fast approaching the 10% mark.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1Nlmap0WYRSOk7Y2UdI8ww/f73d5e7ed173d992dc36ae3f0c9d2579/153059715_fad3b8be43_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/andreweason/153059715/in/photolist-ewtjB-4DuF2q-7nWzyC-54VpEC-piTG3-e9gFka-47jfK-47jfh-47jfC-47jfq-47jf5-54VpFf-gTEbKu-54VpDJ-KvpMZH-47iKQ-7527ue-49Y6Z6-7dcwjq-6kGs2K-kmBTE-8vEFRq-4NfTAs-5AXLyv-qCxMrX-6Th9Ds-7oN3Zc-59wsSQ-5vyf5e-qHrCWQ-JWpMjc-4BqcyP-qFe6ky-aH51WB-7zxqQ-dsFacY-7uv9Qi-4PMzMv-9WUNfe-7yZJHo-9xVWK-sfqKtc-eHEhYe-qpiFHG-apXNix-9tnNDQ-7jdmAj-8Bwgp7-8UsZhm-55ke5e">image</a> by <a href="https://www.flickr.com/photos/andreweason/">aesop</a></p><p>Over the course of the last six months, AES-CBC shed more than 33% of its “market” share, dropping from 20% to just 13.4%.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3gz1nglJIcaJvAFH5DHWJs/e32c986781b4f816b3edd4301129564a/cipher-ms.png" />
            
            </figure><p>All of that share, went to AES-GCM, that currently encrypts over 71.2% of all connections. <a href="/it-takes-two-to-chacha-poly/">ChaCha20-Poly1305</a> is stable, with 15.3% of all connections opting for that cipher. Surprisingly 3DES is still around, with 0.1% of the connections.</p><p>The internal AES-CBC cipher suite breakdown as follows:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/yeTAZ34KUHiQXPku20xEF/70eb66c65844ca1a6bd5344169bd65f8/CBC.png" />
            
            </figure><p>The majority of AES-CBC connections use ECDHE-RSA or RSA key exchange, and not ECDHE-ECDSA, which implies that we mostly deal with older clients.</p>
    <div>
      <h3>RSA is also dying</h3>
      <a href="#rsa-is-also-dying">
        
      </a>
    </div>
    <p>In other good new, the use of <a href="/ecdsa-the-digital-signature-algorithm-of-a-better-internet/">ECDSA</a> surpassed that of RSA at the beginning of the year. Currently more than 60% of all connections use the ECDSA signature.</p><p>Although 2048-bit RSA is not broken, it is generally considered less secure than 256-bit ECDSA, and is significantly slower to boot.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1Tro9KjStX75FiUaOngQZ/03c7701079026f519e5a2bdaad669ce8/signature.png" />
            
            </figure>
    <div>
      <h3>PFS is king</h3>
      <a href="#pfs-is-king">
        
      </a>
    </div>
    <p>Last, but not least, 98.4% of all connections are PFS, using ECDHE for key exchange. That's up from 97.6% six months ago.</p><p>All in all we see the continuation of the positive trend on the web towards safer and faster cryptography. We believe this trend will continue with the finalization of <a href="/introducing-tls-1-3/">TLS 1.3</a> later this year.</p> ]]></content:encoded>
            <category><![CDATA[RSA]]></category>
            <category><![CDATA[Security]]></category>
            <guid isPermaLink="false">1hf5oN6lFgDIbWL26jQ4x7</guid>
            <dc:creator>Vlad Krasnov</dc:creator>
        </item>
        <item>
            <title><![CDATA[HPACK: the silent killer (feature) of HTTP/2]]></title>
            <link>https://blog.cloudflare.com/hpack-the-silent-killer-feature-of-http-2/</link>
            <pubDate>Mon, 28 Nov 2016 14:10:41 GMT</pubDate>
            <description><![CDATA[ If you have experienced HTTP/2 for yourself, you are probably aware of the visible performance gains possible with HTTP/2 due to features like stream multiplexing, explicit stream dependencies, and Server Push.

 ]]></description>
            <content:encoded><![CDATA[ <p>If you have <a href="https://www.cloudflare.com/website-optimization/http2/">experienced HTTP/2</a> for yourself, you are probably aware of the visible performance gains possible with <a href="https://www.cloudflare.com/website-optimization/http2/">HTTP/2</a> due to features like stream multiplexing, explicit stream dependencies, and <a href="/announcing-support-for-http-2-server-push-2/">Server Push</a>.</p><p>There is however one important feature that is not obvious to the eye. This is the HPACK header compression. Current implementation of nginx, as well edge networks and <a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/">CDNs</a> using it, do not support the full HPACK implementation. We have, however, implemented the full HPACK in nginx, and <a href="http://mailman.nginx.org/pipermail/nginx-devel/2015-December/007682.html">upstreamed</a> the part that performs Huffman encoding.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4wGvQogXUWstwB5ETJPpyr/5423d3e1f23daed6c17cc8d79a25ac8d/6129093355_9434beed25_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/conchur/6129093355/in/photolist-akBcVn-6eEtUq-d9Xi2T-c8MwzA-mGVr3z-aVezji-mGVxov-acMJmz-6nvbCJ-6a6DGQ-5uwrr9-rRtCCp-acMKsk-cKeuXY-9F6N13-9GSuHh-acMDBk-cJYes5-cijtcj-akFua9-cK1iqS-fiAHkE-acMCDt-cJZ9Fq-5qtfNv-cJWFiw-e4pfTd-6fzyLV-8RNMTo-p7jJLQ-524dkf-dw52qG-mVDttF-duUbYU-dvXt3Z-c8NkYG-6e6K2S-duKJjZ-5DbaqV-duBiR3-dvGTsM-dvLst4-dvPfJ1-ezYL1J-7f1cMC-bqi5WN-5MMGkV-9F3C7c-eWASXx-cJXsW9">image</a> by <a href="https://www.flickr.com/photos/conchur/">Conor Lawless</a></p><p>This blog post gives an overview of the reasons for the development of HPACK, and the hidden bandwidth and latency benefits it brings.</p>
    <div>
      <h3>Some Background</h3>
      <a href="#some-background">
        
      </a>
    </div>
    <p>As you probably know, a regular HTTPS connection is in fact an overlay of several connections in the multi-layer model. The most basic connection you usually care about is the TCP connection (the transport layer), on top of that you have the TLS connection (mix of transport/application layers), and finally the HTTP connection (application layer).</p><p>In the the days of yore, HTTP compression was performed in the TLS layer, using gzip. Both headers and body were compressed indiscriminately, because the lower TLS layer was unaware of the transferred data type. In practice it meant both were compressed with the <a href="/results-experimenting-brotli/">DEFLATE</a> algorithm.</p><p>Then came SPDY with a new, dedicated, header compression algorithm. Although specifically designed for headers, including the use of a preset <a href="/improving-compression-with-preset-deflate-dictionary/">dictionary</a>, it was still using DEFLATE, including dynamic Huffman codes and string matching.</p><p>Unfortunately both were found to be vulnerable to the <a href="https://www.ekoparty.org/archive/2012/CRIME_ekoparty2012.pdf">CRIME</a> attack, that can extract secret authentication cookies from compressed headers: because DEFLATE uses backward string matches and dynamic Huffman codes, an attacker that can control part of the request headers, can gradually recover the full cookie by modifying parts of the request and seeing how the total size of the request changes during compression.</p><p>Most edge networks, including Cloudflare, disabled header compression because of CRIME. That’s until HTTP/2 came along.</p>
    <div>
      <h3>HPACK</h3>
      <a href="#hpack">
        
      </a>
    </div>
    <p>HTTP/2 supports a new dedicated header compression algorithm, called HPACK. HPACK was developed with attacks like CRIME in mind, and is therefore considered safe to use.</p><p>HPACK is resilient to CRIME, because it does not use partial backward string matches and dynamic Huffman codes like DEFLATE. Instead, it uses these three methods of compression:</p><ul><li><p>Static Dictionary: A <a href="https://http2.github.io/http2-spec/compression.html#static.table.definition">predefined dictionary</a> of 61 commonly used header fields, some with predefined values.</p></li><li><p>Dynamic Dictionary: A list of actual headers that were encountered during the connection. This dictionary has limited size, and when new entries are added, old entries might be evicted.</p></li><li><p>Huffman Encoding: A <a href="https://http2.github.io/http2-spec/compression.html#huffman.code">static Huffman code</a> can be used to encode any string: name or value. This code was computed specifically for HTTP Response/Request headers - ASCII digits and lowercase letters are given shorter encodings. The shortest encoding possible is 5 bits long, therefore the highest compression ratio achievable is 8:5 (or 37.5% smaller).</p></li></ul>
    <div>
      <h4>HPACK flow</h4>
      <a href="#hpack-flow">
        
      </a>
    </div>
    <p>When HPACK needs to encode a header in the format <b><i>name:value</i></b>, it will first look in the static and dynamic dictionaries. If the <b><i>full</i></b> <b>name:value</b> is present, it will simply reference the entry in the dictionary. This will usually take one byte, and in most cases two bytes will suffice! A whole header encoded in a single byte! How crazy is that?</p><p>Since many headers are repetitive, this strategy has a very high success rate. For example, headers like <b>:authority:</b><a href="http://www.cloudflare.com"><b>www.cloudflare.com</b></a> or the sometimes huge <b>cookie</b> headers are the usual suspects in this case.</p><p>When HPACK can't match a whole header in a dictionary, it will attempt to find a header with the same <b><i>name</i></b>. Most of the popular header names are present in the static table, for example: <b>content-encoding</b>, <b>cookie</b>, <b>etag</b>. The rest are likely to be repetitive and therefore present in the dynamic table. For example, Cloudflare assigns a unique <b>cf-ray</b> header to each response, and while the value of this field is always different, the name can be reused!</p><p>If the name was found, it can again be expressed in one or two bytes in most cases, otherwise the name will be encoded using either raw encoding or the Huffman encoding: the shorter of the two. The same goes for the value of the header.</p><p>We found that the Huffman encoding alone saves almost 30% of header size.</p><p>Although HPACK does string matching, for the attacker to find the value of a header, they must guess the entire value, instead of a gradual approach that was possible with DEFLATE matching, and was vulnerable to CRIME.</p>
    <div>
      <h4>Request Headers</h4>
      <a href="#request-headers">
        
      </a>
    </div>
    <p>The gains HPACK provides for HTTP request headers are more significant than for response headers. Request headers get better compression, due to much higher duplication in the headers. For example, here are two requests for our own blog, using Chrome:</p><h6>Request #1:</h6><p>**:authority:**blog.cloudflare.com**:method:**GET<b>:path:</b> /**:scheme:**https**accept:**text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,<i>/</i>;q=0.8**accept-encoding:**gzip, deflate, sdch, br**accept-language:**en-US,en;q=0.8<b>cookie:</b> 297 byte cookie**upgrade-insecure-requests:**1**user-agent:**Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2853.0 Safari/537.36</p><p>I marked in red the headers that can be compressed with the use of the static dictionary. Three fields: <b>:method:GET</b>, <b>:path:/</b> and <b>:scheme:https</b> are always present in the static dictionary, and will each be encoded in a single byte. Then some fields will only have their names compressed in a byte: <b>:authority</b>, <b>accept</b>, <b>accept-encoding</b>, <b>accept-language</b>, <b>cookie</b> and <b>user-agent</b> are present in the static dictionary.</p><p>Everything else, marked in green will be Huffman encoded.</p><p>Headers that were not matched, will be inserted into the dynamic dictionary for the following requests to use.</p><p>Let's take a look at a later request:</p><h6>Request #2:</h6><p><b>:authority:</b><b><b>blog.cloudflare.com</b></b>****<b>:method:</b><b><b>GET</b></b><b>:path:</b>/assets/images/cloudflare-sprite-small.png**:scheme:**https**accept:**image/webp,image/<i>,</i>/*;q=0.8**accept-encoding:**gzip, deflate, sdch, br**accept-language:**en-US,en;q=0.8**cookie:**same 297 byte cookie<b>referer:</b><a href="/assets/css/screen.css?v=2237be22c2">http://blog.cloudflare.com/assets/css/screen.css?v=2237be22c2</a>**user-agent:**Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2853.0 Safari/537.36</p><p>Here I added blue encoded fields. Those indicate fields that were matched from the dynamic dictionary. It is clear that most fields repeat between requests. In this case two fields are again present in the static dictionary and five more are repeated and therefore present in the dynamic dictionary, that means they can be encoded in one or two bytes each. One of those is the ~300 byte <b>cookie</b> header, and ~130 byte <b>user-agent</b>. That is 430 bytes encoded into mere 4 bytes, 99% compression!</p><p>All in all for the repeat request, only three short strings will be Huffman encoded.</p><p>This is how ingress header traffic appears on the Cloudflare edge network during a six hour period:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5hRafOHVmAAbUcHkXmB41S/1f601ca581a9f94fb0d8f08f2966bbc7/headers-ingress-1.png" />
            
            </figure><p>On average we are seeing a 76% compression for ingress headers. As the headers represent the majority of ingress traffic, it also provides substantial savings in the total ingress traffic:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/8J76zioOehggfZXNukjGQ/3e99553a7156092babe86efa68713333/headers-ingress-2-1.png" />
            
            </figure><p>We can see that the total ingress traffic is reduced by 53% as the result of HPACK compression!</p><p>In fact today, we process about the same number of requests for HTTP/1 and HTTP/2 over HTTPS, yet the ingress traffic for HTTP/2 is only half that of HTTP/1.</p>
    <div>
      <h3>Response Headers</h3>
      <a href="#response-headers">
        
      </a>
    </div>
    <p>For the response headers (egress traffic) the gains are more modest, but still spectacular:</p><h6>Response #1:</h6><p>**cache-control:**public, max-age=30**cf-cache-status:**HIT<b>cf-h2-pushed:</b>&lt;/assets/css/screen.css?v=2237be22c2&gt;,&lt;/assets/js/jquery.fitvids.js?v=2237be22c2&gt;**cf-ray:**2ded53145e0c1ffa-DFW**content-encoding:**gzip**content-type:**text/html; charset=utf-8**date:**Wed, 07 Sep 2016 21:41:23 GMT**expires:**Wed, 07 Sep 2016 21:41:53 GMT<b>link:</b> &lt;//cdn.bizible.com/scripts/bizible.js&gt;; rel=preload; as=script,&lt;<a href="https://code.jquery.com/jquery-1.11.3.min.js">https://code.jquery.com/jquery-1.11.3.min.js</a>&gt;; rel=preload; as=script**server:**cloudflare-nginx<b>status:200</b>**vary:**Accept-Encoding**x-ghost-cache-status:**From Cache**x-powered-by:**Express</p><p>The majority of the first response will be Huffman encoded, with some of the field names being matched from the static dictionary.</p><h6>Response #2:</h6><p>**cache-control:**public, max-age=31536000**cf-bgj:imgq:**100**cf-cache-status:**HIT<b>cf-ray</b>:2ded53163e241ffa-DFW**content-type:**image/png**date:**Wed, 07 Sep 2016 21:41:23 GMT**expires:**Thu, 07 Sep 2017 21:41:23 GMT**server:**cloudflare-nginx**status:**200**vary:**Accept-Encoding**x-ghost-cache-status:**From Cache<b>x-powered-by:Express</b></p><p>Again, the blue color indicates matches from the dynamic table, red indicate matches from the static table, and the green ones represent Huffman encoded strings.</p><p>On the second response it is possible to fully match seven of twelve headers. For four of the remaining five, the name can be fully matched, and six strings will be efficiently encoded using the static Huffman encoding.</p><p>Although the two <b>expires</b> headers are almost identical, they can only be Huffman compressed, because they can't be matched in full.</p><p>The more requests are being processed, the bigger the dynamic table becomes, and more headers can be matched, leading to increased compression ratio.</p><p>This is how egress header traffic appears on the Cloudflare edge:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7GxMxHoPdJGWpyZmqzw6lL/75874c72c47944a121e98517fba5b78b/headers-egress-1.png" />
            
            </figure><p>On average egress headers are compressed by 69%. The savings for the total egress traffic are not that significant however:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7MibXK42BowSlnSdhPGZZx/9e82b02d6c1eac9474cbb4d4e55ac919/headers-egress-2-1.png" />
            
            </figure><p>It is difficult to see, but we get 1.4% savings in the total egress HTTP/2 traffic. While it does not look like much, it is still more than <a href="/results-experimenting-brotli/">increasing the compression level for data would give</a> in many cases. This number is also significantly skewed by websites that serve very large files: we measured savings of well over 15% for some websites.</p>
    <div>
      <h3>Test your HPACK</h3>
      <a href="#test-your-hpack">
        
      </a>
    </div>
    <p>If you have nghttp2 installed, you can test the efficiency of HPACK compression on your website with a bundled tool called h2load.</p><p>For example:</p>
            <pre><code>h2load https://blog.cloudflare.com | tail -6 |head -1
traffic: 18.27KB (18708) total, 538B (538) headers (space savings 27.98%), 17.65KB (18076) data</code></pre>
            <p>We see 27.98% space savings in the headers. That is for a single request, and the gains are mostly due to the Huffman encoding. To test if the website utilizes the full power of HPACK, we need to issue two requests, for example:</p>
            <pre><code>h2load https://blog.cloudflare.com -n 2 | tail -6 |head -1
traffic: 36.01KB (36873) total, 582B (582) headers (space savings 61.15%), 35.30KB (36152) data</code></pre>
            <p>If for two similar requests the savings are 50% or more then it is very likely full HPACK compression is utilized.</p><p>Note that compression ratio improves with additional requests:</p>
            <pre><code>h2load https://blog.cloudflare.com -n 4 | tail -6 |head -1
traffic: 71.46KB (73170) total, 637B (637) headers (space savings 78.68%), 70.61KB (72304) data</code></pre>
            
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>By implementing HPACK compression for HTTP response headers we've seen a significant drop in egress bandwidth. HPACK has been enabled for all Cloudflare customers using HTTP/2, all of whom benefit from faster, smaller HTTP responses.</p> ]]></content:encoded>
            <category><![CDATA[HTTP2]]></category>
            <category><![CDATA[TLS]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[NGINX]]></category>
            <guid isPermaLink="false">5PbF7N0blz31qVAU014p8U</guid>
            <dc:creator>Vlad Krasnov</dc:creator>
        </item>
        <item>
            <title><![CDATA[Announcing Support for HTTP/2 Server Push]]></title>
            <link>https://blog.cloudflare.com/announcing-support-for-http-2-server-push-2/</link>
            <pubDate>Thu, 28 Apr 2016 13:00:00 GMT</pubDate>
            <description><![CDATA[ Last November, we rolled out HTTP/2 support for all our customers. At the time, HTTP/2 was not in wide use, but more than 88k of the Alexa 2 million websites are now HTTP/2-enabled. ]]></description>
            <content:encoded><![CDATA[ <p>Last November, we <a href="/introducing-http2/">rolled out</a> HTTP/2 support for all our customers. At the time, HTTP/2 was not in wide use, but more than 88k of the Alexa 2 million websites are now HTTP/2-enabled. Today, <a href="http://isthewebhttp2yet.com/measurements/adoption.html#organization">more than 70%</a> of sites that use HTTP/2 are served via CloudFlare.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5BllrfZ3osa7oyYGPEUgfa/d97891945a77819a659806b652af406d/They_started_our_car_by_pushing_it_backwards_up_the_hill-_-3854246685-.jpg" />
            
            </figure><p><a href="https://creativecommons.org/licenses/by/2.0/">CC BY 2.0</a> <a href="https://commons.wikimedia.org/wiki/File:They_started_our_car_by_pushing_it_backwards_up_the_hill!_(3854246685).jpg">image</a> by <a href="https://www.flickr.com/people/83555001@N00">Roger Price</a></p>
    <div>
      <h3>Incremental Improvements On SPDY</h3>
      <a href="#incremental-improvements-on-spdy">
        
      </a>
    </div>
    <p>HTTP/2’s main benefit is multiplexing, which allows multiple HTTP requests to share a single TCP connection. This has a huge impact on performance compared to HTTP/1.1, but it’s nothing new—SPDY has been multiplexing TCP connections since at least 2012.</p><p>Some of the most important aspects of HTTP/2 have yet to be implemented by major web servers or edge networks. The real promise of HTTP/2 comes from brand new features like Header Compression and Server Push. Since February, we’ve been quietly testing and deploying HTTP/2 Header Compression, which resulted in an average 30% reduction in header size for all of our clients using HTTP/2. That's awesome. However, the real opportunity for a quantum leap in web performance comes from Server Push.</p>
    <div>
      <h3>Pushing Ahead</h3>
      <a href="#pushing-ahead">
        
      </a>
    </div>
    <p>Today, we’re happy to announce <a href="https://cloudflare.com/http2/server-push/">HTTP/2 Server Push support</a> for all of our customers. Server Push enables websites and APIs to speculatively deliver content to the web browser before the browser sends a request for it. This behavior is opportunistic, since in some cases, the content might already be in the client’s cache or not required at all.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/dHj9DypA42vVajxh1mB94/ba8f12ebadb8a3f405af9049cd254d3e/http2-server-push-2.png" />
            
            </figure><p>It’s been <a href="https://www.igvita.com/2013/06/12/innovating-with-http-2.0-server-push/">touted as one of the major features</a> of HTTP/2, and by enabling it, we cover the entire set of HTTP/2 features for all of our users. You can see it in action on our <a href="https://cloudflare.com/http2/server-push/">live Server Push demo</a>.</p><p>Server Push provides significant performance gains if used judiciously. In its most basic form, Server Push allows the server to “bundle” assets that the client didn’t ask for. It works by sending <a href="https://tools.ietf.org/html/rfc7540#section-6.6"><code>PUSH_PROMISE</code></a>—a declaration of the intention to send the assets, followed by the actual assets.</p><p>Upon the receipt of a <code>PUSH_PROMISE</code>, the client may respond with a <a href="https://tools.ietf.org/html/rfc7540#section-6.4"><code>RST_STREAM</code></a> message, indicating this asset is not needed. Even in this case, due to the asynchronous nature of HTTP/2, the client may receive the asset before the server gets the <code>RST_STREAM</code> message.</p><p>The <code>PUSH_PROMISE</code> looks a lot like an HTTP/2 GET request, and the client attempts to match received push promises before sending outgoing requests.</p>
    <div>
      <h3>Enabling Server Push</h3>
      <a href="#enabling-server-push">
        
      </a>
    </div>
    <p>All of our customers using HTTP/2 now have Server Push enabled, but unfortunately, Server Push isn’t one of those features that just works—you’ll need to do a little bit of work to truly leverage the benefits of Server Push.</p><p>Our implementation follows the guidelines laid out in the W3C’s draft standard on the use of a <a href="https://w3c.github.io/preload/#server-push-http-2"><code>preload</code></a> keyword in <code>Link</code> headers<a href="#fn1">[1]</a>, and we will continue to track that standard as it evolves.</p><p>So, if you want to push assets for a given request, you simply add a specially formatted <code>Link</code> header to the response:</p>
            <pre><code>Link: &lt;/asset/to/push.js&gt;; rel=preload; as=script</code></pre>
            <p>Those links can be manually added, but they’re already created automatically by many publishing tools or via plugins for popular content management systems (CMS) such as WordPress.</p><p>Only relative links will be pushed by our edge servers, which means Server Push won’t work with third-party resources.</p>
    <div>
      <h4>Disabling Server Push</h4>
      <a href="#disabling-server-push">
        
      </a>
    </div>
    <p>The <code>Link</code> header was originally designed to let browsers know that they should preload an asset. If you still need this behavior for legacy reasons, you can append the <code>nopush</code> directive to the header, like so:</p>
            <pre><code>Link: &lt;/dont/want/to/push/this.css&gt;; rel=preload; as=style; nopush</code></pre>
            
    <div>
      <h3>Is Server Push For Me?</h3>
      <a href="#is-server-push-for-me">
        
      </a>
    </div>
    <p>Server Push has the potential for tremendous performance gains. However, it’s not able to speed up every website, and it can even degrade the performance somewhat if you get overzealous. Generally, the downside of pushing assets that will end up unused is only the wasted bandwidth, while the upside is a speedup equivalent to one round trip from the client to our edge network.</p><p>Both the downside and the upside are mostly evident on slow, lossy connections, such as mobile networks. We suggest you profile the behavior of your individual website/API with and without Server Push to estimate the benefits. In our testing, we saw around a 45% performance gain when using Server Push on a mobile network.</p><p>Also note that because Server Push operates over a given HTTP/2 connection, it can only be used to push resources from <i>your</i> domain. If your website is bottlenecked on third-party assets, Server Push is unlikely to help you.</p><p>Some of the best use cases for HTTP/2 Server Push are:</p><ul><li><p>Uncacheable content - Content that is not cached on the edge benefits from Server Push, since it will be requested from the origin earlier in the connection.</p></li><li><p>All assets on a requested page - By pushing all the CSS, JS, and image assets on a given page, it’s possible to transfer the entire page in a single round trip. This is only useful when no third party assets are blocking the page rendering. If the majority of the assets are cached on the client’s browser, this behavior can be wasteful.</p></li><li><p>The most likely next page - If there is a link on the loaded page that is most likely clicked next (for example the most recent post in a blog) you could push both the HTML and all of that pages assets. When the user clicks the link, it will render almost instantly.</p></li></ul>
    <div>
      <h3>Profiling Server Push</h3>
      <a href="#profiling-server-push">
        
      </a>
    </div>
    <p>There are currently several tools and browsers that support Server Push. However, to visualize the performance benefits of the feature, the current <a href="https://www.google.co.uk/chrome/browser/canary.html">Canary</a> version of Google Chrome is the best. Here is an example of a webpage with five images loading with and without Server Push, as depicted in the timeline:</p>
    <div>
      <h4>Plain HTTP/2:</h4>
      <a href="#plain-http-2">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/73FpGNBukfaH2eXahBHOx0/7b5fb5b9a0ffb942efb3c3a9d84014d3/Screen-Shot-2016-04-26-at-15-07-53.png" />
            
            </figure><p>After the main page is loaded (and some processing time) the browser makes a request for the five images. After another round trip, those images are delivered and loaded.</p><h6>HTTP/2 + Server Push:</h6>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/awzxwhSEWtmEZk2hsuaxe/6e9e4afc662e655c77e8ced078a229d2/Screen-Shot-2016-04-26-at-15-08-59.png" />
            
            </figure><p>With Server Push enabled, we can see that the images are delivered while the page is being processed, thus no additional round trip is required. As soon as the images are needed, Chrome matches them with existing Push Promises and immediately uses them.</p><p>In Canary Chrome, you can also see that the pushed assets are identified in the Initiator column as Push/Other.</p>
    <div>
      <h4>Other browsers:</h4>
      <a href="#other-browsers">
        
      </a>
    </div>
    
    <div>
      <h4>Firefox</h4>
      <a href="#firefox">
        
      </a>
    </div>
    <p>Pushed assets will not get a timeline and are identified by a solid grey circle (unlike cached content that is identified by a non-solid circle, and a “cached” indication in the “Transferred tab”).</p><p><b>Plain HTTP/2:</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/50EP7Couz0xYY3nmdU0uMA/d29ab6853c8803bf8611f3dcc9b01b56/Screen-Shot-2016-04-26-at-17-25-15.png" />
            
            </figure><p><b>HTTP/2 + Server Push:</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/24DVYPcTyqoiOthb97LwCh/5d902ead36ff7749c39f007f8ecaf1b8/Screen-Shot-2016-04-26-at-17-25-42.png" />
            
            </figure>
    <div>
      <h4>Edge</h4>
      <a href="#edge">
        
      </a>
    </div>
    <p>Pushed assets won’t have the yellow bar in the timeline (TTFB), and under the Protocol tab, the protocol will appear as HTTPS and not HTTP/2.</p><p><b>Plain HTTP/2:</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3acdLJx33EUVZWqDfGvdqs/46a413799609ea4b66ffd6ea35fceb5d/Capture3.PNG.png" />
            
            </figure><p><b>HTTP/2 + Server Push:</b></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5JHBwo6FAHhA2xdkbKl07q/e97c0befd3367f6240236ba59324d229/Capture1.PNG.png" />
            
            </figure>
    <div>
      <h4>Safari</h4>
      <a href="#safari">
        
      </a>
    </div>
    <p>The Safari browser does not support Server Push at the moment, but support is expected in the near future.</p>
    <div>
      <h3>What’s Next For HTTP/2?</h3>
      <a href="#whats-next-for-http-2">
        
      </a>
    </div>
    <p>In the future, we plan to develop new tools to help our users to make more educated decisions in regards to Server Push. Over time, CloudFlare will even be able to predict the best assets to push automatically.</p><p>This feature is very new, and CloudFlare is the first major provider to deploy it at scale. We look forward to hearing from customers who make use of Server Push, now that we’ve made it available for experimentation.</p><p>We believe that Server Push is the most important upgrade to the web since SPDY. It has enormous potential to speed up the Internet since, for the first time, it gives website owners the power to <i>send</i> information to a web browser before the browser even asks for it.We’ll also be monitoring the still ongoing process of HTTP/2 development, especially efforts to make Server Push more aware of the client’s cache (thus reducing the number of redundant pushes).</p><hr /><ol><li><p>We are currently only looking at link elements in HTTP headers and not in page HTML. <a href="#fnref1">↩︎</a></p></li></ol> ]]></content:encoded>
            <category><![CDATA[HTTP2]]></category>
            <category><![CDATA[Reliability]]></category>
            <category><![CDATA[spdy]]></category>
            <category><![CDATA[Product News]]></category>
            <guid isPermaLink="false">7Bo8uU68c3aWiKbCG8kwMq</guid>
            <dc:creator>Vlad Krasnov</dc:creator>
        </item>
        <item>
            <title><![CDATA[It takes two to ChaCha (Poly)]]></title>
            <link>https://blog.cloudflare.com/it-takes-two-to-chacha-poly/</link>
            <pubDate>Mon, 04 Apr 2016 11:50:49 GMT</pubDate>
            <description><![CDATA[ Not long ago we introduced support for TLS cipher suites based on the ChaCha20-Poly1305 AEAD, for all our customers. Back then those cipher suites were only supported by the Chrome browser and Google's websites, but were in the process of standardization.  ]]></description>
            <content:encoded><![CDATA[ <p>Not long ago we introduced support for TLS cipher suites based on the <a href="/do-the-chacha-better-mobile-performance-with-cryptography">ChaCha20-Poly1305 AEAD</a>, for all our customers. Back then those cipher suites were only supported by the Chrome browser and Google's websites, but were in the process of standardization. We introduced these cipher suites to give end users on mobile devices the best possible performance and security.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/76JfUTKzzJ1Ekd3YCMKaoq/9105193b000f8311ecc51c0e4ce14629/2821666673_99ffefc4fa_z.jpg" />
            
            </figure><p><a href="https://creativecommons.org/licenses/by-nd/2.0/">CC BY-ND 2.0</a> <a href="https://www.flickr.com/photos/edwinylee/2821666673/in/photolist-5ikMcp-bAg3F4-e6jkMk-e6jkZp-6P2N19-jwGM2U-9kgP2-5Uxu5g-uktqX-jr1RU-77dm17-VCRrX-7SuN38-7k7tua-5YMmzn-6YgumF-59e8A2-9rVhgZ-8CJxKR-aLEEYi-5hpUs3-5kCBuv-5UNsd4-DneuNV-8CJsXa-8CJrir-Cx78Fi-DsbZ29-Cx8w9x-8CMz3w-D3oryJ-aLEBzX-4cta2C-8CMADj-9rVgpc-5kCBy8-9rVeBP-8CJxwv-8CJyRr-8CJxVH-8CJxhi-8CJrtr-8CMy1o-8CMBgY-8CJuu4-8CJv4t-Cx74wB-8CMzFq-8CJts6-rpdreQ">image</a> by <a href="https://www.flickr.com/photos/edwinylee/">Edwin Lee</a></p><p>Today the standardization process is all but complete and implementations of the most recent specification of the cipher suites have begun to surface. Firefox and OpenSSL have both implemented the new cipher suites for upcoming versions, and Chrome updated its implementation as well.</p><p>We, as pioneers of ChaCha20-Poly1305 adoption on the web, also updated our <a href="https://github.com/cloudflare/sslconfig">open sourced patch for OpenSSL</a>. It implements both the older "draft" version, to keep supporting millions of users of the existing versions of Chrome, and the newer "RFC" version that supports the upcoming browsers from day one.</p><p>In this blog entry I review the history of ChaCha20-Poly1305, its standardization process, as well as its importance for the future of the web. I will also take a peek at its performance, compared to the other standard AEAD.</p>
    <div>
      <h3>What is an AEAD?</h3>
      <a href="#what-is-an-aead">
        
      </a>
    </div>
    <p>ChaCha20-Poly1305 is an AEAD, Authenticated Encryption with Additional Data cipher. AEADs support two operations: "seal" and "open". Another common AEAD in use for TLS connections is AES-GCM.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/LhV85lpve8IUcgsPIy7pJ/8eee50347c1e4dfb6c1faec4f59749df/5739303393_511a9264b9_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/adactio/5739303393/in/photolist-9KaqWM-7oFA8P-DVB5q9-6rPWNc-7dr2Q6-6rU8bG-73MpAa-ahpmBH-57Aa9w-6rUazb-dUKpD3-8JDz4J-ahs6pC-VBS4Z-qeqvFj-aKMJfk-khMnPh-okCvp5-aXWudP-op4rz1-khKCV2-gKDihZ-8pfaXg-7xs15k-nWn1nW-dVvLrD-kaioud-qCMd9L-5EmRR4-khJYvt-qU1p3X-awaTdB-o6zhX1-peqrqu-e6nWX5-9mSGEZ-cFuzqG-aBBYgA-dfGdDQ-awdzjY-7vfyGt-dVQQYp-5g67ag-95QKtU-fBcZu1-gvFgpu-482uHD-hNJ7Kv-7vjnzL-7iKj4g">image</a> by <a href="https://www.flickr.com/photos/adactio/">Jeremy Keith</a></p><p>The "seal" operation receives the following as input:</p><ol><li><p>The message to be encrypted - this is the plaintext.</p></li><li><p>A secret key.</p></li><li><p>A unique initialization value - aka the IV. It must be unique between invocations of the "seal" operation with the same key, otherwise the secrecy of the cipher is completely compromised.</p></li><li><p>Optionally some other, non-secret, additional data. This data will not be encrypted, but will be authenticated - this is the AD in AEAD.</p></li></ol><p>The "seal" operation uses the key and the IV to encrypt the plaintext into a ciphertext of equal length, using the underlying cipher. For ChaCha20-Poly1305 the cipher is ChaCha20 and for AES-GCM (the other AEAD in common use) the cipher is AES in CounTeR mode (AES-CTR).</p><p>After the data is encrypted, "seal" uses the key (and optionally the IV) to generate a secondary key. The secondary key is used to generate a keyed hash of the AD, the ciphertext and the individual lengths of each. The hash used in ChaCha20-Poly1305, is Poly1305 and in AES-GCM the hash is GHASH.</p><p>The final step is to take the hash value and encrypt it too, generating the final MAC (Message Authentication Code) and appending it to the ciphertext.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2BR7qw6OQK1fvPlEhEN6G6/547d24463da25fadc7885b8c6fddde69/5730883_872a32b09d_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/genista/5730883/in/photolist-vnAk-nbwijY-oqvP2U-djVTUr-4HRo5J-7vyDSj-4ruBSh-4GViwx-upr6h-dTawSs-boDYGK-F3UhJ-nr9o9-8S78ic-9znk6k-5ByUdb-5FwdxE-4zvHb3-rcCeg9-363XxR-6c2mhp-2WP2cp-2WTsWS-2WTuSq-mXTrWc-2WTtid-qMsATA-xj9C8-e1wsgc-euuypv-atjkJQ-Ldsdy-73xJpA-uj5nP-964Fgq-4aFbEx-7zS5wE-7EXkPD-5m2URM-bwMFxR-4tR3xD-9eXn1R-5TeoZP-6oZYqj-8ugZ6e-5m2Xmt-4BuPz9-8Tv15P-4tV4Nj-Gj3Xz">image</a> by <a href="https://www.flickr.com/photos/genista">Kai Schreiber</a></p><p>The "open" operation is the reverse of "seal". It takes the same key and IV and generates the MAC of the ciphertext and the AD, similarly to the way "seal" did. It then reads the MAC appended after the ciphertext, and compares the two. Any difference in the MAC values would mean the ciphertext or the AD was tampered with, and they should be discarded as unsafe. If the two match, however, the operation decrypts the ciphertext, returning the original plaintext.</p>
    <div>
      <h3>What makes AEADs special?</h3>
      <a href="#what-makes-aeads-special">
        
      </a>
    </div>
    <p>AEADs are special in the sense that they combine two algorithms - cipher and MAC, into a single primitive, with provable security guarantees. Before AEADs, it was acceptable to take some cipher and some MAC, which were considered secure independently, and combine them into an insecure combination. For example some combinations were broken by reusing the same keys for encryption and MAC (AES-CBC with CBC-MAC), while others by performing MAC over plaintext instead of the ciphertext <a href="/padding-oracles-and-the-decline-of-cbc-mode-ciphersuites/">AES-CBC with HMAC in TLS</a>.</p>
    <div>
      <h3>The new kid in the block</h3>
      <a href="#the-new-kid-in-the-block">
        
      </a>
    </div>
    <p>Up until recently the only standard AEAD used was AES-GCM. The problem with that, is that if someone breaks the GCM mode, we are left with no alternative - you wouldn't want to jump out of a plane without a backup parachute, would you? ChaCha20-Poly1305 is this backup.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/55zEvzS1lYE9ktrdEuEosi/47cac47d4446d20894e485e6fe408838/3636735800_8670fbf207_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/linasmith/3636735800/in/photolist-6xndRo-6xgauc-4bxmdZ-4P1iZd-5vfNfG-5vfNMy-5rTQCD-4NVQHp-6ct79D-4P1qUy-6cMX5f-4NVRfT-4NWbtP-6cH2JT-5vfQM7-4P15W7-4NVR7z-4NVQUK-6cMgD9-4P1jQw-6cH1Rp-4NVYLa-6cMdWY-4NVQCR-4P1633-4P1vM7-4P1qY1-5vfPuA-5vbu8R-4NWteT-4NWkXt-4P1jD9-4NWbp4-4NVT4n-6cMvnB-cV4Qz7-4NW4At-6cRDd3-4NW5BR-4NW4Ke-4P16td-fnssrZ-4NW5G4-4P1kcN-4P1jGs-4P1wjG-4P1AqE-4NVTRZ-4P1jzu-4NWgBB">image</a> by <a href="https://www.flickr.com/photos/linasmith">lina smith</a></p><p>We can't really call either ChaCha20 or Poly1305 really new. Both are the brain children of Daniel J. Bernstein (DJB). ChaCha20 is based upon an earlier cipher developed by DJB called Salsa, that dates back to 2005, and was submitted to the eSTREAM competition. ChaCha20 itself was published in 2008. It slightly modifies the Salsa round, and the number 20 indicates that it repeats for 20 rounds in total. Similar to AES-CTR, ChaCha20 is a stream cipher. It generates a pseudo-random stream of bits from an incremented counter, the stream is then "XORed" with plaintext to encrypt it (or "XORed" with ciphertext to decrypt). Because you do not need to know the plaintext in advance to generate the stream, this approach allows both to be very efficient and parallelizable. ChaCha20 is a 256-bit cipher.</p><p>Poly1305 was published in 2004. Poly1305 is a MAC, and can be used with any encrypted or unencrypted message, to generate a keyed authentication token. The purpose of such tokens is to guarantee the integrity of a given message. Originally Poly1305 used AES as the underlying cipher (Poly1305-AES); now it uses ChaCha20. Again, similarly to GHASH, it is a polynomial evaluation hash. Unlike GHASH, its key changes for each new message, because it depends on the IV. When DJB developed this MAC, he made it especially suited for efficient execution on the floating point hardware present on the CPUs back then. Today it is much more efficient to execute using 64-bit integer instructions, and might have been designed slightly differently.</p><p>Both have received considerable scrutiny from the crypto community in the years since, and today are considered completely safe, albeit there is a concern about the <a href="http://www.metzdowd.com/pipermail/cryptography/2016-March/028824.html">monoculture</a> that forms when one person is responsible for so many standards in the industry (DJB is also responsible for Curve25519 key exchange).</p>
    <div>
      <h3>From zero to hero</h3>
      <a href="#from-zero-to-hero">
        
      </a>
    </div>
    <p>The body that governs internet standards is the IETF - Internet Engineering Task Force. All the standards we use on the internet, including TLS, come from that organization. All standards that relate to encryption come from the TLS and CFRG workgroups of IETF. The standardization process is open to all, and the correspondence that relates to it is kept public in a special <a href="https://mailarchive.ietf.org/arch/">archive</a>.</p><p>The first mention for ChaCha20-Poly1305 I found in the archive dates to <a href="https://www.ietf.org/mail-archive/web/tls/current/msg09707.html">30 July 2013</a>. It is still referred to as Salsa back then.</p><p>After some time and debate an <a href="https://datatracker.ietf.org/doc/draft-agl-tls-chacha20poly1305/">initial draft</a> was published by Adam Langley from Google in September 2013. The latest draft of the ChaCha20-Poly1305 for TLS including all the previous revisions can be found <a href="https://datatracker.ietf.org/doc/draft-ietf-tls-chacha20-poly1305/">here</a>. It is interesting to see the incremental process, and the gradual refinement. For example initially ChaCha20 was also supposed to work with HMAC-SHA1.</p><p>Another standard that defines the general usage of ChaCha20-Poly1305 is <a href="https://datatracker.ietf.org/doc/rfc7539/">RFC7539</a>. First published in January 2014, it was standardized in May 2015.</p>
    <div>
      <h3>Differences</h3>
      <a href="#differences">
        
      </a>
    </div>
    <p>There are two key differences between the draft version we initially implemented and the current version of the cipher suites that make the two incompatible.</p><p>The first difference relates to how the cipher suite is used in TLS. The current version incorporated the TLS records sequence number into the IV generation, making it more resistant to dangerous IV reuse.</p><p>The second difference relates to how Poly1305 applies to the TLS record. Records are the equivalent of a TCP packet for TLS. When data is streamed over TLS, it is broken into many smaller records. Each record holds part of the data (encrypted), with the MAC calculated for that record. It also holds other information, such as the protocol version, record type and length. The maximum amount of data a single record can hold is 16KB.</p><p>The draft Poly1305 calculated the hash of the additional data, followed by the length of the additional data as an 8 byte string, followed by the ciphertext, followed by the length of the ciphertext as an 8 byte string. In the current iteration, the hash is generated over the additional data, padded with zeroes to 16 byte boundary, followed by ciphertext similarly padded with zeroes, followed by the length of the additional data as an 8 byte string, followed by the length of the ciphertext as an 8 byte string.</p><p>The older cipher suites can be identified by IDs {cc}{13}, {cc}{14} and {cc}{15}, while the newer cipher suites have IDs {cc}{a8} through {cc}{ae}.</p>
    <div>
      <h3>Future of ChaCha20-Poly1305</h3>
      <a href="#future-of-chacha20-poly1305">
        
      </a>
    </div>
    <p>Today we already see that almost 20% of all the request to sites using CloudFlare use <a href="/padding-oracles-and-the-decline-of-cbc-mode-ciphersuites/">ChaCha20-Poly1305</a>. And that is with only one browser supporting it. In the coming months Firefox will join the party, potentially increasing this number.</p><p>More importantly, the IETF is currently finalizing another very important standard, <a href="/going-to-ietf-95-join-the-tls-1-3-hackathon/">TLS 1.3</a>. Right now it looks like TLS 1.3 will allow AEADs only, leaving AES-GCM and ChaCha20-Poly1305 as the only two options (for now). This would definitely bring the usage of ChaCha20-Poly1305 up significantly.</p>
    <div>
      <h3>Can you handle it?</h3>
      <a href="#can-you-handle-it">
        
      </a>
    </div>
    <p>Given the rising popularity of ChaCha20-Poly1305 suites, and TLS in general, it is important to have efficient implementations that does not hog too much of the servers' CPU time. ChaCha20-Poly1305 allows for highly efficient implementation using SIMD instructions. Most of our servers are based on Intel CPUs with 256-bit SIMD extensions called AVX2. We utilize those to get the maximal performance.</p><p>The main competition for ChaCha20-Poly1305 are the AES-GCM based cipher suites. The most widely used AES-GCM, uses AES with 128 bit key, however in terms of security AES-256 is more comparable to ChaCha20.</p><p>Usually cipher performance numbers are reported for large messages, to show asymptotic performance, but on our network we started using <a href="https://istlsfastyet.com/">dynamic record sizing</a>. In practice it means many connections will never reach the maximal size of a TLS record (16KB), but instead will use significantly smaller records (below 1400 bytes). The records dynamically grow as the connection progresses, scaling to about 4KB and eventually to 16KB. Most messages will also not fit precisely into a record, and all sizes are possible.</p><p>Below are two graphs, comparing the performance of our ChaCha20-Poly1305 to the implementation in OpenSSL 1.1.0 pre, and to AES-GCM. The performance is reported in CPU cycles per byte, for a plaintext of given length, when performing the "seal" operation on a given plaintext with 13 bytes of AD, similarly to TLS. The first graph covers sizes 64-1536, while the second covers the remaining sizes to 16KB.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4aCgzCF31FBk2y2x6JALC6/2bcdb455449826306a0ac7997a7947ac/image01.png" />
            
            </figure><p>CPU cycles per byte (Y-axis) vs. record size in bytes (X-axis)</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/67MyIu3q0slgs053DHqy6L/c3bca79b186604388233fe17fbe435d3/image00.png" />
            
            </figure><p>CPU cycles per byte (Y-axis) vs. record size in bytes (X-axis)</p><p>We can see that our implementation significantly outperforms OpenSSL for short records, and is slightly faster for longer records. The average performance advantage is 7%. AES-128-GCM and AES-256-GCM both still beat ChaCha20-Poly1305 in pure performance for records larger than 320 bytes, but getting below 2 cycles/byte is a major performance achievement. Not many modes can beat this performance. It is also important to note that AES-GCM uses two dedicated CPU instructions (AESENC and CLMULQDQ), whereas both ChaCha and Poly only use the generic SIMD instructions.</p>
    <div>
      <h3>Performance outlook</h3>
      <a href="#performance-outlook">
        
      </a>
    </div>
    <p>The current performance is outstanding. We measured the performance on a Haswell CPU. Broadwell and Skylake CPUs actually perform AES-GCM faster, but we don't use them in our servers yet.</p><p>In the future, processors with wider SIMD instructions are expected to bridge the performance gap. The AVX512 will provide instructions twice as wide, and potentially will improve the performance two fold as well, bringing it below 1 cycle/byte. Following AVX512, Intel is expected to release the AVX512IFMA extensions too, that will accelerate Poly1305 even <a href="https://rt.openssl.org/Ticket/Display.html?id=3615&amp;user=guest&amp;pass=guest">further</a>.</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>CloudFlare is constantly pushing the envelope in terms of TLS performance and availability of the most secure cipher suites and modes. We are actively involved in the development and specification of TLS 1.3 and are committed to open source by releasing our performance patches.</p> ]]></content:encoded>
            <category><![CDATA[TLS]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Chrome]]></category>
            <category><![CDATA[Security]]></category>
            <guid isPermaLink="false">6K4djwcZ8vGemZD7mCtRDG</guid>
            <dc:creator>Vlad Krasnov</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[Doubling the speed of jpegtran with SIMD]]></title>
            <link>https://blog.cloudflare.com/doubling-the-speed-of-jpegtran/</link>
            <pubDate>Thu, 08 Oct 2015 10:10:41 GMT</pubDate>
            <description><![CDATA[ It is no secret that at CloudFlare we put a great effort into accelerating our customers' websites. One way to do it is to reduce the size of the images on the website. This is what our Polish product is for.  ]]></description>
            <content:encoded><![CDATA[ <p>It is no secret that at CloudFlare we put a great effort into accelerating our customers' websites. One way to do it is to reduce the size of the images on the website. This is what our <a href="/introducing-polish-automatic-image-optimizati/">Polish</a> product is for. It takes various images and makes them smaller using open source tools, such as jpegtran, gifsicle and pngcrush.</p><p>However those tools are computationally expensive, and making them go faster, makes our servers go faster, and subsequently our customers' websites as well.</p><p>Recently, I noticed that we spent ten times as much time "polishing" jpeg images as we do when polishing pngs.</p><p>We already improved the performance of <a href="https://github.com/cloudflare/pngcrush">pngcrush</a> by using our supercharged version of <a href="/cloudflare-fights-cancer/">zlib</a>. So it was time to look what can be done for jpegtran (part of the <a href="http://www.ijg.org/">libjpeg</a> distribution).</p>
    <div>
      <h3>Quick profiling</h3>
      <a href="#quick-profiling">
        
      </a>
    </div>
    <p>To get fast results I usually use the Linux perf utility. It gives a nice, if simple, view of the hotspots in the code. I used this image for my benchmark.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/etDiZy3jlhasH4jzoAVTD/44548413e30760906deae91d0b367f73/print_poster_0025.jpg" />
            
            </figure><p><a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a> <a href="http://www.eso.org/public/products/print_posters/print_poster_0025/">image</a> by <a href="http://www.eso.org/">ESO</a></p><p><code>perf record ./jpegtran -outfile /dev/null -progressive -optimise -copy none test.jpeg</code></p><p>And we get:</p><p><code>perf report</code><code>54.90% lt-jpegtran libjpeg.so.9.1.0 [.] encode_mcu_AC_refine</code><code>28.58% lt-jpegtran libjpeg.so.9.1.0 [.] encode_mcu_AC_first</code></p><p>Wow. That is 83.5% of the time spent in just two functions! A quick look suggested that both functions deal with Huffman coding of the jpeg image. That is a fairly simple compression technique where frequently occurring characters are replaced with short encodings, and rare characters get longer encodings.</p><p>The time elapsed for the tested file was 6 seconds.</p>
    <div>
      <h3>Where to start?</h3>
      <a href="#where-to-start">
        
      </a>
    </div>
    <p>First let's look at the most used function, <i>encode_mcu_AC_refine</i>. It is the heaviest function, and therefore optimizing it would benefit us the most.</p><p>The function has two loops. The first loop:</p>
            <pre><code>for (k = cinfo-&gt;Ss; k &lt;= Se; k++) {
    temp = (*block)[natural_order[k]];
    if (temp &lt; 0)
      temp = -temp;      /* temp is abs value of input */
    temp &gt;&gt;= Al;         /* apply the point transform */
    absvalues[k] = temp; /* save abs value for main pass */
    if (temp == 1)
      EOB = k;           /* EOB = index of last newly-nonzero coef */
  }</code></pre>
            <p>This loop iterates over one array (<i>natural_order</i>), reads indices that it uses to read from *<i>block</i> into <i>temp</i>, computes the absolute value of <i>temp</i>, and shifts it to the right by some value. In addition it keeps the <i>EOB</i> variable pointing to the last value of the index for which <i>temp</i> was <i>1</i>.</p><p>Generally looping over an array is the schoolbook exercise for SIMD (Single Instruction Multiple Data) instructions, however in this case we have two problems: 1) Indirect indices are hard for SIMD 2) Keeping the <i>EOB</i> value updated.</p>
    <div>
      <h3>Optimizing</h3>
      <a href="#optimizing">
        
      </a>
    </div>
    <p>The first problem can not be solved on the function level. While Haswell introduced the new Gather instructions, that allow loading data using indices, those instructions are still slow, and more importantly they operate only on 32-bit and 64-bit values. We are dealing with 16-bit values here.</p><p>Our only option is therefore to load all the values sequentially. One SIMD XMM register is 128-bits wide and can hold eight 16-bit integers, therefore we will load eight values each iteration of the loop. (Note: we could also choose to use 256-bit wide YMM registers here, but that would only work on the newest CPUs, while gaining little performance).</p>
            <pre><code>    __m128i x1 = _mm_setzero_si128(); // Load 8 16-bit values sequentially
    x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+0]], 0);
    x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+1]], 1);
    x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+2]], 2);
    x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+3]], 3);
    x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+4]], 4);
    x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+5]], 5);
    x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+6]], 6);
    x1 = _mm_insert_epi16(x1, (*block)[natural_order[k+7]], 7);</code></pre>
            <p>What you see above are intrinsic functions. Such intrinsics usually map to a specific CPU instruction. By using intrinsics we can integrate high performance code inside a C function, without writing assembly code. Usually they give more than enough flexibility and performance. The <i>__m128i</i> type corresponds to a 128-bit XMM register.</p><p>Performing the transformation is straightforward:</p>
            <pre><code>    x1 = _mm_abs_epi16(x1);        // Get absolute value of 16-bit integers
    x1 = _mm_srli_epi16(x1, Al);   // &gt;&gt; 16-bit integers by Al bits
    _mm_storeu_si128((__m128i*)&amp;absvalues[k], x1); // Store</code></pre>
            <p>As you can see we only need two instructions to perform the simple transformation on eight values, and no branch is required as a bonus.</p><p>The next tricky part is to update <i>EOB</i>, so it points to the last nonzero index. For that we need to find the index of the highest nonzero value inside <i>x1</i>, and if there is one update <i>EOB</i> accordingly.</p><p>We can easily compare all the values inside x1 to one:</p>
            <pre><code>    x1 = _mm_cmpeq_epi16(x1, _mm_set1_epi16(1));  // Compare to 1</code></pre>
            <p>The result of this operation is a mask. For all equal values all bits are set to 1 and for all non-equal values all bits are cleared to 0. However, how do we find the highest index whose bits are all ones? Well, there is an instruction that extracts the top bit of each byte inside an <i>__m128i</i> value, and puts it into a regular integer. From there we can find the index by finding the top set bit in that integer:</p>
            <pre><code>    unsigned int idx = _mm_movemask_epi8(x1);        // Extract byte mask
    EOB = idx? k + 16 - __builtin_clz(idx)/2 : EOB;  // Compute index</code></pre>
            <p>That is it. EOB will be updated correctly.</p><p>What does that simple optimization give us? Running jpegtran again shows that the same image now takes 5.2 seconds! That is a 1.15x speedup right there. But no reason to stop now.</p>
    <div>
      <h3>Keep going</h3>
      <a href="#keep-going">
        
      </a>
    </div>
    <p>Next the loop iterates over the array we just prepared (<i>absvalues</i>). At the beginning of the loop we see:</p>
            <pre><code>  if ((temp = absvalues[k]) == 0) {
      r++;
      continue;
    }</code></pre>
            <p>So it simply skips all the 0 values in the array, until it finds a nonzero value. Is it worth optimizing? Running a quick test, I found out that it is common to have long sequences of zero values. And iterating over them individually can be expensive. Let's try our previous method of finding the top nonzero element in an array here:</p>
            <pre><code>    __m128i t = _mm_loadu_si128((__m128i*)&amp;absvalues[k]);
    t = _mm_cmpeq_epi16(t, _mm_setzero_si128()); // Compare to 0
    int idx = _mm_movemask_epi8(t);              // Extract byte mask
    if (idx == 0xffff) {                         // If all the values are zero skip them all
      r += 8;
      k += 8;
      continue;
    } else {             // If some are nonzero, skip up to the first nonzero
      int skip = __builtin_ctz(~idx)/2;
      r += skip;
      k += skip;
      if (k&gt;Se) break;   // Stop if gone too far
    }
    temp = absvalues[k]; // Load the first nonzero value</code></pre>
            <p>What do we get now? Our test image now takes only 3.7 seconds. We are already at 1.62x speedup.</p><p>And what does perf show?</p><p><code>45.94% lt-jpegtran libjpeg.so.9.1.0 [.] encode_mcu_AC_first</code><code>28.73% lt-jpegtran libjpeg.so.9.1.0 [.] encode_mcu_AC_refine</code></p><p>We can see that the tables have turned. Now <i>encode_mcu_AC_first</i> is the slower function of the two, and takes almost 50% of the time.</p>
    <div>
      <h3>Moar optimizations!</h3>
      <a href="#moar-optimizations">
        
      </a>
    </div>
    <p>Looking at <i>encode_mcu_AC_first</i> we see one loop, that performs similar operations as the function we already optimized. Why not reuse the optimizations we already know are good?</p><p>We shall split the one big loop into two smaller loops, one would perform the required transformations, and store them into auxiliary arrays, the other would read those values skipping the zero entries.</p><p>Note that transformations are a little different now:</p>
            <pre><code>  if (temp &lt; 0) {
      temp = -temp;
      temp &gt;&gt;= Al;
      temp2 = ~temp;
    } else {
      temp &gt;&gt;= Al;
      temp2 = temp;
    }</code></pre>
            <p>The equivalent for SIMD would be:</p>
            <pre><code>    __m128i neg = _mm_cmpgt_epi16(_mm_setzero_si128(), x1);// Negative mask
    x1 = _mm_abs_epi16(x1);                                // Absolute value
    x1 = _mm_srli_epi16(x1, Al);                           // &gt;&gt;
    __m128i x2 = _mm_andnot_si128(x1, neg);                // Invert x1, and apply negative mask
    x2 = _mm_xor_si128(x2, _mm_andnot_si128(neg, x1));     // Apply non-negative mask</code></pre>
            <p>And skipping zero values is performed in the same manner as before.</p><p>Measuring the performance now gives as 2.65 seconds for the test image! And that is 2.25X faster than the original jpegtran.</p>
    <div>
      <h3>Conclusions</h3>
      <a href="#conclusions">
        
      </a>
    </div>
    <p>Let's take a final look at the perf output:</p><p><code>40.78% lt-jpegtran libjpeg.so.9.1.0 [.] encode_mcu_AC_refine</code><code>26.01% lt-jpegtran libjpeg.so.9.1.0 [.] encode_mcu_AC_first</code></p><p>We can see that the two functions we optimized still take a significant portion of the execution. But now it is just 67% of the entire program. If you are asking yourself how we got 2.25X speedup with those functions going from 83.5% to 67%, read about the <a href="https://en.wikipedia.org/wiki/Potato_paradox">Potato Paradox</a>.</p><p>It is definitely worth making further optimizations here. But it might be better looking outside the functions into how the data is arranged and passed around.</p><p>The bottom line is: optimizing code is fun, and using SIMD instructions can give you significant boost. Such as in this case.</p><p>We at CloudFlare make sure that our servers run at top notch performance, so our customers' websites do as well!.</p><p>You can find the final code on our GitHub repository: <a href="https://github.com/cloudflare/jpegtran">https://github.com/cloudflare/jpegtran</a></p> ]]></content:encoded>
            <category><![CDATA[Speed]]></category>
            <category><![CDATA[Cloudflare Polish]]></category>
            <category><![CDATA[Open Source]]></category>
            <category><![CDATA[Optimization]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Programming]]></category>
            <guid isPermaLink="false">4HR5elkONeiCUjDAOCBRNk</guid>
            <dc:creator>Vlad Krasnov</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[Go crypto: bridging the performance gap]]></title>
            <link>https://blog.cloudflare.com/go-crypto-bridging-the-performance-gap/</link>
            <pubDate>Thu, 07 May 2015 10:06:00 GMT</pubDate>
            <description><![CDATA[ It is no secret that we at CloudFlare love Go. We use it, and we use it a LOT. There are many things to love about Go, but what I personally find appealing is the ability to write assembly code! ]]></description>
            <content:encoded><![CDATA[ <p>It is no secret that we at CloudFlare love Go. We use it, and we use it a LOT. There are many things to love about Go, but what I personally find appealing is the ability to write assembly code!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7KXkUfdeZkkc1sWRj7cMWM/0169fe43ac4fca720cc5e3c048bd913f/4238725400_58e0df7d8a_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/curns/4238725400/in/photolist-7syzoq-bX6MBP-7MB6AP-bV6qYW-JKJyC-jwtSkq-eaezFL-cYsmkE-ndmMRH-2EMNd1-oW5DRv-N7SWP-62CbPG-mm4X-5hUghN-bUdHXE-awERSL-5rx4ST-nUJ7Za-ewYVyy-diiyTv-pprT55-6tuZgp-fQDkYD-fQDkUF-bMRznn-3VAiJx-bvGxVF-7S8RZX-pcqyM9-6g4iYb-8NwRYU-cwiigy-9PkavU-61G39m-4bnYXH-d2QTGb-32J1co-q2VB1P-nGbBjk-rnm8s8-nT6b6W-88u81w-9aWDrY-9C5Anx-nT6TBk-oah7x8-oarCdA-pb3eXW-68dr8i">image</a> by <a href="https://www.flickr.com/photos/curns/">Jon Curnow</a></p><p>That is probably not the first thing that pops to your mind when you think of Go, but yes, it does allow you to write code "close to the metal" if you need the performance!</p><p>Another thing we do a lot in CloudFlare is... cryptography. To keep your data safe we encrypt everything. And everything in CloudFlare is a LOT.</p><p>Unfortunately the built-in cryptography libraries in Go do not perform nearly as well as state-of-the-art implementations such as OpenSSL. That is not acceptable at CloudFlare's scale, therefore we created assembly implementations of <a href="/ecdsa-the-digital-signature-algorithm-of-a-better-internet/">Elliptic Curves</a> and AES-GCM for Go on the amd64 architecture, supporting the AES and CLMUL NI to bring performance up to par with the OpenSSL implementation we use for <a href="/universal-ssl-how-it-scales/">Universal SSL</a>.</p><p>We have been using those improved implementations for a while, and attempting to make them part of the official Go build for the good of the community. For now you can use our <a href="https://github.com/cloudflare/go">special fork of Go</a> to enjoy the improved performance.</p><p>Both implementations are constant-time and side-channel protected. In addition the fork includes small improvements to Go's RSA implementation.</p><p>The performance benefits of this fork over the standard Go 1.4.2 library on the tested Haswell CPU are:</p>
            <pre><code>                         CloudFlare          Go 1.4.2        Speedup
AES-128-GCM           2,138.4 MB/sec          91.4 MB/sec     23.4X

P256 operations:
Base Multiply           26,249 ns/op        737,363 ns/op     28.1X
Multiply/ECDH comp     110,003 ns/op      1,995,365 ns/op     18.1X
Generate Key/ECDH gen   31,824 ns/op        753,174 ns/op     23.7X
ECDSA Sign              48,741 ns/op      1,015,006 ns/op     20.8X
ECDSA Verify           146,991 ns/op      3,086,282 ns/op     21.0X

RSA2048:
Sign                 3,733,747 ns/op      7,979,705 ns/op      2.1X
Sign 3-prime         1,973,009 ns/op      5,035,561 ns/op      2.6X</code></pre>
            
    <div>
      <h3>AES-GCM in a brief</h3>
      <a href="#aes-gcm-in-a-brief">
        
      </a>
    </div>
    <p>So what is AES-GCM and why do we care? Well, it is an AEAD - Authenticated Encryption with Associated Data. Specifically AEAD is a special combination of a cipher and a MAC (Message Authentication Code) algorithm into a single robust algorithm, using a single key. This is different from the other method of performing authenticated encryption "encrypt-then-MAC" (or as TLS does it with CBC-SHAx, "MAC-then-encrypt"), where you can use any combination of cipher and MAC.</p><p>Using a dedicated AEAD reduces the dangers of bad combinations of ciphers and MACs, and other mistakes, such as using related keys for encryption and authentication.</p><p>Given the <i>many</i> vulnerabilities related to the use of AES-CBC with HMAC, and the weakness of RC4, AES-GCM is the de-facto secure standard on the web right now, as the only IETF-approved AEAD to use with TLS at the moment.</p><p>Another AEAD you may have heard of is <a href="/do-the-chacha-better-mobile-performance-with-cryptography/">ChaCha20-Poly1305</a>, which CloudFlare also supports, but it is not a standard just yet.</p><p>That is why we use AES-GCM as the preferred cipher for customer HTTPS only prioritizing ChaCha20-Poly1305 for mobile browsers that support it. You can see it in our <a href="https://github.com/cloudflare/sslconfig">configuration</a>. As such today more than 60% of our client facing traffic is encrypted with AES-GCM, and about 10% is encrypted with ChaCha20-Poly1305. This percentage grows every day, as browser support improves. We also use AES-GCM to encrypt all the traffic between our data centers.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1YfENR53QhCOS7e6p5hxww/82283b52e997187cfa7c4a3ed9b80448/2564482934_d26c31c011_z-1.jpg" />
            
            </figure><p><a href="https://creativecommons.org/licenses/by/2.0/">CC BY 2.0</a> <a href="https://www.flickr.com/photos/319/2564482934/in/photolist-5FYmSD-7zvQwN-6ATEuo-4VgpgZ-kUSNp3-6wAN3r-c3LXyY-c3LXpW-4S2DaR-4UBDr1-4UxqxR-82A7UM-4VgH6r-6cXGRY/">image</a> by <a href="https://www.flickr.com/photos/319/">3:19</a></p>
    <div>
      <h3>AES-GCM as an AEAD</h3>
      <a href="#aes-gcm-as-an-aead">
        
      </a>
    </div>
    <p>As I mentioned AEAD is a special combination of a cipher and a MAC. In the case of AES-GCM the cipher is the AES block cipher in <a href="http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29">Counter Mode</a> (AES-CTR). For the MAC it uses a universal hash called <a href="http://en.wikipedia.org/wiki/Galois/Counter_Mode#Encryption_and_authentication">GHASH</a>, encrypted with AES-CTR.</p><p>The inputs to the AES-GCM AEAD encryption are as follows:</p><ul><li><p>The secret key (K), that may be 128, 192 or 256 bit long. In TLS, the key is usually valid for the entire connection.</p></li><li><p>A special unique value called IV (initialization value) - in TLS it is 96 bits. The IV is not secret, but the same IV may not be used for more than one message with the same key under any circumstance! To achieve that, usually part of the IV is generated as a nonce value, and the rest of it is incremented as a counter. In TLS the IV counter is also the record sequence number. The IV of GCM is unlike the IV in CBC mode, which must also be unpredictable.The disadvantage of using this type of IV, is that in order to avoid collisions, one must change the encryption key, before the IV counter overflows.</p></li><li><p>The additional data (A) - this data is not secret, and therefore not encrypted, but it is being authenticated by the GHASH. In TLS the additional data is 13 bytes, and includes data such as the record sequence number, type, length and the protocol version.</p></li><li><p>The plaintext (P) - this is the secret data, it is both encrypted and authenticated.</p></li></ul><p>The operation outputs the ciphertext (C) and the authentication tag (T). The length of C is identical to that of P, and the length of T is 128 bits (although some applications allow for shorter tags). The tag T is computed over A and C, so if even a single bit of either of them is changed, the decryption process will detect the tampering attempt and refuse to use the data. In TLS, the tag T is attached at the end of the ciphertext C.</p><p>When decrypting the data, the function will receive A, C and T and compute the authentication tag of the received A and C. It will compare the resulting value to T, and only if both are equal it will output the plaintext P.</p><p>By supporting the two state of the art AEADs - AES-GCM and ChaCha20-Poly1305, together with <a href="https://www.cloudflare.com/learning/dns/dnssec/ecdsa-and-dnssec/">ECDSA</a> and ECDH algorithms, CloudFlare is able to provide the fastest, most flexible and most secure TLS experience possible to date on all platforms, be it a PC or a mobile phone.</p>
    <div>
      <h3>Bottom line</h3>
      <a href="#bottom-line">
        
      </a>
    </div>
    <p>Go is a very easy to learn and fun to use, yet it is one of the most powerful languages for system programming. It allows us to deliver robust web-scale software in short time frames. With the performance improvements CloudFlare brings to its crypto stack, Go can now be used for high performance TLS servers as well!</p> ]]></content:encoded>
            <category><![CDATA[TLS]]></category>
            <category><![CDATA[Elliptic Curves]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[OpenSSL]]></category>
            <category><![CDATA[SSL]]></category>
            <category><![CDATA[Go]]></category>
            <category><![CDATA[Programming]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Cryptography]]></category>
            <guid isPermaLink="false">2pfrw28XfVhe2XpPETRNAm</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[Improving PicoHTTPParser further with AVX2]]></title>
            <link>https://blog.cloudflare.com/improving-picohttpparser-further-with-avx2/</link>
            <pubDate>Thu, 18 Dec 2014 16:21:00 GMT</pubDate>
            <description><![CDATA[ In a recent post, Kazuho's Weblog describes an improvement to PicoHTTPParser. This improvement utilizes the SSE4.2 instruction PCMPESTRI in order to find the delimiters in a HTTP request/response and parse them accordingly.  ]]></description>
            <content:encoded><![CDATA[ <p><i>Vlad Krasnov recently joined CloudFlare to work on low level optimization of CloudFlare's servers. This is the first of a number of blog posts that will include code he's optimized and open sourced.</i></p><p>In a recent post, <a href="http://blog.kazuhooku.com/2014/12/improving-parser-performance-using-sse.html">Kazuho's Weblog</a> describes an improvement to <a href="https://github.com/h2o/picohttpparser">PicoHTTPParser</a>. This improvement utilizes the SSE4.2 instruction <a href="http://www.felixcloutier.com/x86/PCMPESTRI.html">PCMPESTRI</a> in order to find the delimiters in a HTTP request/response and parse them accordingly. This update, compared to the previous version of the code, is impressive.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3hrbNOuPzx7yQUXbH6wgC2/fbfcbfdb196abbf648e3b95102b31ff4/11359813146_2b5b7f815e_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/intelfreepress/11359813146/">image</a> by <a href="https://www.flickr.com/photos/intelfreepress/">Intel Free Press</a></p><p>PCMPESTRI is a versatile instruction that allows scanning of up to 16 bytes at once for occurrences of up to 16 distinct characters (bytes), or up to 8 ranges of characters (bytes). It can also be used for string and substring comparison. However, there are a few drawbacks: the instruction has a high latency of 11 cycles, and is limited to 16 bytes per instruction. It's also under utilized for range comparison in PicoHTTPParser, because it only tests two or three ranges per invocation (out of eight it is capable of). Furthermore, some simple math (16 bytes / 11 cycles) shows that using this instruction limits the parser to 1.45 bytes/cycle throughput.</p>
    <div>
      <h3>Is this really as fast as this parser can go?</h3>
      <a href="#is-this-really-as-fast-as-this-parser-can-go">
        
      </a>
    </div>
    <p>Today on the latest Haswell processors, we have the potent <a href="http://en.wikipedia.org/wiki/Advanced_Vector_Extensions">AVX2</a> instructions. The AVX2 instructions operate on 32 bytes, and most of the boolean/logic instructions perform at a throughput of 0.5 cycles per instruction. This means that we can execute roughly 22 AVX2 instructions in the same amount of time it takes to execute a single PCMPESTRI. Why not give it a shot?</p><p>Let's look at the logic first. If, for example, we search for bytes in the ranges 0-8, 10-31, and 127, we see that to convert the PCMPESTRI logic to AVX2 logic we first need to check for each byte X: <code>((-1 &lt; X &lt; 32) AND !(X = 9)) OR (X = 127)</code>. Luckily, we can treat the bytes as signed because the AVX2 instruction set does not provide a single instruction for unsigned 'greater equal' operations. This logic, when implemented with AVX2 instructions, would mark all the qualifying bytes. Second, to get the position of the first qualifying byte, as PCMPESTRI does, we should use the <a href="http://www.felixcloutier.com/x86/PMOVMSKB.html">PMOVMSKB</a> instructions to get the byte mask of the AVX2 register, and then apply the new <a href="http://www.felixcloutier.com/x86/TZCNT.html">TZCNT</a> instruction (to get the position of the first set bit in the register). The latter two instructions have a latency of 3 cycles each, and the logic itself has a latency of around 4-5 cycles.</p><p>In theory, we traded a single instruction that has latency of 11 cycles for a complex flow with a theoretical latency of 10 cycles. But we would make it up in throughput, right? Apparently not. This is because the fields in the HTTP request are usually short so we don't gain from the increased throughput.</p>
    <div>
      <h3>So what can we do?</h3>
      <a href="#so-what-can-we-do">
        
      </a>
    </div>
    <p>The solution is to change the logical flow of the program from latency bound to throughput oriented. Instead of searching for the next occurrence of a token, we create a bitmap of all the occurrences in a long string. We did this when we used the PMOVMSKB instruction before; however, instead of discarding all but the first bit, we use all the bits! Moreover, to gain more from the high throughput of the AVX2 instructions, we created a bitmask for 128 bytes of the request at a time, and in addition, we create bitmasks for two types of token together—both the name/value delimiter and the new line delimiter.</p><p>To parse the resulting bit string, we use the TZCNT instruction repeatedly resulting in improved performance. This was done by compiling with gcc 4.9.2, with <code>-mavx2 -mbmi2 -O3</code> for both versions. Here are the results from Haswell i5-4278U, with turbo disabled for consistency:</p>
            <pre><code>             PCMPESTRI   AVX2       Improvement
bench.c      3,900,156   6,963,788  1.79x
fukamachi.c  4,807,692   8,064,516  1.68x</code></pre>
            <p>While we managed to squeeze significant performance from the parser by using AVX2 instructions, there is still plenty of room to improve the code—feel free to try it yourself!</p><p>To read the actual code behind this blog post see <a href="https://github.com/h2o/picohttpparser/pull/10">this pull request</a>.</p> ]]></content:encoded>
            <category><![CDATA[Speed & Reliability]]></category>
            <guid isPermaLink="false">1ytvvfoqWmuEvGZWOMKBQ8</guid>
            <dc:creator>Vlad Krasnov</dc:creator>
        </item>
    </channel>
</rss>