<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blosc Home Page  (Posts about Blosc2)</title><link>https://blosc.org/</link><description></description><atom:link href="https://blosc.org/categories/blosc2.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 &lt;a href="mailto:blosc@blosc.org"&gt;The Blosc Developers&lt;/a&gt; </copyright><lastBuildDate>Sun, 19 Jul 2026 11:09:12 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>The Surprising Speed of Compressed Data: A Roofline Story</title><link>https://blosc.org/posts/roofline-analysis-blosc2/</link><dc:creator>Francesc Alted</dc:creator><description>&lt;p&gt;Can a library designed for computing with compressed data ever hope to outperform highly optimized numerical engines like NumPy and Numexpr? The answer is complex, and it hinges on the "memory wall" — a phenomenon which occurs when system memory limitations start to drag on CPU. This post uses Roofline analysis to explore this very question, dissecting the performance of Blosc2 and revealing the surprising scenarios where it can gain a competitive edge.&lt;/p&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update on 2026-02-06:&lt;/strong&gt; We have published a follow-up post, &lt;a class="reference external" href="https://ironarray.io/blog/miniexpr-powered-blosc2"&gt;Python-Blosc2 4.0: Unleashing Compute Speed with miniexpr&lt;/a&gt;, which revisits this topic. This new post explains how the integration of miniexpr into Blosc2's compute engine has significantly improved performance—especially for in-memory operations—updating the conclusions drawn in this original analysis. We highly recommend reading the new post for the latest insights.&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="tl-dr"&gt;
&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;Before we dive in, here's what we discovered:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;For in-memory tasks, Blosc2's overhead can make it slower than Numexpr, especially on x86 CPUs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This changes on Apple Silicon, where Blosc2's performance is much more competitive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For on-disk tasks, Blosc2 consistently outperforms NumPy/Numexpr on both platforms.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The "memory wall" is real, and disk I/O is an even bigger one, which is where compression shines.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="a-trip-down-memory-lane"&gt;
&lt;h2&gt;A Trip Down Memory Lane&lt;/h2&gt;
&lt;p&gt;Let's rewind to 2008. NumPy 1.0 was just a toddler, and the computing world was buzzing with the arrival of multi-core CPUs and their shiny new SIMD instructions. On the &lt;a class="reference external" href="https://mail.python.org/archives/list/numpy-discussion@python.org/thread/YPX5PGM5WZXQAMQ5AZLLEU67D5RZBOVH/#YFX3G2RYHTIYMFDPCHKHED5F7CT4OTVK"&gt;NumPy mailing list&lt;/a&gt;, a group of us were brainstorming how to harness this new power to make Python's number-crunching faster.&lt;/p&gt;
&lt;p&gt;The idea seemed simple: trust newer compilers to use SIMD (and, possibly, data alignment) to perform operations on multiple data points at once. To test this, a &lt;a class="reference external" href="https://mail.python.org/archives/list/numpy-discussion@python.org/message/S2IEJV7U7TXHQLEMORGME6KIGRZTG33L/"&gt;simple benchmark&lt;/a&gt; was shared: multiply two large vectors element-wise. Developers from around the community ran the code and shared their results. What came back was a revelation.&lt;/p&gt;
&lt;p&gt;For small arrays that fit snugly into the CPU's high-speed cache, SIMD was quite good at accelerating computations. But as soon as the arrays grew larger, the performance boost vanished. Some of us were already suspicious about the new "memory wall" that had been growing lately, seemingly due to the widening gap between CPU speeds and memory bandwidth.  However, a conclusive answer (and solution) was still lacking.&lt;/p&gt;
&lt;p&gt;But amidst the confusion, a curious anomaly emerged. One machine, belonging to NumPy legend Charles Harris, was consistently outperforming the rest—even those with faster processors. It made no sense. We checked our code, our compilers, everything. Yet, his machine remained inexplicably faster. The answer, when it finally came, wasn't in the software at all. Charles, a hardware wizard, had &lt;a class="reference external" href="https://mail.python.org/archives/list/numpy-discussion@python.org/message/YFX3G2RYHTIYMFDPCHKHED5F7CT4OTVK/"&gt;tinkered with his BIOS to overclock his RAM&lt;/a&gt; from 667 MHz to a whopping 800 MHz.&lt;/p&gt;
&lt;p&gt;That was my lightbulb moment: for data-intensive tasks, raw CPU clock speed was not the limiting factor; memory bandwidth was what truly mattered.&lt;/p&gt;
&lt;p&gt;This led me to a wild idea: what if we could make memory &lt;em&gt;effectively&lt;/em&gt; faster? What if we could compress data in memory and decompress it on-the-fly, just in time for the CPU? This would &lt;a class="reference external" href="https://www.blosc.org/docs/StarvingCPUs-CISE-2010.pdf"&gt;slash the amount of data being moved&lt;/a&gt;, boosting our effective memory bandwidth. That idea became the seed for &lt;a class="reference external" href="https://www.blosc.org"&gt;Blosc&lt;/a&gt;, a project I started in 2010 that has been &lt;a class="reference external" href="https://github.com/Blosc/python-blosc2"&gt;my passion ever since&lt;/a&gt;. Now, 15 years later, it is time to revisit that idea and see how well it holds up in today's computing landscape.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="roofline-model-understanding-the-memory-wall"&gt;
&lt;h2&gt;Roofline Model: Understanding the Memory Wall&lt;/h2&gt;
&lt;p&gt;Not all computations are equally affected by the memory wall - in general performance can be either CPU-bound or memory-bound. To diagnose which resource is the limiting factor, the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Roofline_model"&gt;Roofline model&lt;/a&gt; provides an insightful analytical framework. This model &lt;a class="reference external" href="https://docs.nersc.gov/tools/performance/roofline/"&gt;plots computational performance against arithmetic intensity&lt;/a&gt; (i.e. floating-point operations per second versus memory accesses per second) to visually determine whether a task is constrained by CPU speed or memory bandwidth.&lt;/p&gt;
&lt;img alt="/images/roofline-surprising-story/roofline-intro.avif" src="https://blosc.org/images/roofline-surprising-story/roofline-intro.avif"&gt;
&lt;p&gt;We will use Roofline plots to analyze Blosc2's performance, compared to that of NumPy and Numexpr. NumPy, with its highly optimized linear algebra backends, and Numexpr, with its efficient evaluation of element-wise expressions, together form a strong performance baseline for the full range of arithmetic intensities tested.&lt;/p&gt;
&lt;p&gt;To highlight the role of memory bandwidth, we will conduct our benchmarks on an AMD Ryzen 7800X3D CPU at two different memory speeds: the standard 4800 MTS and an overclocked 6000 MTS. This allows us to directly observe how memory frequency impacts computational performance.&lt;/p&gt;
&lt;p&gt;To cover a range of computational scenarios, our benchmarks include five operations with varying arithmetic intensities:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Very Low&lt;/strong&gt;: A simple element-wise addition (a + b + c).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Low&lt;/strong&gt;: A moderately complex element-wise expression (sqrt(a + 2 * b + (c / 2)) ^ 1.2).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Medium&lt;/strong&gt;: A highly complex element-wise calculation involving trigonometric and exponential functions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;High&lt;/strong&gt;: Matrix multiplication on small matrices (labeled matmul0).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Very High&lt;/strong&gt;: Matrix multiplication on large matrices (labeled matmul1 and matmul2).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt="/images/roofline-surprising-story/roofline-mem-speed-AMD-7800X3D.png" src="https://blosc.org/images/roofline-surprising-story/roofline-mem-speed-AMD-7800X3D.png"&gt;
&lt;p&gt;The Roofline plot confirms that increasing memory speed only benefits memory-bound operations (low arithmetic intensity), while CPU-bound tasks (high arithmetic intensity) are unaffected, as expected. Although this might suggest the "memory wall" is not a major obstacle, low-intensity operations like element-wise calculations, reductions, and selections are extremely common and often create performance bottlenecks. Therefore, optimizing for memory performance remains crucial.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-in-memory-surprise-why-wasn-t-compression-faster"&gt;
&lt;h2&gt;The In-Memory Surprise: Why Wasn't Compression Faster?&lt;/h2&gt;
&lt;p&gt;We benchmarked Blosc2 (both compressed and uncompressed) against NumPy and Numexpr. For this test, Blosc2 was configured with the LZ4 codec and shuffle filter, a setup known for its balance of speed and compression ratio.  The benchmarks were executed on an AMD Ryzen 7800X3D CPU with memory speed set to 6000 MTS, ensuring optimal memory bandwidth for the tests.&lt;/p&gt;
&lt;img alt="/images/roofline-surprising-story/roofline-7800X3D-mem-def.png" src="https://blosc.org/images/roofline-surprising-story/roofline-7800X3D-mem-def.png"&gt;
&lt;p&gt;The analysis reveals a surprising outcome: for memory-bound operations, Blosc2 is up to five times slower than Numexpr. Although operating on compressed data provides a marginal improvement over uncompressed Blosc2, it is not enough to overcome this performance gap. This result is unexpected because Blosc2 leverages Numexpr internally, and the reduced memory bandwidth from compression should theoretically lead to better performance in these scenarios.&lt;/p&gt;
&lt;p&gt;To understand this counter-intuitive result, we must examine Blosc2's core architecture. The key lies in its double partitioning scheme, which, while powerful, introduces an overhead that can negate the benefits of compression in memory-bound contexts.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="unpacking-the-overhead-a-look-inside-blosc2-s-architecture"&gt;
&lt;h2&gt;Unpacking the Overhead: A Look Inside Blosc2's Architecture&lt;/h2&gt;
&lt;p&gt;The performance characteristics of Blosc2 are rooted in its double partitioning architecture, which organizes data into chunks and blocks.&lt;/p&gt;
&lt;img alt="/images/roofline-surprising-story/double-partition-b2nd.avif" src="https://blosc.org/images/roofline-surprising-story/double-partition-b2nd.avif"&gt;
&lt;p&gt;This design is crucial for both aligning with the CPU's memory hierarchy and enabling efficient multidimensional array representation (important for things like e.g. n-dimensional slicing). However, this structure introduces an inherent overhead from additional indexing logic. In memory-bound scenarios, this latency counteracts the performance gains from reduced memory traffic, explaining why Blosc2 does not surpass Numexpr.&lt;/p&gt;
&lt;p&gt;Conversely, as arithmetic intensity increases, the computational demands begin to dominate the total execution time. In these CPU-bound regimes, the partitioning overhead is effectively amortized, allowing Blosc2 to close the performance gap and eventually match NumPy's performance in tasks like large matrix multiplications.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="modern-arm-architectures"&gt;
&lt;h2&gt;Modern ARM Architectures&lt;/h2&gt;
&lt;p&gt;CPU architecture is a rapidly evolving field. To investigate how these changes impact performance, we extended our analysis to the Apple Silicon M4 Pro, a modern ARM-based processor.&lt;/p&gt;
&lt;img alt="/images/roofline-surprising-story/roofline-m4pro-mem-def.png" src="https://blosc.org/images/roofline-surprising-story/roofline-m4pro-mem-def.png"&gt;
&lt;p&gt;The results show that Blosc2 performs significantly better on this platform, narrowing the performance gap with NumPy/NumExpr, especially for operations on compressed data. While compute engines optimized for uncompressed data still hold an edge, these findings suggest that compression will play an increasingly important role in improving computational performance in the future.&lt;/p&gt;
&lt;p&gt;However, while the in-memory results are revealing, they don't tell the whole story. Blosc2 was designed not just to fight the memory wall, but to conquer an even greater bottleneck: disk I/O. Although compression has the benefit of fitting more data into RAM when used in-memory (which is per se extremely interesting in these times, where &lt;a class="reference external" href="https://arstechnica.com/gadgets/2025/11/spiking-memory-prices-mean-that-it-is-once-again-a-horrible-time-to-build-a-pc/"&gt;RAM prices skyrocketed&lt;/a&gt;), its true power is unleashed when computations move off-motherboard. Now, let's shift the battlefield to the disk and see how Blosc2 performs in its native territory.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="a-different-battlefield-blosc2-shines-with-on-disk-data"&gt;
&lt;h2&gt;A Different Battlefield: Blosc2 Shines with On-Disk Data&lt;/h2&gt;
&lt;p&gt;Blosc2's architecture extends its computational engine to operate seamlessly on data stored on disk, a significant advantage for large-scale analysis.  This is particularly relevant in scenarios where datasets exceed available memory, necessitating out-of-core processing, as commonly encountered in data science, machine learning workflows or &lt;a class="reference external" href="https://ironarray.io/cat2cloud"&gt;cloud computing environments&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our on-disk benchmarks were designed to use datasets larger than the system's available memory to prevent filesystem caching from influencing the results. To establish a baseline, we implemented an out-of-core solution for NumPy/NumExpr, leveraging memory-mapped files. Here Blosc2 has a performance edge, particularly for memory-bound operations on compressed data, being able to send and receive data faster to disk than the memory-mapped NumPy arrays.&lt;/p&gt;
&lt;p&gt;In this case, we've used high-performance NVMe SSDs (NVMe 4.0) to minimize the impact of disk speed on the results.  We also switched to the ZSTD codec for Blosc2, as its superior compression ratio over LZ4 further minimizes data transfer to and from the disk.&lt;/p&gt;
&lt;p&gt;First, let's see the results for the AMD Ryzen 7800X3D system:&lt;/p&gt;
&lt;img alt="/images/roofline-surprising-story/roofline-7800X3D-disk-def.png" src="https://blosc.org/images/roofline-surprising-story/roofline-7800X3D-disk-def.png"&gt;
&lt;p&gt;The plots above show that Blosc2 outperforms both NumPy and Numexpr for all low-to-medium intensity operations. This is because the high latency of disk I/O amortizes the overhead of Blosc2's double partitioning scheme. Furthermore, the reduced bandwidth required for compressed data gives Blosc2 an additional performance advantage in this scenario.&lt;/p&gt;
&lt;p&gt;Now, let's see the results for the Apple Silicon M4 Pro system:&lt;/p&gt;
&lt;img alt="/images/roofline-surprising-story/roofline-m4pro-disk-def.png" src="https://blosc.org/images/roofline-surprising-story/roofline-m4pro-disk-def.png"&gt;
&lt;p&gt;On the Apple Silicon M4 Pro system, Blosc2 again outperforms both NumPy and Numexpr for all on-disk operations, mirroring the results from the AMD system. However, the performance advantage is even more significant here, especially for memory-bound tasks. This is mainly because memory-mapped arrays are less efficient on Apple Silicon than on x86_64 systems, increasing the overhead for the NumPy/Numexpr baseline.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="roofline-plot-in-memory-vs-on-disk"&gt;
&lt;h2&gt;Roofline Plot: In-Memory vs On-Disk&lt;/h2&gt;
&lt;p&gt;To better understand the trade-offs between in-memory and on-disk processing with Blosc2, the following plot contrasts their performance characteristics for compressed data:&lt;/p&gt;
&lt;img alt="/images/roofline-surprising-story/roofline-mem-disk-def.png" src="https://blosc.org/images/roofline-surprising-story/roofline-mem-disk-def.png"&gt;
&lt;p&gt;A notable finding for the AMD system is that Blosc2's on-disk operations are noticeably faster than its in-memory operations, especially for memory-bound tasks (low arithmetic intensity). This is likely due to two factors: first, the larger datasets used for on-disk tests allow Blosc2 to use more efficient internal partitions (chunks and blocks), and second, parallel data reads from disk further reduce bandwidth requirements.&lt;/p&gt;
&lt;p&gt;In contrast, for CPU-bound tasks (high arithmetic intensity), on-disk performance is comparable to, albeit slightly slower than, in-memory performance. The analysis also reveals a specific weakness: small matrix multiplications (matmul0) are significantly slower on-disk, identifying a clear target for future optimization.&lt;/p&gt;
&lt;p&gt;In contrast to the AMD system, the Apple Silicon M4 Pro shows that Blosc2's on-disk operations are slower than in-memory, a difference that is most significant for memory-bound tasks. This performance disparity suggests that current on-disk optimizations may favor x86_64 architectures over ARM.&lt;/p&gt;
&lt;p&gt;As with the AMD platform, CPU-bound operations exhibit similar performance for both on-disk and in-memory contexts. The notable exception remains the small matrix multiplication (matmul0), which performs significantly worse on-disk. This recurring pattern pinpoints a clear opportunity for future optimization efforts.&lt;/p&gt;
&lt;p&gt;Finally, and in addition to its on-disk performance, Blosc2 offers a significant cost advantage. With the &lt;a class="reference external" href="https://arstechnica.com/gadgets/2025/11/spiking-memory-prices-mean-that-it-is-once-again-a-horrible-time-to-build-a-pc/"&gt;recent rise in SSD prices&lt;/a&gt;, compressing data on disk becomes an economically attractive strategy, allowing you to store more data in less space and thereby reduce hardware expenses.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="reproducibility"&gt;
&lt;h2&gt;Reproducibility&lt;/h2&gt;
&lt;p&gt;All the &lt;a class="reference external" href="https://github.com/Blosc/python-blosc2/blob/main/bench/ndarray/roofline-analysis.py"&gt;benchmarks&lt;/a&gt; and &lt;a class="reference external" href="https://github.com/Blosc/python-blosc2/blob/main/bench/ndarray/roofline-plot.py"&gt;plots&lt;/a&gt; presented in this blog post can be reproduced. You are invited to run the scripts on your own hardware to explore the performance characteristics of Blosc2 in different environments. In case you get interesting results, please consider sharing them with the community!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conclusions"&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;In this blog post, we explored the Roofline model to analyze the performance of Blosc2, NumPy, and Numexpr. We've confirmed that memory-bound operations are significantly affected by the "memory wall", making data compression of interest when maximizing performance. However, for in-memory operations, the overhead of Blosc2's double partitioning scheme can be a limiting factor, especially on x86_64 architectures. Encouragingly, this performance gap narrows considerably on modern ARM platforms like Apple Silicon, suggesting a promising future.&lt;/p&gt;
&lt;p&gt;The situation changes dramatically for on-disk operations. Here, Blosc2 consistently outperforms NumPy and Numexpr, as the high latency of disk I/O (even if we used SSDs here) amortizes its internal overhead. This makes Blosc2 a compelling choice for out-of-core computations, one of its primary use cases.&lt;/p&gt;
&lt;p&gt;Overall, this analysis has provided valuable insights, highlighting the importance of the memory hierarchy. It has also exposed specific areas for improvement, such as the performance of small matrix multiplications. As Blosc2 continues to evolve, I am confident we can address these points and further enhance its performance, making it an even more powerful tool for numerical computations in Python.&lt;/p&gt;
&lt;hr class="docutils"&gt;
&lt;p&gt;Read more about &lt;a class="reference external" href="https://ironarray.io"&gt;ironArray SLU&lt;/a&gt; — the company behind Blosc2, Caterva2, Numexpr and other high-performance data processing libraries.&lt;/p&gt;
&lt;p&gt;Compress Better, Compute Bigger!&lt;/p&gt;
&lt;/section&gt;</description><category>Blosc2</category><category>memory wall</category><category>numexpr</category><category>numpy</category><category>performance</category><category>roofline</category><guid>https://blosc.org/posts/roofline-analysis-blosc2/</guid><pubDate>Thu, 27 Nov 2025 08:05:21 GMT</pubDate></item><item><title>New grok plugin for Blosc2</title><link>https://blosc.org/posts/blosc2-grok-release/</link><dc:creator>Marta Iborra, Francesc Alted</dc:creator><description>&lt;p&gt;&lt;strong&gt;Update 2024-01-04:&lt;/strong&gt; Use the new global plugin ID for the grok codec in the examples.&lt;/p&gt;
&lt;p&gt;The Blosc Development Team is happy to announce that the first public release (0.2.0) of &lt;a class="reference external" href="https://github.com/Blosc/blosc2_grok"&gt;blosc2-grok&lt;/a&gt; is available for testing. This dynamic plugin is meant for using the JPEG2000 codec from the &lt;a class="reference external" href="https://github.com/GrokImageCompression/grok"&gt;grok library&lt;/a&gt; as another codec inside Blosc2 (both from C and Python).&lt;/p&gt;
&lt;p&gt;In this blog we will see how to use it as well as the functionality of some parameters. To do so, we will depict &lt;a class="reference external" href="https://github.com/Blosc/blosc2_grok/blob/main/examples/params.py"&gt;an already created example&lt;/a&gt;. Let's get started!&lt;/p&gt;
&lt;section id="why-jpeg2000"&gt;
&lt;h2&gt;Why JPEG2000?&lt;/h2&gt;
&lt;p&gt;It can be stated that currently the best compromise between image quality and compression factor is JPEG2000. This image compression standard has been in use for over 20 years and is widely accepted in the imaging community due to its ability to achieve a compression factor of approximately 10x without significant loss of information.&lt;/p&gt;
&lt;p&gt;JPEG2000 has been implemented in many libraries, but the one that stands out is the grok library because of its speed and completeness. This is why we have chosen it to be the first image codec to be added to Blosc2.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="installing-the-plugin"&gt;
&lt;h2&gt;Installing the plugin&lt;/h2&gt;
&lt;p&gt;First of all, you will need to install the blosc2-grok plugin. You can do it with:&lt;/p&gt;
&lt;pre class="literal-block"&gt;pip install blosc2-grok&lt;/pre&gt;
&lt;p&gt;That's it! You are ready to use it.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="using-the-grok-codec-via-the-plugin"&gt;
&lt;h2&gt;Using the grok codec via the plugin&lt;/h2&gt;
&lt;p&gt;The grok codec has been already registered as a global plugin for Blosc2, so you only need to use its plugin id (&lt;cite&gt;blosc2.Codec.GROK&lt;/cite&gt;) in the codec field of the cparams:&lt;/p&gt;
&lt;pre class="literal-block"&gt;# Define the compression parameters. Disable the filters and the
# splitmode, because these don't work with the codec.
cparams = {
    'codec': blosc2.Codec.GROK,
    'filters': [],
    'splitmode': blosc2.SplitMode.NEVER_SPLIT,
}&lt;/pre&gt;
&lt;p&gt;It is important to disable any filter or splitmode, since we don't want the data to be modified before proceeding to the compression using grok.&lt;/p&gt;
&lt;p&gt;Now, imagine you have an image as a NumPy array (let's say, created using &lt;a class="reference external" href="https://pillow.readthedocs.io/en/stable/"&gt;pillow&lt;/a&gt;), and you want to compress via &lt;cite&gt;blosc2_grok&lt;/cite&gt;. Before, you will need to tell blosc2-grok which format to use among the available ones in the grok library (we will get through the different parameters later):&lt;/p&gt;
&lt;pre class="literal-block"&gt;# Set the parameters that will be used by the codec
kwargs = {'cod_format': blosc2_grok.GrkFileFmt.GRK_FMT_JP2}
blosc2_grok.set_params_defaults(**kwargs)&lt;/pre&gt;
&lt;p&gt;And finally, you are able to compress the image with:&lt;/p&gt;
&lt;pre class="literal-block"&gt;bl_array = blosc2.asarray(
    np_array,
    chunks=np_array.shape,
    blocks=np_array.shape,
    cparams=cparams,
    urlpath="myfile.b2nd",
    mode="w",
)&lt;/pre&gt;
&lt;p&gt;We already have compressed our first image with blosc2-grok!&lt;/p&gt;
&lt;p&gt;In this case, the &lt;cite&gt;chunks&lt;/cite&gt; and &lt;cite&gt;blocks&lt;/cite&gt; params of Blosc2 have been set to the shape of the image (including the number of components) so that grok receives the image as a whole and therefore, can find more opportunities to compress better.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="setting-grok-parameters"&gt;
&lt;h2&gt;Setting grok parameters&lt;/h2&gt;
&lt;p&gt;We have already used the &lt;cite&gt;cod_format&lt;/cite&gt; grok parameter to set the format to use with the &lt;cite&gt;blosc2_grok.set_params_defaults()&lt;/cite&gt;, but blosc2-grok lets you set many other parameters. All of them are mentioned in the &lt;a class="reference external" href="https://github.com/Blosc/blosc2_grok#parameters-for-compression"&gt;README&lt;/a&gt;. When possible, and to make it easier for existing users, these parameters are named the same than in &lt;a class="reference external" href="https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#jpeg-2000-saving"&gt;the Pillow library&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, let's see how to set the &lt;cite&gt;quality_mode&lt;/cite&gt; and &lt;cite&gt;quality_layers&lt;/cite&gt; params, which are meant for lossy compression. So, realize you don't care too much about the quality but want a compression ratio of 10x. Then, you would specify the &lt;cite&gt;quality_mode&lt;/cite&gt; to be &lt;cite&gt;rates&lt;/cite&gt; and the &lt;cite&gt;quality_layers&lt;/cite&gt; to 10:&lt;/p&gt;
&lt;pre class="literal-block"&gt;kwargs = {'cod_format': blosc2_grok.GrkFileFmt.GRK_FMT_JP2}
kwargs['quality_mode'] = 'rates'
kwargs['quality_layers'] = np.array([10], dtype=np.float64)
blosc2_grok.set_params_defaults(**kwargs)&lt;/pre&gt;
&lt;p&gt;With that, you will be able to store the same image than before, but with a compression ratio of 10x.  Please note that the &lt;cite&gt;quality_layers&lt;/cite&gt; parameter is a numpy array. By the way, specifying more than one element here will produce different layers of quality of the original image, but this has little use in Blosc2, since it is better to store different layers in different &lt;cite&gt;NDArray&lt;/cite&gt; objects (or files).&lt;/p&gt;
&lt;p&gt;Now, just like in Pillow, &lt;cite&gt;quality_mode&lt;/cite&gt; can also be expressed in &lt;cite&gt;dB&lt;/cite&gt;, which indicates that you want to specify the quality as the peak signal-to-noise ratio (PSNR) in decibels. For example, let's set a PSNR of 45 dB (which will give us a compression of 9x):&lt;/p&gt;
&lt;pre class="literal-block"&gt;kwargs['quality_mode'] = 'dB'
kwargs['quality_layers'] = np.array([45], dtype=np.float64)&lt;/pre&gt;
&lt;p&gt;Another useful parameter if you want to speed things up is the &lt;cite&gt;num_threads&lt;/cite&gt; parameter. Although grok already sets a good default for you, you can set it to some other value (e.g. when experimenting the best one for your box). Or, if you would like to deactivate multithreading, you can set it to 1:&lt;/p&gt;
&lt;pre class="literal-block"&gt;kwargs['num_threads'] = 1&lt;/pre&gt;
&lt;p&gt;For example, in a MacBook Air laptop with Apple M2 CPU (8-core), the speed difference when performing lossless compression between the single thread setting and the default thread value is around 6x, so expect quite large accelerations by leveraging multithreading.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="visual-example"&gt;
&lt;h2&gt;Visual example&lt;/h2&gt;
&lt;p&gt;Below we did a total of 3 different compressions.  First an image using lossless compression showing the original image:&lt;/p&gt;
&lt;img alt="Lossless compression" src="https://blosc.org/images/blosc2-grok-release/kodim23.png" style="width: 50%;"&gt;
&lt;p&gt;Then, a couple of images using lossy compression: one with 10x for &lt;cite&gt;rates&lt;/cite&gt; quality mode (left) and another with 45dB for &lt;cite&gt;dB&lt;/cite&gt; quality mode (right):&lt;/p&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;p&gt;&lt;img alt="Compression with quality mode rates" src="https://blosc.org/images/blosc2-grok-release/kodim23rates.png" style="width: 100%;"&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;img alt="Compression with quality mode dB" src="https://blosc.org/images/blosc2-grok-release/kodim23dB.png" style="width: 100%;"&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;As can be seen, the lossy images have lost some quality which is to be expected when using this level of compression (around 10x), but the great quality of the JPEG2000 codec allows us human beings to still perceive the image quite well.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="a-glimpse-on-performance"&gt;
&lt;h2&gt;A glimpse on performance&lt;/h2&gt;
&lt;p&gt;The combination of the great implementation of the JPEG2000 codec in grok and the multithreading capabilities of Blosc2 allow to compress, but specially decompress, the image very fast (&lt;a class="reference external" href="https://github.com/Blosc/blosc2_grok/blob/main/bench/encode-chunking-i13900K.ipynb"&gt;benchmark&lt;/a&gt; run on an Intel i9-13900K CPU):&lt;/p&gt;
&lt;img alt="Compression speed using multithreading" src="https://blosc.org/images/blosc2-grok-release/comp-speed-mt.png" style="width: 45%;"&gt;
&lt;img alt="Decompression speed using multithreading" src="https://blosc.org/images/blosc2-grok-release/decomp-speed-mt.png" style="width: 45%;"&gt;
&lt;p&gt;One can see that the compression speed is quite good (around 140 MB/s), but that the decompression speed is much faster (up to 800 MB/s).  See how, in comparison, the compression speed of the JPEG2000 in Pillow (via the  &lt;a class="reference external" href="https://github.com/uclouvain/openjpeg"&gt;OpenJPEG codec&lt;/a&gt;) is much slower (around 4.5 MB/s max.) and so is the decompression speed (around 16 MB/s max.).&lt;/p&gt;
&lt;p&gt;Besides, both grok and OpenJPEG can achieve very similar quality when using similar compression ratios. For example, when using the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Structural_similarity"&gt;structural similarity index measure&lt;/a&gt; (SSIM) to compare the original image with the decompressed one, we get the following results:&lt;/p&gt;
&lt;img alt="Compression speed using multithreading" src="https://blosc.org/images/blosc2-grok-release/blosc2-grok-quality.png" style="width: 50%;"&gt;
&lt;p&gt;Actually, the flexibility of the double partitioning in Blosc2 allows for quite a few ways to divide the workload during compression/decompression, affecting both speed and quality, but we will leave this discussion for another blog.  If you are interested in this topic, you can have a look at the &lt;a class="reference external" href="https://github.com/Blosc/blosc2_grok/tree/main/bench"&gt;blosc2-grok benchmarks&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The addition of the grok plugin to Blosc2 opens many possibilities for compressing images. In the example we used a RGB image, but grayscale images, up to 16-bit of precision, can also be compressed without any problem.&lt;/p&gt;
&lt;p&gt;Although fully usable, this plugin is still in its early stages, so we encourage you to try it out and give us feedback; we will be happy to hear from you!&lt;/p&gt;
&lt;p&gt;Thanks to the &lt;a class="reference external" href="https://www.leaps-innov.eu"&gt;LEAPS consortium&lt;/a&gt; for sponsoring this work, and &lt;a class="reference external" href="https://numfocus.org"&gt;NumFOCUS&lt;/a&gt; for their continuous support through the past years. Thanks also to Aaron Boxer, for the excellent &lt;a class="reference external" href="https://github.com/GrokImageCompression/grok"&gt;grok&lt;/a&gt; library, and for his help in making this plugin possible.  If you like what we are doing in the Blosc world, please consider &lt;a class="reference external" href="https://numfocus.org/donate-to-blosc"&gt;donating&lt;/a&gt; to the project.&lt;/p&gt;
&lt;/section&gt;</description><category>Blosc2</category><category>grok</category><category>JPEG2000</category><category>plugin</category><guid>https://blosc.org/posts/blosc2-grok-release/</guid><pubDate>Fri, 22 Dec 2023 12:32:20 GMT</pubDate></item><item><title>Bytedelta: Enhance Your Compression Toolset</title><link>https://blosc.org/posts/bytedelta-enhance-compression-toolset/</link><dc:creator>Francesc Alted</dc:creator><description>&lt;p&gt;&lt;cite&gt;Bytedelta&lt;/cite&gt; is a new filter that calculates the difference between bytes
in a data stream.  Combined with the shuffle filter, it can improve compression
for some datasets.  Bytedelta is based on &lt;a class="reference external" href="https://aras-p.info/blog/2023/03/01/Float-Compression-7-More-Filtering-Optimization/"&gt;initial work by Aras Pranckevičius&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: We have a brief introduction to bytedelta in the 3rd section of
&lt;a class="reference external" href="https://www.blosc.org/docs/Blosc2-WP7-LEAPS-Innov-2023.pdf"&gt;this presentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The basic concept is simple: after applying the shuffle filter,&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/shuffle-filter.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/shuffle-filter.png" style="width: 75%;"&gt;
&lt;p&gt;then compute the difference for each byte in the byte streams (also called splits in Blosc terminology):&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/bytedelta-filter.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/bytedelta-filter.png" style="width: 75%;"&gt;
&lt;p&gt;The key insight enabling the bytedelta algorithm lies in its implementation, especially the use of SIMD on Intel/AMD and ARM NEON CPUs, making the filter overhead minimal.&lt;/p&gt;
&lt;p&gt;Although Aras's original code implemented shuffle and bytedelta together, it was limited to a specific item size (4 bytes). Making it more general would require significant effort.  Instead, for Blosc2 we built on the existing shuffle filter and created a new one that just does bytedelta. When we insert both in the &lt;a class="reference external" href="https://www.blosc.org/docs/Blosc2-Intro-PyData-Global-2021.pdf"&gt;Blosc2 filter pipeline&lt;/a&gt; (it supports up to 6 chained filters), it leads to a completely general filter that works for any type size supported by existing shuffle filter.&lt;/p&gt;
&lt;p&gt;With that said, the &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2/pull/456"&gt;implementation of the bytedelta filter&lt;/a&gt; has been a breeze thanks to the &lt;a class="reference external" href="https://www.blosc.org/posts/registering-plugins/"&gt;plugin support in C-Blosc2&lt;/a&gt;. You can also implement your own filters and codecs on your own, or if you are too busy, &lt;a class="reference external" href="mailto:contact@blosc.org"&gt;we will be happy to assist you&lt;/a&gt;.&lt;/p&gt;
&lt;section id="compressing-era5-datasets"&gt;
&lt;h2&gt;Compressing ERA5 datasets&lt;/h2&gt;
&lt;p&gt;The best approach to evaluate a new filter is to apply it to real data. For this, we will use some of the &lt;a class="reference external" href="https://www.ecmwf.int/en/forecasts/datasets/reanalysis-datasets/era5"&gt;ERA5 datasets&lt;/a&gt;, representing different measurements and labeled as "wind", "snow", "flux", "pressure" and "precip". They all contain floating point data (float32) and we will use a full month of each one, accounting for 2.8 GB for each dataset.&lt;/p&gt;
&lt;p&gt;The diverse datasets exhibit rather dissimilar complexity, which proves advantageous for testing diverse compression scenarios. For instance, the wind dataset appears as follows:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/wind-colormap.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/wind-colormap.png" style="width: 100%;"&gt;
&lt;p&gt;The image shows the intricate network of winds across the globe on October 1, 1987. The South American continent is visible on the right side of the map.&lt;/p&gt;
&lt;p&gt;Another example is the snow dataset:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/snow-colormap.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/snow-colormap.png" style="width: 100%;"&gt;
&lt;p&gt;This time the image is quite flat. Here one can spot Antarctica, Greenland, North America and of course, Siberia, which was pretty full of snow by 1987-10-01 23:00:00 already.&lt;/p&gt;
&lt;p&gt;Let's see how the new bytedelta filter performs when compressing these datasets.  All the plots below have been made using a box with an Intel i13900k processor, 32 GB of RAM and using Clear Linux.&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cratio-vs-filter.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cratio-vs-filter.png" style="width: 100%;"&gt;
&lt;p&gt;In the box plot above, we summarized the compression ratios for all datasets using different codecs (BLOSCLZ, LZ4, LZ4HC and ZSTD). The main takeaway is that using bytedelta yields the best median compression ratio: bytedelta achieves a median of 5.86x, compared to 5.62x for bitshuffle, 5.1x for shuffle, and 3.86x for codecs without filters.  Overall, bytedelta seems to improve compression ratios here, which is good news.&lt;/p&gt;
&lt;p&gt;While the compression ratio is a useful metric for evaluating the new bytedelta filter, there is more to consider. For instance, does the filter work better on some data sets than others? How does it impact the performance of different codecs? If you're interested in learning more, read on.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="effects-on-various-datasets"&gt;
&lt;h2&gt;Effects on various datasets&lt;/h2&gt;
&lt;p&gt;Let's see how different filters behave on various datasets:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cratio-vs-dset.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cratio-vs-dset.png" style="width: 100%;"&gt;
&lt;p&gt;Here we see that, for datasets that compress easily (precip, snow), the behavior is quite different from those that are less compressible. For precip, bytedelta actually worsens results, whereas for snow, it slightly improves them. For less compressible datasets, the trend is more apparent, as can be seen in this zoomed in image:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cratio-vs-dset-zoom.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cratio-vs-dset-zoom.png" style="width: 100%;"&gt;
&lt;p&gt;In these cases, bytedelta clearly provides a better compression ratio, most specifically with the pressure dataset, where compression ratio by using bytedelta has increased by 25% compared to the second best, bitshuffle (5.0x vs 4.0x, using ZSTD clevel 9). Overall, only one dataset (precip) shows an actual decrease. This is good news for bytedelta indeed.&lt;/p&gt;
&lt;p&gt;Furthermore, Blosc2 supports another compression parameter for splitting the compressed streams into bytes with the same significance. Normally, this leads to better speed but less compression ratio, so this is automatically activated for faster codecs, whereas it is disabled for slower ones. However, it turns out that, when we activate splitting for all the codecs, we find a welcome surprise: bytedelta enables ZSTD to find significantly better compression paths, resulting in higher compression ratios.&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cratio-vs-dset-always-split-zoom.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cratio-vs-dset-always-split-zoom.png" style="width: 100%;"&gt;
&lt;p&gt;As can be seen, in general ZSTD + bytedelta can compress these datasets better. For the pressure dataset in particular, it goes up to 5.7x, 37% more than the second best, bitshuffle (5.7x vs 4.1x, using ZSTD clevel 9).  Note also that this new highest is 14% more than without splitting (the default).&lt;/p&gt;
&lt;p&gt;This shows that when compressing, you cannot just trust your intuition for setting compression parameters - there is no substitute for experimentation.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="effects-on-different-codecs"&gt;
&lt;h2&gt;Effects on different codecs&lt;/h2&gt;
&lt;p&gt;Now, let's see how bytedelta affects performance for different codecs and compression levels.&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cratio-vs-codec.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cratio-vs-codec.png" style="width: 100%;"&gt;
&lt;p&gt;Interestingly, on average bytedelta proves most useful for ZSTD and higher compression levels of ZLIB (Blosc2 comes with &lt;a class="reference external" href="https://github.com/zlib-ng/zlib-ng"&gt;ZLIB-NG&lt;/a&gt;). On the other hand, the fastest codecs (LZ4, BLOSCLZ) seem to benefit more from bitshuffle instead.&lt;/p&gt;
&lt;p&gt;Regarding compression speed, in general we can see that bytedelta has little effect on performance:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cspeed-vs-codec.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cspeed-vs-codec.png" style="width: 100%;"&gt;
&lt;p&gt;As we can see, compression algorithms like BLOSCLZ, LZ4 and ZSTD can achieve extremely high speeds. LZ4 reaches and surpasses speeds of 30 GB/s, even when using bytedelta. BLOSCLZ and ZSTD can also exceed 20 GB/s, which is quite impressive.&lt;/p&gt;
&lt;p&gt;Let’s see the compression speed grouped by compression levels:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cspeed-vs-codec-clevel.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cspeed-vs-codec-clevel.png" style="width: 100%;"&gt;
&lt;p&gt;Here one can see that, to achieve the highest compression rates when combined with shuffle and bytedelta, the codecs require significant CPU resources; this is especially noticeable in the zoomed-in view:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cspeed-vs-codec-clevel-zoom.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cspeed-vs-codec-clevel-zoom.png" style="width: 100%;"&gt;
&lt;p&gt;where capable compressors like ZSTD do require up to 2x more time to compress when using bytedelta, especially for high compression levels (6 and 9).&lt;/p&gt;
&lt;p&gt;Now, let us examine decompression speeds:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/dspeed-vs-codec.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/dspeed-vs-codec.png" style="width: 100%;"&gt;
&lt;p&gt;In general, decompression is faster than compression. BLOSCLZ, LZ4 and LZ4HC can achieve over 100 GB/s. BLOSCLZ reaches nearly 180 GB/s using no filters on the snow dataset (lowest complexity).&lt;/p&gt;
&lt;p&gt;Let’s see the decompression speed grouped by compression levels:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/dspeed-vs-codec-clevel.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/dspeed-vs-codec-clevel.png" style="width: 100%;"&gt;
&lt;p&gt;The bytedelta filter noticeably reduces speed for most codecs, up to 20% or more.  ZSTD performance is less impacted.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="achieving-a-balance-between-compression-ratio-and-speed"&gt;
&lt;h2&gt;Achieving a balance between compression ratio and speed&lt;/h2&gt;
&lt;p&gt;Often, you want to achieve a good balance of compression and speed, rather than extreme values of either. We will conclude by showing plots depicting a combination of both metrics and how bytedelta influences them.&lt;/p&gt;
&lt;p&gt;Let's first represent the compression ratio versus compression speed:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cratio-vs-cspeed.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cratio-vs-cspeed.png" style="width: 100%;"&gt;
&lt;p&gt;As we can see, the shuffle filter is typically found on the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Pareto_front"&gt;Pareto frontier&lt;/a&gt; (in this case, the point furthest to the right and top). Bytedelta comes next.  In contrast, not using a filter at all is on the opposite side.  This is typically the case for most real-world numerical datasets.&lt;/p&gt;
&lt;p&gt;Let's now group filters and datasets and calculate the mean values of combining
(in this case, multiplying) the compression ratio and compression speed for all codecs.&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cspeed-vs-filter.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cspeed-vs-filter.png" style="width: 100%;"&gt;
&lt;p&gt;As can be seen, bytedelta works best with the wind dataset (which is quite complex), while bitshuffle does a good job in general for the others. The shuffle filter wins on the snow dataset (low complexity).&lt;/p&gt;
&lt;p&gt;If we group by compression level:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cratio_x_cspeed-vs-codec-clevel.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cratio_x_cspeed-vs-codec-clevel.png" style="width: 100%;"&gt;
&lt;p&gt;We see that bytedelta works well with LZ4 here, and also with ZSTD at the lowest compression level (1).&lt;/p&gt;
&lt;p&gt;Let's revise the compression ratio versus decompression speed comparison:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cratio-vs-dspeed.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cratio-vs-dspeed.png" style="width: 100%;"&gt;
&lt;p&gt;Let's group together the datasets and calculate the mean for all codecs:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cratio_x_dspeed-vs-filter-dset.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cratio_x_dspeed-vs-filter-dset.png" style="width: 100%;"&gt;
&lt;p&gt;In this case, shuffle generally prevails, with bitshuffle also doing reasonably well, winning on precip and pressure datasets.&lt;/p&gt;
&lt;p&gt;Also, let’s group the data by compression level:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cratio_x_dspeed-vs-codec-clevel.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cratio_x_dspeed-vs-codec-clevel.png" style="width: 100%;"&gt;
&lt;p&gt;We find that bytedelta compression does not outperform shuffle compression in any scenario. This is unsurprising since decompression is typically fast, and bytedelta's extra processing can decrease performance more easily. We also see that LZ4HC (clevel 6 and 9) + shuffle strikes the best balance in this scenario.&lt;/p&gt;
&lt;p&gt;Finally, let's consider the balance between compression ratio, compression speed, and decompression speed:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cratio_x_cspeed_dspeed-vs-dset.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cratio_x_cspeed_dspeed-vs-dset.png" style="width: 100%;"&gt;
&lt;p&gt;Here the winners are shuffle and bitshuffle, depending on the data set, but bytedelta never wins.&lt;/p&gt;
&lt;p&gt;If we group by compression levels:&lt;/p&gt;
&lt;img alt="/images/bytedelta-enhance-compression-toolset/cratio_x_cspeed_dspeed-vs-codec-clevel.png" class="align-center" src="https://blosc.org/images/bytedelta-enhance-compression-toolset/cratio_x_cspeed_dspeed-vs-codec-clevel.png" style="width: 100%;"&gt;
&lt;p&gt;Overall, we see LZ4 as the clear winner at any level, especially when combined with shuffle. On the other hand, bytedelta did not win in any scenario here.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="benchmarks-for-other-computers"&gt;
&lt;h2&gt;Benchmarks for other computers&lt;/h2&gt;
&lt;p&gt;We have run the benchmarks presented here in an assortment of different boxes:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.blosc.org/docs/era5-pds/plot_transcode_data-m1.html"&gt;MacBook Air with M1 processor and 8 GB RAM. MacOSX 13.1.&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.blosc.org/docs/era5-pds/plot_transcode_data-m1.html"&gt;AMD Ryzen 9 5950X processor and 32 GB RAM. Debian 22.04.&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.blosc.org/docs/era5-pds/plot_transcode_data-i10k.html"&gt;Intel i9-10940X processor and 64 GB RAM. Debian 22.04.&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.blosc.org/docs/era5-pds/plot_transcode_data-i13k.html"&gt;Intel i9-13900K processor and 32 GB RAM. Clear Linux.&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, find here a couple of runs using the i9-13900K box above, but with the always split and never split settings:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.blosc.org/docs/era5-pds/plot_transcode_data-i13k-always-split.html"&gt;Intel i9-13900K. Always Split.&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.blosc.org/docs/era5-pds/plot_transcode_data-i13k-never-split.html"&gt;Intel i9-13900K. Never Split.&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Reproducing the benchmarks is straightforward. First, &lt;a class="reference external" href="https://github.com/Blosc/python-blosc2/blob/main/bench/ndarray/download_data.py"&gt;download the data&lt;/a&gt;; the downloaded files will be in the new &lt;cite&gt;era5_pds/&lt;/cite&gt; directory.  Then perform &lt;a class="reference external" href="https://github.com/Blosc/python-blosc2/blob/main/bench/ndarray/transcode_data.py"&gt;the series of benchmarks&lt;/a&gt;; this is takes time, so grab coffee and wait 30 min (fast workstations) to 6 hours (slow laptops).  Finally, run the &lt;a class="reference external" href="https://github.com/Blosc/python-blosc2/blob/main/bench/ndarray/plot_transcode_data.ipynb"&gt;plotting Jupyter notebook&lt;/a&gt; to explore your results.  If you wish to share your results with the &lt;a class="reference external" href="mailto:contact@blosc.org"&gt;Blosc development team&lt;/a&gt;, we will appreciate hearing from you!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Bytedelta can achieve higher compression ratios in most datasets, specially in combination with capable codecs like ZSTD, with a maximum gain of 37% (pressure) over other codecs; only in one case (precip) compression ratio decreases. By compressing data more efficiently, bytedelta can reduce file sizes even more, accelerating transfer and storage.&lt;/p&gt;
&lt;p&gt;On the other hand, while bytedelta excels at achieving high compression ratios, this requires more computing power. We have found that for striking a good balance between high compression and fast compression/decompression, other filters, particularly shuffle, are superior overall.&lt;/p&gt;
&lt;p&gt;We've learned that no single codec/filter combination is best for all datasets:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;ZSTD (clevel 9) + bytedelta can get better absolute compression ratio for most of the datasets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;LZ4 + shuffle is well-balanced for all metrics (compression ratio, speed, decompression speed).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;LZ4 (clevel 6) and ZSTD (clevel 1) + shuffle strike a good balance of compression ratio and speed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;LZ4HC (clevel 6 and 9) + shuffle balances well compression ratio and decompression speed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;BLOSCLZ without filters achieves best decompression speed (at least in one instance).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In summary, the optimal choice depends on your priorities.&lt;/p&gt;
&lt;p&gt;As a final note, the Blosc development team is working on BTune, a new deep learning tuner for Blosc2. BTune can be trained to automatically recognize different kinds of datasets and choose the optimal codec and filters to achieve the best balance, based on the user's needs. This would create a much more intelligent compressor that can adapt itself to your data faster, without requiring time-consuming manual tuning. If interested, &lt;a class="reference external" href="mailto:contact@blosc.org"&gt;contact us&lt;/a&gt;; we are looking for beta testers!&lt;/p&gt;
&lt;/section&gt;</description><category>Blosc2</category><category>bytedelta</category><category>filter</category><guid>https://blosc.org/posts/bytedelta-enhance-compression-toolset/</guid><pubDate>Fri, 24 Mar 2023 11:32:20 GMT</pubDate></item><item><title>Blosc Has Won Google's Open Source Peer Bonus Program</title><link>https://blosc.org/posts/prize-push-Blosc2/</link><dc:creator>Francesc Alted</dc:creator><description>&lt;p&gt;Past month &lt;a class="reference external" href="https://opensource.googleblog.com/2017/10/more-open-source-peer-bonus-winners.html"&gt;Google announced the winners for the 2017’s second round of their Open Source Peer Bonus program&lt;/a&gt; and I was among them for my commitment to the Blosc project.  It took a bit, but I wanted to express my thoughts on this nice event.  Needless to say, I am proud and honored for this recognition, most specially when this is the first completely uninterested donation that someone made to me after 15 years of doing open source (in many occasions doing that as a full-time work), so thank you very much Google!  The assumption is that people does open source because 1) they believe in the concept and 2) they can earn a public consideration that allows them to get contracts (so allowing many of us to have a life!).  However, this time the unexpected happened, and that an important corporation like Google decided to publicly recognize this work makes me very happy (would that pave the way for others to follow? :-).&lt;/p&gt;
&lt;p&gt;Having said this, and as it happens with any open source project that has seen some success, the contributions for other people have been instrumental in making Blosc such a featured and stable library.  People like Jack Pappas, Valentin Hänel, Christohper Speller, Antonio Valentino and up to 30 contributors made important contributions to the project.  This award goes indeed to them too.&lt;/p&gt;
&lt;p&gt;This push comes very timely because it is giving me more stamina towards the release of &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2"&gt;Blosc2&lt;/a&gt;.  Blosc2 is the next generation of Blosc, and will add features like:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Full 64-bit support for chunks (i.e. not anymore limited to 2 GB).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;New filters, like delta and truncation of floating point precision.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A new filter pipeline that will allow to run more than one filter before the compression step.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Support for variable length objects (i.e. not limited to fixed-length datasets).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Support for dictionaries between different blocks in the same chunk.  That will be important for allowing smaller chunks (and hence improving decompression latency) while keeping compression ratio and performance mostly untouched.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Support for more codecs (&lt;a class="reference external" href="http://blosc.org/posts/new-lizard-codec/"&gt;lizard&lt;/a&gt; support is already in).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;New serialisation format which is meant to allow self-discovery via magic numbers and introspection.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;New super-chunk object that will allow to work seamlessly with arbitrarily large sets of chunks, both in-memory and on-disk.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Support for &lt;a class="reference external" href="http://blosc.org/posts/arm-is-becoming-a-first-class-citizen-for-blosc/"&gt;SIMD in ARM processors&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All in all, and after more than 2 years working in different aspects of these features, I am quite satisfied on the progress so far. My expectation was to do a beta release during this fall, and although the work is quite advanced, there are still some loose ends that require quite a bit of work.  If you like where I am headed and are interested in seeing this work to complete faster, a contribution to the project in the form of a pull request or, better yet, a donation suggesting which feature you would like the most will be greatly appreciated.&lt;/p&gt;
&lt;p&gt;Finally, I'd like to take the opportunity to annonunce that Blosc has a logo (finally!). You can admire it at the header of this page.  This is the work of &lt;a class="reference external" href="http://domenec123.blogspot.com.es"&gt;Domènec Morera&lt;/a&gt; who also made for us the logo of &lt;a class="reference external" href="http://www.pytables.org"&gt;PyTables&lt;/a&gt;.  I really think he is a great artist and that he did an excellent job again; I hope the new logo will be beneficial for the Blosc project as a whole!&lt;/p&gt;</description><category>Blosc2</category><category>Google</category><category>Prize</category><guid>https://blosc.org/posts/prize-push-Blosc2/</guid><pubDate>Fri, 17 Nov 2017 17:32:20 GMT</pubDate></item><item><title>ARM is becoming a first-class citizen for Blosc</title><link>https://blosc.org/posts/arm-is-becoming-a-first-class-citizen-for-blosc/</link><dc:creator>Francesc Alted</dc:creator><description>&lt;p&gt;We are happy to announce that Blosc is receiving official support for
ARM processors.  Blosc has always been meant to support all platforms
where a C89 compliant C compiler can be found, but until now the only
hardware platforms that we were testing on a regular basis has been
Intel (on top of Unix/Linux, Mac OSX and Windows).&lt;/p&gt;
&lt;p&gt;We want this to change and the ARM architecture has been our first
candidate to become a fully supported platform besides Intel/AMD.  You
may be wondering that we could have chosen any other architecture like
MIPS or PowerPC, so why ARM?&lt;/p&gt;
&lt;section id="arm-is-eating-the-world"&gt;
&lt;h2&gt;ARM is eating the world&lt;/h2&gt;
&lt;p&gt;ARM is an increasingly popular architecture and we can find
implementation exemplars of it not only in the phones, tablets or
ChromeBooks, but also acting as embedded processors, as well as in
providing computing power to immensely popular Raspberry Pi's and
Arduinos and even environments so &lt;em&gt;apparently&lt;/em&gt; alien to it like &lt;a class="reference external" href="http://www.theplatform.net/2015/06/16/mont-blanc-sets-the-stage-for-arm-hpc/"&gt;High
Performance Computing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Contrarily to what has been traditional for other computer platforms,
one of the most important design features for ARM is to keep energy
consumption under very strict limits.  Nowadays, the ARM architecture
can run decently powerful CPUs where each core &lt;a class="reference external" href="http://www.androidauthority.com/arms-secret-recipe-for-power-efficient-processing-409850"&gt;consumes just 600 to
750 mWatt or less&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In my opinion, it is precisely this energy efficiency what makes of
ARM one of the platforms with more projection to gain ground as a
general computer platform in the short future.  By now, we all know
that ARM allows packing more cores into a single die (e.g. your phone
having more cores than your laptop, anyone?).  And more cores also
means more combined computing throughput (albeit a bit more difficult
to program), but more importantly, &lt;strong&gt;more cores being able to bring
data from memory at the same time&lt;/strong&gt;.  Contrarily to what one might
think, having different threads transmitting data from RAM to the CPU
caches provides a better utilization of memory buses, and hence, a
much better global memory bandwidth.  This can be seen, for example,
in &lt;a class="reference external" href="http://blosc.org/benchmarks-blosclz.html"&gt;typical Blosc benchmarks&lt;/a&gt; by looking at how the
bandwidth grows with the number of threads in all the dots, but
specially where compression ratio equals 1 (i.e. no compression is
active, so Blosc is only doing a &lt;em&gt;memory copy&lt;/em&gt; in this case).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="blosc-is-getting-ready-for-arm"&gt;
&lt;h2&gt;Blosc is getting ready for ARM&lt;/h2&gt;
&lt;p&gt;So ARM is cool indeed, but what we are doing for making it a
first-class citizen?  For starters, we have created a new &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2"&gt;C-Blosc2&lt;/a&gt; repository that is going to act
as a playground for some time and where we are going to experiment
with a new range of features (those will be discussed in a later
post).  And this is exactly the place where we have already started
implementing a NEON version of the shuffle filter.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0002a/BABIIFHA.html"&gt;NEON&lt;/a&gt;
is an SIMD extension in the same spirit than SSE2 or AVX2 present in
Intel/AMD offerings.  NEON extension was introduced in ARMv7
architecture, and is present in most of the current high-end devices
(including most of the phones and tablets floating around, including
the new Raspberry Pi 2).  As many of you know, leveraging SIMD in
modern CPUs is key for allowing Blosc to be one of the fastest
compressors around, and if we wanted to be serious about ARM, NEON
support had to be here, &lt;strong&gt;period&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The new NEON implementation of shuffle for Blosc has been entirely
made by &lt;a class="reference external" href="https://github.com/LucianMarc"&gt;Lucian Marc&lt;/a&gt;, a summer
student that joined the project at the beginning of July 2015.  Lucian
did a terrific work on implementing the &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2/blob/master/blosc/shuffle-neon.c"&gt;shuffle filter NEON&lt;/a&gt;,
and during the 2-months stage he did not only that, but he also had
time to do a preliminary version of the bitshuffle filter as well (not
completely functional yet, but as time allows, he plans to finish that).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="some-hints-on-the-measured-increase-in-performance"&gt;
&lt;h2&gt;Some hints on the measured increase in performance&lt;/h2&gt;
&lt;p&gt;So you might be asking, how fast can perform Blosc on an ARM with
NEON?  Well, let's start first by showing how fast it works on a
Raspberry Pi 2 (Broadcom BCM2836 ARMv7 Quad Core Processor) having
NEON and running Raspbian (gcc 4.7.2).  To not bore people, we are
going to show just decompression speeds:&lt;/p&gt;
&lt;img alt="/images/blosclz-shuffle-neon-rpi2.png" src="https://blosc.org/images/blosclz-shuffle-neon-rpi2.png"&gt;
&lt;p&gt;It turns out that, when using the 4 cores and low compression levels,
Blosc with NEON support already shows evidence that it can equal the
performance of memcpy() on ARM.  This is an important fact because I
did not think that ARM performance was enough to allow Blosc doing
that already.  I was wrong.&lt;/p&gt;
&lt;p&gt;Okay, so Blosc using NEON can be fast, but exactly how much when
compared to a &lt;a class="reference external" href="https://github.com/Blosc/c-blosc/blob/master/blosc/shuffle-generic.h"&gt;shuffle implementation in pure C&lt;/a&gt;?
Here you have the figures for the generic C shuffle:&lt;/p&gt;
&lt;img alt="/images/blosclz-shuffle-generic-rpi2.png" src="https://blosc.org/images/blosclz-shuffle-generic-rpi2.png"&gt;
&lt;p&gt;That means that NEON can accelerate the whole decompression process
between 2x and 3x, which is pretty significant, and also speaks highly
about the quality of Lucian's NEON implementation.&lt;/p&gt;
&lt;p&gt;Does that mean that we can extrapolate these figures for all ARM
processors out there?  Not quite.  In fact, the performance of a
Raspberry Pi 2 is quite mild compared with other boards.  So, let's
see what is the performance on a &lt;a class="reference external" href="http://www.hardkernel.com/main/products/prdt_info.php?g_code=G140448267127"&gt;ODROID-XU3&lt;/a&gt;
(although it has been replaced by &lt;a class="reference external" href="http://www.hardkernel.com/main/products/prdt_info.php"&gt;ODROID-XU4&lt;/a&gt;, the XU3 has
the same processor, so we are testing a pretty powerful CPU model
here).  This board comes with a Samsung Exynos5422 Cortex-A15 2.0 GHz
quad core and Cortex™-A7 quad core CPUs, so it is a representative of
the ARM Heterogeneous Multi-Processing solution (aka big.LITTLE).
Here are its figures:&lt;/p&gt;
&lt;img alt="/images/blosclz-shuffle-neon-odroid.png" src="https://blosc.org/images/blosclz-shuffle-neon-odroid.png"&gt;
&lt;p&gt;So, the first thing to note is the memcpy() speed that at 1.6 GB/s,
is considerably faster than the RPi2 (&amp;lt; 0.9 GB/s).  Yeah, this is a
much more capable board from a computational point of view.  The
second thing is that decompression speed &lt;em&gt;almost doubles the memcpy()
speed&lt;/em&gt;.  Again, I was very impressed because I did not expect this
range of speeds &lt;em&gt;at all&lt;/em&gt;.  ARM definitely is getting in a situation
where compression can be used for an advantage, computationally
speaking.&lt;/p&gt;
&lt;p&gt;The third thing to note is a bit disappointing though: why only 3
threads appear in the plot?  Well, it turns out that the benchmark
suite fails miserably when using 4 threads or more.  As the Raspberry
setup does not suffer from this problem at all, I presume that this is
more related with the board or the libraries that come with the
operating system (Ubuntu 14.04).  This is rather unfortunate because I
was really curious to see such an ARMv7 8-core beast running at full
steam using the 8 threads.  At any rate, time will tell if the problem
is in the board or in Blosc itself.&lt;/p&gt;
&lt;p&gt;Just to make the benchmarks a bit more complete, let me finish this
benchmark section showing the performance using the generic C code for
the shuffling algorithm:&lt;/p&gt;
&lt;img alt="/images/blosclz-shuffle-generic-odroid.png" src="https://blosc.org/images/blosclz-shuffle-generic-odroid.png"&gt;
&lt;p&gt;If we compare with NEON figures for the ODROID board, we can see again
an increase in speed of between 2x and 4x, which is crazy amazing
(sorry if I seem a bit over-enthusiastic, but again, I was not really
prepared for seeing this).  Again, only figures for 2 threads are
in this plot because the benchmark crashes for 3 threads (this is
another hint that points to the fault being outside Blosc itself
and not in its NEON implementation of the shuffle filter).&lt;/p&gt;
&lt;p&gt;At decompression speeds of 3 GB/s and ~ 2 Watt of energy consumption,
the ARM platform has one of the best bandwidth/Watt ratios that you can find
in the market, and this can have (and will have) profound implications
on how computations will be made in the short future (as the &lt;a class="reference external" href="http://www.montblanc-project.eu/publications/energy-efficiency-high-performance-computing-mont-blanc-project"&gt;Mont
Blanc initiative is trying to demonstrate&lt;/a&gt;).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="what-to-expect-from-arm-blosc-in-the-forthcoming-months"&gt;
&lt;h2&gt;What to expect from ARM/Blosc in the forthcoming months&lt;/h2&gt;
&lt;p&gt;This work on supporting ARM platforms is just the beginning.  As ARM
processors get more spread, and most specially, &lt;a class="reference external" href="http://www.arm.com/products/processors/cortex-a/cortex-a72-processor.php"&gt;faster&lt;/a&gt;,
we will need to refine the support for ARM in Blosc.&lt;/p&gt;
&lt;p&gt;NEON support is only a part of the game, and things like efficient
handling of ARM heterogeneous architectures (&lt;a class="reference external" href="https://en.wikipedia.org/wiki/ARM_big.LITTLE"&gt;big.LITTLE&lt;/a&gt;) or making specific
tweaks for ARM cache sizes will be critical so as to make of ARM a
truly first-citizen for the Blosc ecosystem.&lt;/p&gt;
&lt;p&gt;If you have ideas on what can be improved, and most specially &lt;strong&gt;how&lt;/strong&gt;,
we want to learn from you :) If you want to contribute code to the
project, your pull requests are very welcome too!  If you like what we
are doing and want to see more of this, you can also &lt;a class="reference external" href="http://blosc.org/blog/seeking-sponsoship.html"&gt;sponsor us&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;</description><category>ARM</category><category>Blosc2</category><category>NEON</category><guid>https://blosc.org/posts/arm-is-becoming-a-first-class-citizen-for-blosc/</guid><pubDate>Wed, 09 Sep 2015 11:32:20 GMT</pubDate></item></channel></rss>