<?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 by Oscar Guiñon, Francesc Alted)</title><link>https://blosc.org/</link><description></description><atom:link href="https://blosc.org/authors/oscar-guinon-francesc-alted.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>Wed, 10 Jun 2026 17:44:34 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Blosc2 Meets PyTables: Making HDF5 I/O Performance Awesome</title><link>https://blosc.org/posts/blosc2-pytables-perf/</link><dc:creator>Oscar Guiñon, Francesc Alted</dc:creator><description>&lt;p&gt;&lt;a class="reference external" href="http://www.pytables.org"&gt;PyTables&lt;/a&gt; lets users to easily handle data tables and array objects in a hierarchical structure. It also supports a variety of different data compression libraries through &lt;a class="reference external" href="https://docs.hdfgroup.org/hdf5/develop/_f_i_l_t_e_r.html"&gt;HDF5 filters&lt;/a&gt;.  With the release of &lt;a class="reference external" href="https://github.com/PyTables/PyTables/releases/tag/v3.8.0"&gt;PyTables 3.8.0&lt;/a&gt;, the Blosc Development Team is pleased to announce the availability of Blosc2, acting not only as another HDF5 filter, but also as an additional partition tool (aka 'second partition') that complements the existing HDF5 chunking schema.&lt;/p&gt;
&lt;p&gt;By providing support for a second partition in HDF5, the chunks (aka the 'first partition') can be made larger, ideally fitting in cache level 3 in modern CPUs (see below for advantages of this).  Meanwhile, Blosc2 will use its internal blocks (aka the second partition) as the minimum amount of data that should be read and decompressed during data retrieval, no matter how small the hyperslice to be read is.&lt;/p&gt;
&lt;p&gt;When Blosc2 is used to implement a second partition for data (referred ahead as 'optimized Blosc2' too), it can bypass the HDF5 pipeline for writing and for reading.  This brings another degree of freedom when choosing the different internal I/O buffers, which can be of extraordinary importance in terms of performance and/or resource saving.&lt;/p&gt;
&lt;section id="how-second-partition-allows-for-big-chunking"&gt;
&lt;h2&gt;How second partition allows for Big Chunking&lt;/h2&gt;
&lt;p&gt;Blosc2 in PyTables is meant for compressing data in big chunks (typically in the range of level 3 caches in modern CPUs, that is, 10 ~ 1000 MB).  This has some interesting advantages:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;It allows to reduce the number of entries in the HDF5 hash table. This means less resource consumption in the HDF5 side, so PyTables can handle larger tables using less resources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It speeds-up compression and decompression because multithreading works better with more blocks. Remember that you can specify the number of threads to use by using the &lt;a class="reference external" href="http://www.pytables.org/usersguide/parameter_files.html?highlight=max_blosc_threads#tables.parameters.MAX_BLOSC_THREADS"&gt;MAX_BLOSC_THREADS&lt;/a&gt; parameter, or by using the &lt;a class="reference external" href="https://www.blosc.org/c-blosc2/reference/blosc1.html?highlight=blosc_nthreads#blosc1-api"&gt;BLOSC_NTHREADS&lt;/a&gt; environment variable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, the traditional drawback of having large chunks is that getting small slices would take long time because the whole chunk has to be read completely and decompressed.  Blosc2 surmounts that difficulty like this: it asks HDF5 where chunks start on-disk (via &lt;a class="reference external" href="https://docs.hdfgroup.org/hdf5/v1_12/group___h5_d.html#title12"&gt;H5Dget_chunk_info()&lt;/a&gt;), and then it access to the internal blocks (aka the &lt;em&gt;second partition&lt;/em&gt;) independently instead of having to decompress the entire chunk.  This effectively avoids penalizing access to small data slices.&lt;/p&gt;
&lt;p&gt;In the graphic below you can see the second partition in action where, in order to retrieve the green slice, only blocks 2 and 3 needs to be addressed and decompressed, instead of the (potentially much) larger chunk 0 and 1, which would be the case for the traditional 1 single partition in HDF5:&lt;/p&gt;
&lt;img alt="/images/blosc2_pytables/block-slice.png" class="align-center" src="https://blosc.org/images/blosc2_pytables/block-slice.png" style="width: 70%;"&gt;
&lt;p&gt;In the benchmarks below we are comparing the performance of existing filters inside PyTables (like Zlib or Blosc(1)) against Blosc2, both working as a filter or in optimized mode, that is, bypassing the HDF5 filter pipeline completely.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="benchmarks"&gt;
&lt;h2&gt;Benchmarks&lt;/h2&gt;
&lt;p&gt;The data used in this section have been fetched from &lt;a class="reference external" href="https://www.ecmwf.int/en/forecasts/datasets/reanalysis-datasets/era5"&gt;ERA5 database&lt;/a&gt; (see &lt;a class="reference external" href="https://github.com/PyTables/PyTables/blob/master/bench/fetch_meteo_data.py"&gt;downloading script&lt;/a&gt;), which provides hourly estimates of a large number of atmospheric, land and oceanic climate variables.  To build the tables used for reading and writing operations, we have used five different ERA5 datasets with the same shape (100 x 720 x 1440) and the same variables (latitude, longitude and time).  Then, we have built a table with a column for each variable and each dataset and added the latitude, longitude and time as columns (for a total of 8 cols). Finally, there have been written 100 x 720 x 1440 rows (more than 100 million) to this table, which makes for a total data size of 3.1 GB.&lt;/p&gt;
&lt;p&gt;We present different scenarios when comparing resource usage for writing and reading between the Blosc and Blosc2 filters, including the Blosc2 optimized versions.  First one is when PyTables is choosing the chunkshape automatically (the default); as Blosc2 is meant towards large chunks, PyTables has been tuned to produce far larger chunks for Blosc2 in this case (Blosc and other filters will remain using the same chunk sizes as usual). Second, we will visit the case where the chunkshape is equal for both Blosc and Blosc2.  Spoiler alert: we will see how Blosc2 behaves well (and sometimes &lt;em&gt;much beter&lt;/em&gt;) in both scenarios.&lt;/p&gt;
&lt;section id="automatic-chunkshape"&gt;
&lt;h3&gt;Automatic chunkshape&lt;/h3&gt;
&lt;section id="inkernel-searches"&gt;
&lt;h4&gt;Inkernel searches&lt;/h4&gt;
&lt;p&gt;We start by performing &lt;a class="reference external" href="https://www.pytables.org/usersguide/optimization.html#in-kernel-searches"&gt;inkernel queries&lt;/a&gt; where the chunkshape for the table is chosen automatically by the PyTables machinery (see &lt;a class="reference external" href="https://github.com/PyTables/PyTables/blob/master/bench/query_meteo_data.py"&gt;query script here&lt;/a&gt;).  This size is the same for Blosc, Zlib and uncompressed cases which are all using 16384 rows (about 512 KB), whereas for Blosc2 the computed chunkshape is quite larger: 1179648 rows (about 36 MB; this actually depends on the size of the L3 cache, which is automatically queried in real-time by PyTables and it turns out to be exactly 36 MB for our CPU, an Intel i9-13900K).&lt;/p&gt;
&lt;p&gt;Now, we are going to analyze the memory and time usage of performing six inkernel searches, which means scanning the full table six times, in different cases:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;With no compression; size is 3,1 GB.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using HDF5 with ZLIB + Shuffle; size is 407 MB.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using Blosc filter with BloscLZ codec + Bitshuffle; size is 468 MB.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using Blosc2 filter with BloscLZ codec + Bitshuffle; size is 421 MB.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using Blosc2 filter with Zstd codec + Bitshuffle; size is 341 MB.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt="/images/blosc2_pytables/inkernel-nocomp-zlib-blosc-zstd.png" class="align-center" src="https://blosc.org/images/blosc2_pytables/inkernel-nocomp-zlib-blosc-zstd.png" style="width: 125%;"&gt;
&lt;p&gt;As we can see, the queries with no compression enable do not take much time or memory consumption, but it requires storing the full 3.1 GB of data. When using ZLIB, which is the HDF5 default, it does not require much memory either, but it takes a much more time (about 10x more), although the stored data is more than 6x smaller. When using Blosc, the time spent in (de-)compression is much less, but the queries still takes more time (1.7x more) than the no compression case; in addition, the compression ratio is quite close to the ZLIB case.&lt;/p&gt;
&lt;p&gt;However, the big jump comes when using Blosc2 with BloscLZ and BitShuffle, since although it uses just a little bit more memory than Blosc (a consequence of using larger chunks), in exchange it is quite faster than the previous methods while achieving a noticeably better compression ratio.  Actually, this combination is 1.3x faster than using no compression; this is one of the main features of Blosc (and even more with Blosc2): it can accelerate operation by using compression.&lt;/p&gt;
&lt;p&gt;Finally, in case we want to improve compression further, Blosc2 can be used with the ZSTD codec, which achieves the best compression ratio here, in exchange for a slightly slower time (but still 1.15x faster than not using compression).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="pytables-inkernel-vs-pandas-queries"&gt;
&lt;h4&gt;PyTables inkernel vs pandas queries&lt;/h4&gt;
&lt;p&gt;Now that we have seen how Blosc2 can help PyTables in getting great query performance, we are going to compare it against pandas queries; to make things more interesting, we will be using the same NumExpr engine in both PyTables (where it is used in inkernel queries) and pandas.&lt;/p&gt;
&lt;p&gt;For this benchmark, we have been exploring the best configuration for speed, so we will be using 16 threads (for both Blosc2 and NumExpr) and the Shuffle filter instead of Bitshuffle; this leads to slightly less compression ratios (see below), but now the goal is getting full speed, not reducing storage (keep in mind that Pandas stores data in-memory without compression).&lt;/p&gt;
&lt;p&gt;Here it is how PyTables and pandas behave when doing the same 6 queries than in the previous section.&lt;/p&gt;
&lt;img alt="/images/blosc2_pytables/inkernel-pandas.png" class="align-center" src="https://blosc.org/images/blosc2_pytables/inkernel-pandas.png" style="width: 125%;"&gt;
&lt;p&gt;And here it is another plot for the same queries, but comparing raw I/O performance for a variety of codecs and filters:&lt;/p&gt;
&lt;img alt="/images/blosc2_pytables/inkernel-vs-pandas2.png" class="align-center" src="https://blosc.org/images/blosc2_pytables/inkernel-vs-pandas2.png" style="width: 80%;"&gt;
&lt;p&gt;As we can see, the queries using Blosc2 are generally faster (up to 2x faster) than not using compression.  Furthermore, Blosc2 + LZ4 get nearly as good times as pandas, while the memory consumption is much smaller with Blosc2 (as much as 20x less in this case; more for larger tables indeed). This is remarkable, as this means that Blosc2 compression results in acceleration that almost compensates for all the additional layers in PyTables (the disk subsystem and the HDF5 library itself).&lt;/p&gt;
&lt;p&gt;And in case you wonder how much compression ratio we have lost by switching from Bitshuffle to Shuffle, not much actually:&lt;/p&gt;
&lt;img alt="/images/blosc2_pytables/shuffle-bitshuffle-ratios.png" class="align-center" src="https://blosc.org/images/blosc2_pytables/shuffle-bitshuffle-ratios.png" style="width: 70%;"&gt;
&lt;p&gt;The take away message here is that, when used correctly, compression can make out-of-core queries go as fast as pure in-memory ones (even when using a high performance tool-set like pandas + NumExpr).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="writing-and-reading-speed-with-automatic-chunkshape"&gt;
&lt;h4&gt;Writing and reading speed with automatic chunkshape&lt;/h4&gt;
&lt;p&gt;Now, let's have a look at the raw write and read performance. In this case we are going to compare Blosc, Blosc2 as an HDF5 filter, and the optimized Blosc2 (acting as a de facto second partition). Remember that in this section the chunkshape determination is still automatic and different for Blosc (16384 rows, about 512 KB) and Blosc2 (1179648 rows, about 36 MB).&lt;/p&gt;
&lt;img alt="/images/blosc2_pytables/append-expectedrows.png" class="align-center" src="https://blosc.org/images/blosc2_pytables/append-expectedrows.png" style="width: 70%;"&gt;
&lt;p&gt;For writing, optimized Blosc2 is able to do the job faster and get better compression ratios than others, mainly because it uses the HDF5 direct chunking mechanism, bypassing the overhead of the HDF5 pipeline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: the standard Blosc2 filter cannot make of use HDF5 direct chunking, but it still has an advantage when using bigger chunks because it allows for more threads working in parallel and hence, allowing improved parallel (de-)compression.&lt;/p&gt;
&lt;p&gt;The plot below shows how optimized Blosc2 is able to read the table faster and how the performance advantage grows as we use more threads.&lt;/p&gt;
&lt;img alt="/images/blosc2_pytables/read-expectedrows.png" class="align-center" src="https://blosc.org/images/blosc2_pytables/read-expectedrows.png" style="width: 70%;"&gt;
&lt;p&gt;And now, let's compare the mean times of Blosc and Blosc2 optimized to read a small slice. In this case, Blosc chunkshape is much smaller, but optimized Blosc2 still can reach similar speed since it uses blocks that are similar in size to Blosc chunks.&lt;/p&gt;
&lt;img alt="/images/blosc2_pytables/slice-read-expectedrows.png" class="align-center" src="https://blosc.org/images/blosc2_pytables/slice-read-expectedrows.png" style="width: 70%;"&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="writing-and-reading-speed-when-using-the-same-chunkshape"&gt;
&lt;h3&gt;Writing and reading speed when using the same chunkshape&lt;/h3&gt;
&lt;p&gt;In this scenario, we are choosing the same chunkshape (720 x 1440 rows, about 32 MB) for both Blosc and Blosc2.  Let's see how this affects performance:&lt;/p&gt;
&lt;img alt="/images/blosc2_pytables/append-chunklen.png" class="align-center" src="https://blosc.org/images/blosc2_pytables/append-chunklen.png" style="width: 70%;"&gt;
&lt;p&gt;The plot above shows how optimized Blosc2 manages to write the table faster (mainly because it can bypass the HDF5 pipeline); with the advantage being larger as more threads are used.&lt;/p&gt;
&lt;img alt="/images/blosc2_pytables/read-chunklen.png" class="align-center" src="https://blosc.org/images/blosc2_pytables/read-chunklen.png" style="width: 70%;"&gt;
&lt;p&gt;Regarding reading, the optimized Blosc2 is able to perform faster too, and we continue to see the same trend of getting more speed when more threads are thrown at the task, with optimized Blosc2 scaling better.&lt;/p&gt;
&lt;p&gt;Finally, let's compare the mean times of Blosc and Blosc2 when reading a small slice in this same chunkshape scenario. In this case, since chunkshapes are equal and large, optimized Blosc2 is much faster than the others because it has the ability to decompresses just the necessary internal blocks, instead of the whole chunks.  However, the Blosc and the Blosc2 filter still need to decompress the whole chunk, so getting much worse times.  See this effect below:&lt;/p&gt;
&lt;img alt="/images/blosc2_pytables/slice-read-chunklen.png" class="align-center" src="https://blosc.org/images/blosc2_pytables/slice-read-chunklen.png" style="width: 70%;"&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="final-words"&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;By allowing a second partition on top of the HDF5 layer, Blosc2 provides a great boost in PyTables I/O speed, specially when using big chunks (mainly when they fit in L3 CPU cache).  That means that you can read, write and query large compressed datasets in less time. Interestingly, Blosc2 compression can make these operations faster than when using no compression at all, and even being competitive against a pure in-memory solution like pandas (but consuming vastly less memory).&lt;/p&gt;
&lt;p&gt;On the other hand, there are situations where using big chunks would not be acceptable. For example, when using other HDF5 apps that do not support the optimized paths for Blosc2 second partition, and one is forced to use the plain Blosc2 filter. In this case having large chunks would penalize the retrieval of small data slices too much. By the way, you can find a nice assortment of generic filters (including Blosc2) for HDF5 in the &lt;a class="reference external" href="https://github.com/silx-kit/hdf5plugin"&gt;hdf5plugin library&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Also note that, in the current implementation we have just provided optimized Blosc2 paths for the &lt;a class="reference external" href="http://www.pytables.org/usersguide/libref/structured_storage.html?highlight=table#tables.Table"&gt;Table&lt;/a&gt; object in PyTables.  That makes sense because &lt;cite&gt;Table&lt;/cite&gt; is probably the most used entity in PyTables.  Other chunked objects in PyTables (like &lt;cite&gt;EArray&lt;/cite&gt; or &lt;cite&gt;CArray&lt;/cite&gt;) could be optimized with Blosc2 in the future too (although that would require providing a &lt;a class="reference external" href="https://github.com/Blosc/caterva"&gt;*multidimensional* second partition for Blosc2&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Last but not least, we would like to thank NumFOCUS and other PyTables donors for providing the funds required to implement Blosc2 support in PyTables.  If you like what we are doing, and would like our effort to continue developing well, you can support our work by donating to &lt;a class="reference external" href="https://numfocus.org/project/pytables"&gt;PyTables project&lt;/a&gt; or &lt;a class="reference external" href="https://numfocus.org/project/blosc"&gt;Blosc project&lt;/a&gt; teams. Thank you!&lt;/p&gt;
&lt;/section&gt;</description><category>blosc2 pytables performance</category><guid>https://blosc.org/posts/blosc2-pytables-perf/</guid><pubDate>Fri, 23 Dec 2022 12:32:20 GMT</pubDate></item><item><title>Announcing Support for Lossy ZFP Codec as a Plugin for C-Blosc2</title><link>https://blosc.org/posts/support-lossy-zfp/</link><dc:creator>Oscar Guiñon, Francesc Alted</dc:creator><description>&lt;section id="announcing-support-for-lossy-zfp-codec-as-a-plugin-for-c-blosc2"&gt;
&lt;h2&gt;Announcing Support for Lossy ZFP Codec as a Plugin for C-Blosc2&lt;/h2&gt;
&lt;p&gt;Blosc supports different filters and codecs for compressing data, like e.g. the lossless &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2/tree/main/plugins/codecs/ndlz"&gt;NDLZ&lt;/a&gt; codec and the &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2/tree/main/plugins/filters/ndcell"&gt;NDCELL&lt;/a&gt; filter.  These have been developed explicitly to be used in   multidimensional datasets (via &lt;a class="reference external" href="https://github.com/Blosc/caterva/"&gt;Caterva&lt;/a&gt; or &lt;a class="reference external" href="https://github.com/ironArray/iarray-community"&gt;ironArray Community Edition&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;However, a lossy codec like &lt;a class="reference external" href="https://zfp.readthedocs.io/"&gt;ZFP&lt;/a&gt; allows for much better compression ratios at the expense of loosing some precision in floating point data.  Moreover, while NDLZ is only available for 2-dim datasets, ZFP can be used up to 4-dim datasets.&lt;/p&gt;
&lt;section id="how-zfp-works"&gt;
&lt;h3&gt;How ZFP works?&lt;/h3&gt;
&lt;p&gt;ZFP partitions datasets into cells of 4^(number of dimensions) values, i.e., 4, 16, 64, or 256 values for 1D, 2D, 3D, and 4D arrays, respectively. Each cell is then (de)compressed independently, and the resulting bit strings are concatenated into a single stream of bits.&lt;/p&gt;
&lt;p&gt;Furthermore, ZFP usually truncates each input value either to a fixed number of bits to meet a storage budget or to some variable length needed to meet a chosen error tolerance.  For more info on how this works, see &lt;a class="reference external" href="https://zfp.readthedocs.io/en/release0.5.5/overview.html"&gt;zfp overview docs&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="zfp-implementation"&gt;
&lt;h3&gt;ZFP implementation&lt;/h3&gt;
&lt;p&gt;Similarly to other registered Blosc2 official plugins, this codec is now available at the &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2/tree/main/plugins/codecs/zfp"&gt;blosc2/plugins directory&lt;/a&gt; of the &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2"&gt;C-Blosc2 repository&lt;/a&gt;.  However, as there are different modes for working with ZFP, there are several associated codec IDs (see later).&lt;/p&gt;
&lt;p&gt;So, in order to use ZFP, users just have to choose the ID for the desired ZFP mode between the ones listed in &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2/blob/main/include/blosc2/codecs-registry.h"&gt;blosc2/codecs-registry.h&lt;/a&gt;. For more info on how the plugin selection mechanism works, see &lt;a class="reference external" href="https://www.blosc.org/posts/registering-plugins/"&gt;https://www.blosc.org/posts/registering-plugins/&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="zfp-modes"&gt;
&lt;h3&gt;ZFP modes&lt;/h3&gt;
&lt;p&gt;ZFP is a lossy codec, but it still lets the user to choose the degree of the data loss.  There are different compression modes:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;BLOSC_CODEC_ZFP_FIXED_ACCURACY:&lt;/strong&gt; The user can choose the absolute error in truncation.  For example, if the desired absolute error is 0.01, each value loss must be less than or equal to 0.01. With that, if 23.0567 is a value of the original input, after compressing and decompressing this input with error=0.01, the new value must be between 23.0467 and 23.0667.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;BLOSC_CODEC_ZFP_FIXED_PRECISION:&lt;/strong&gt; The user specifies the maximum number of bit planes encoded during compression (relative error). This is, for each input value, the number of most significant bits that will be encoded.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;BLOSC_CODEC_ZFP_FIXED_RATE:&lt;/strong&gt; The user chooses the size that the compressed cells must have based on the input cell size. For example, if the cell size is 2000 bytes and user chooses ratio=50, the output cell size will be 50% of 2000 = 1000 bytes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more info, see: &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2/blob/main/plugins/codecs/zfp/README.md"&gt;https://github.com/Blosc/c-blosc2/blob/main/plugins/codecs/zfp/README.md&lt;/a&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="benchmark-zfp-fixed-accuracy-vs-fixed-precision-vs-fixed-rate-modes"&gt;
&lt;h3&gt;Benchmark: ZFP FIXED-ACCURACY VS FIXED_PRECISION VS FIXED-RATE modes&lt;/h3&gt;
&lt;p&gt;The dataset used in this benchmark is called &lt;em&gt;precipitation_amount_1hour_Accumulation.zarr&lt;/em&gt; and has been fetched from &lt;a class="reference external" href="https://www.ecmwf.int/en/forecasts/datasets/reanalysis-datasets/era5"&gt;ERA5 database&lt;/a&gt;, which provides hourly estimates of a large number of atmospheric, land and oceanic climate variables.&lt;/p&gt;
&lt;p&gt;Specifically, the downloaded dataset in Caterva format has this parameters:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;ndim = 3&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;type = float32&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;shape = [720, 721, 1440]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;chunkshape = [128, 128, 256]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;blockshape = [16, 32, 64]&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The next plots represent the compression results obtained by using the different ZFP modes to compress the already mentioned dataset.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; It is important to remark that this is a specific dataset and the codec may perform differently for other ones.&lt;/p&gt;
&lt;img alt="/images/zfp-plugin/ratio_zfp.png" class="align-center" src="https://blosc.org/images/zfp-plugin/ratio_zfp.png" style="width: 100%;"&gt;
&lt;img alt="/images/zfp-plugin/times_zfp.png" class="align-center" src="https://blosc.org/images/zfp-plugin/times_zfp.png" style="width: 100%;"&gt;
&lt;p&gt;Below the bars it is annotated what parameter is used for each test. For example, for the first column, the different compression modes are setup like this:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;FIXED-ACCURACY: for each input value, the absolute error is 10^(-6) = 0.000001.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;FIXED-PRECISION: for each input value, only the 20 most significant bits for the mantissa will be encoded.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;FIXED-RATE: the size of the output cells is 30% of the input cell size.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Although the FIXED-PRECISION mode does not obtain great results, we see that with the FIXED-ACCURACY mode we do get better performance as the absolute error increases.  Similarly, we can see how the FIXED-RATE mode gets the requested ratios, which is cool but, in exchange, the amount of data loss is unknown.&lt;/p&gt;
&lt;p&gt;Also, while FIXED-ACCURACY and FIXED-RATE modes consume similar times, the FIXED-PRECISION mode, which seems to have less data loss, also takes longer to compress.  Generally speaking we can see how, the more data loss (more data truncation) achieved by a mode, the faster it operates.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="third-partition"&gt;
&lt;h3&gt;"Third partition"&lt;/h3&gt;
&lt;p&gt;One of the most appealing features of Caterva besides supporting multi-dimensionality is its implementation of a second partition, &lt;a class="reference external" href="https://www.blosc.org/posts/caterva-slicing-perf/"&gt;making slicing more efficient&lt;/a&gt;.  As one of the distinctive characteristics of ZFP is that it compresses data in independent (and small) cells, we have been toying with the idea of implementing a third partition so that slicing of thin selections or just single-point selection can be made faster.&lt;/p&gt;
&lt;p&gt;So, as part of the current ZFP implementation, we have combined the Caterva/Blosc2 partitioning (chunking and blocking) with the independent cell handling of ZFP, allowing to extract single cells within the ZFP streams (blocks in Blosc jargon). Due to the properties and limitations of the different ZFP compression modes, we have been able to implement a sort of "third partition" just for the &lt;em&gt;FIXED-RATE&lt;/em&gt; mode when used together with the &lt;a class="reference external" href="https://c-blosc2.readthedocs.io/en/latest/reference/context.html?highlight=blosc_getitem#c.blosc2_getitem_ctx"&gt;blosc2_getitem_ctx()&lt;/a&gt; function.&lt;/p&gt;
&lt;p&gt;Such a combination of the existing partitioning and single cell extraction is useful for selecting more narrowly the data to extract, saving time and memory.  As an example, below you can see a comparison of the mean times that it takes to retrieve a bunch of single elements out of different multidimensional arrays from the ERA5 dataset (see above).  Here we have used Blosc2 with a regular LZ4 codec compared against the FIXED-RATE mode of the new ZFP codec:&lt;/p&gt;
&lt;img alt="/images/zfp-plugin/zfp_fixed_rate.png" class="align-center" src="https://blosc.org/images/zfp-plugin/zfp_fixed_rate.png" style="width: 100%;"&gt;
&lt;p&gt;As you can see, using the ZFP codec in FIXED-RATE mode allows for a good improvement in speed (up to more than 2x) for retrieving single elements (or, in general an amount not exceeding the cell size) in comparison with the existing codecs (even the fastest ones, like LZ4) inside Blosc2.  As the performance improvement is of the same order than random access time of modern SSDs, we anticipate that this could be a major win in scenarios where random access is important.&lt;/p&gt;
&lt;p&gt;If you are curious on how this new functionality performs for your own datasets and computer, you can use/adapt our &lt;a class="reference external" href="https://github.com/Blosc/caterva/blob/master/bench/bench_zfp_getitem.c"&gt;benchmark code&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conclusions"&gt;
&lt;h3&gt;Conclusions&lt;/h3&gt;
&lt;p&gt;The integration of ZFP as a codec plugin will greatly enhance the capabilities of lossy compression inside C-Blosc2.  The current ZFP plugin supports different modes; if users want to specify data loss during compression, it is recommended to use the FIXED-ACCURACY or FIXED-PRECISION modes (and most specially the former because of its better compression performance).&lt;/p&gt;
&lt;p&gt;However, if the priority is to get specific compression ratios without paying too much attention to the amount of data loss, one should use the FIXED-RATE mode, which let choose the desired compression ratio.  This mode also has the advantage that a "third partition" can be used for improving random elements access speed.&lt;/p&gt;
&lt;p&gt;This work has been done thanks to a Small Development Grant from the &lt;a class="reference external" href="https://numfocus.org"&gt;NumFOCUS Foundation&lt;/a&gt;, to whom we are very grateful indeed. NumFOCUS is doing a excellent job in sponsoring scientific projects and you can donate to the Blosc project (or many others under the NumFOCUS umbrella) via its &lt;a class="reference external" href="https://numfocus.org/support#donate"&gt;donation page&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;</description><category>blosc plugins zfp lossy</category><guid>https://blosc.org/posts/support-lossy-zfp/</guid><pubDate>Fri, 11 Mar 2022 10:32:20 GMT</pubDate></item><item><title>Caterva Slicing Performance: A Study</title><link>https://blosc.org/posts/caterva-slicing-perf/</link><dc:creator>Oscar Guiñon, Francesc Alted</dc:creator><description>&lt;img alt="/images/cat_slicing/caterva.png" class="align-center" src="https://blosc.org/images/cat_slicing/caterva.png" style="width: 50%;"&gt;
&lt;p&gt;&lt;a class="reference external" href="https://caterva.readthedocs.io/en/latest/getting_started/overview.html"&gt;Caterva&lt;/a&gt; is a C library for handling multi-dimensional, chunked, compressed datasets in an easy and fast way.  It is build on top of the &lt;a class="reference external" href="https://c-blosc2.readthedocs.io/en/latest/"&gt;C-Blosc2&lt;/a&gt; library, leveraging all its &lt;a class="reference external" href="https://www.blosc.org/posts/blosc2-ready-general-review/"&gt;avantages on modern CPUs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Caterva can be used in a lot of different situations; however, where it really stands out is for extracting multidimensional slices of compressed datasets because, thanks to the double partitioning schema that it implements, the amount of data that has to be decompressed so as to get the slice is minimized, making data extraction faster (usually).  In this installment, you will be offered a rational on how double partitioning works, together with some examples where it shines, and others where it is not that good.&lt;/p&gt;
&lt;section id="double-partitioning"&gt;
&lt;h2&gt;Double partitioning&lt;/h2&gt;
&lt;img alt="/images/cat_slicing/cat_vs_zarr,hdf5.png" class="align-center" src="https://blosc.org/images/cat_slicing/cat_vs_zarr,hdf5.png" style="width: 70%;"&gt;
&lt;p&gt;Some libraries like &lt;a class="reference external" href="https://www.hdfgroup.org/solutions/hdf5/"&gt;HDF5&lt;/a&gt; or &lt;a class="reference external" href="https://zarr.readthedocs.io/en/stable/"&gt;Zarr&lt;/a&gt; store data into multidimensional chunks. This makes slice extraction from compressed datasets more efficient than using monolithic compression, since only the chunks containing the interesting slice are decompressed instead of the entire array.&lt;/p&gt;
&lt;p&gt;In addition, Caterva introduces a new level of partitioning.  Within each chunk, the data is re-partitioned into smaller multidimensional sets called blocks.  This generally improves the slice extraction, since this allows to decompress only the blocks containing the data in desired slice instead of the whole chunks.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="slice-extraction-with-caterva-hdf5-and-zarr"&gt;
&lt;h2&gt;Slice extraction with Caterva, HDF5 and Zarr&lt;/h2&gt;
&lt;p&gt;So as to see how the double partitioning performs with respect to a traditional single partition schema, we are going to compare the ability to extract multidimensional slices from compressed data of Caterva, HDF5 and Zarr. The examples below consist on extracting some hyper-planes from chunked arrays with different properties and seeing how Caterva performs compared with traditional libraries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; So as to better compare apples with apples, all the benchmarks below have been run using Blosc (with LZ4 as the internal codec) as the compressor by default, with the shuffle filter.  Even if Caterva uses the newest C-Blosc2 compressor, and HDF5 and Zarr uses its C-Blosc(1) antecessor, the performance of both libraries are very similar.  Also, for easier interactivity, we have used the libraries via Python wrappers (&lt;a class="reference external" href="https://python-caterva.readthedocs.io/en/latest/"&gt;python-caterva&lt;/a&gt;, &lt;a class="reference external" href="http://www.h5py.org"&gt;h5py&lt;/a&gt;, &lt;a class="reference external" href="https://zarr.readthedocs.io/en/stable/"&gt;Zarr&lt;/a&gt;).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="dimensional-array"&gt;
&lt;h2&gt;2-dimensional array&lt;/h2&gt;
&lt;p&gt;This is a 2-dimensional array and has the following properties, designed to optimize slice extraction from the second dimension:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code console"&gt;&lt;a id="rest_code_8fcd3ba9fece4e03b42a8dbc7c2a7ed6-1" name="rest_code_8fcd3ba9fece4e03b42a8dbc7c2a7ed6-1" href="https://blosc.org/posts/caterva-slicing-perf/#rest_code_8fcd3ba9fece4e03b42a8dbc7c2a7ed6-1"&gt;&lt;/a&gt;&lt;span class="go"&gt;shape = (8_000, 8_000)&lt;/span&gt;
&lt;a id="rest_code_8fcd3ba9fece4e03b42a8dbc7c2a7ed6-2" name="rest_code_8fcd3ba9fece4e03b42a8dbc7c2a7ed6-2" href="https://blosc.org/posts/caterva-slicing-perf/#rest_code_8fcd3ba9fece4e03b42a8dbc7c2a7ed6-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;chunkshape = (4_000, 100)&lt;/span&gt;
&lt;a id="rest_code_8fcd3ba9fece4e03b42a8dbc7c2a7ed6-3" name="rest_code_8fcd3ba9fece4e03b42a8dbc7c2a7ed6-3" href="https://blosc.org/posts/caterva-slicing-perf/#rest_code_8fcd3ba9fece4e03b42a8dbc7c2a7ed6-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;blockshape = (500, 25)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here we can see that the ratio between chunkshape and blockshape is 8x in dimension 0 and 4x in dimension 1.&lt;/p&gt;
&lt;img alt="/images/cat_slicing/dim0.png" class="align-center" src="https://blosc.org/images/cat_slicing/dim0.png" style="width: 70%;"&gt;
&lt;img alt="/images/cat_slicing/dim1.png" class="align-center" src="https://blosc.org/images/cat_slicing/dim1.png" style="width: 70%;"&gt;
&lt;p&gt;Now we are going to extract some planes from the chunked arrays and will plot the performance. For dimension 0 we extract a hyperplane &lt;cite&gt;[i, :]&lt;/cite&gt;, and for dimension 1, &lt;cite&gt;[:, i]&lt;/cite&gt;, where &lt;em&gt;i&lt;/em&gt; is a random integer.&lt;/p&gt;
&lt;img alt="/images/cat_slicing/2dim.png" class="align-center" src="https://blosc.org/images/cat_slicing/2dim.png" style="width: 80%;"&gt;
&lt;p&gt;Here we see that the slicing times are similar in the dimension 1. However, Caterva performs better in the dimension 0. This is because with double partitioning you only have to decompress the blocks containing the slice instead of the whole chunk.&lt;/p&gt;
&lt;p&gt;In fact, Caterva is around 12x faster than HDF5 and 9x faster than Zarr for slicing the dimension 0, which makes sense since Caterva decompresses 8x less data.
For the dimension 1, Caterva is approximately 3x faster than HDF5 and Zarr; in this case Caterva has to decompress 4x less data.&lt;/p&gt;
&lt;p&gt;That is, the difference in slice extraction speed depends largely on the ratio between the chunk size and the block size. Therefore, for slices where the chunks that contain the slice also have many items that do not belong to it, the existence of blocks (i.e. the second partition) allows to significantly reduce the amount of data to decompress.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="overhead-of-the-second-partition"&gt;
&lt;h2&gt;Overhead of the second partition&lt;/h2&gt;
&lt;p&gt;So as to better assess the possible performance cost of the second partition, let's analyze a new case of a 3-dimensional array with the following parameters:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code console"&gt;&lt;a id="rest_code_1e56182bd6334209b0dd29bd2ea0f0dd-1" name="rest_code_1e56182bd6334209b0dd29bd2ea0f0dd-1" href="https://blosc.org/posts/caterva-slicing-perf/#rest_code_1e56182bd6334209b0dd29bd2ea0f0dd-1"&gt;&lt;/a&gt;&lt;span class="go"&gt;shape = (800, 600, 300)&lt;/span&gt;
&lt;a id="rest_code_1e56182bd6334209b0dd29bd2ea0f0dd-2" name="rest_code_1e56182bd6334209b0dd29bd2ea0f0dd-2" href="https://blosc.org/posts/caterva-slicing-perf/#rest_code_1e56182bd6334209b0dd29bd2ea0f0dd-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;chunkshape = (200, 100, 80)&lt;/span&gt;
&lt;a id="rest_code_1e56182bd6334209b0dd29bd2ea0f0dd-3" name="rest_code_1e56182bd6334209b0dd29bd2ea0f0dd-3" href="https://blosc.org/posts/caterva-slicing-perf/#rest_code_1e56182bd6334209b0dd29bd2ea0f0dd-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;blockshape = (20, 100, 10)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So, in the dimensions 0 and 2 the difference between shape and chunkshape is not too big whereas the difference between chunkshape and blockshape is remarkable.&lt;/p&gt;
&lt;p&gt;However, for the dimension 1, there is not a difference at all between chunkshape and blockshape.  This means that in dim 1 the Caterva machinery will make extra work because of the double partitioning, but it will not get any advantage of it since the block size is going to be equal to the chunk size.  This a perfect scenario for measuring the overhead of the second partition.&lt;/p&gt;
&lt;p&gt;The slices to extract will be &lt;cite&gt;[i, :, :]&lt;/cite&gt;, &lt;cite&gt;[:, i, :]&lt;/cite&gt; or &lt;cite&gt;[:, :, i]&lt;/cite&gt;. Let's see the execution times for slicing these planes:&lt;/p&gt;
&lt;img alt="/images/cat_slicing/3dim.png" class="align-center" src="https://blosc.org/images/cat_slicing/3dim.png" style="width: 80%;"&gt;
&lt;p&gt;As we can see, the performance in dim 1 is around the same order than HDF5 and Zarr (Zarr being a bit faster actually), but difference is not large, so that means that the overhead introduced purely by the second partition is not that important.
However, in the other dimensions Caterva still outperforms (by far) Zarr and HDF5.  This is because the two level partitioning works as intended here.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="a-last-hyper-slicing-example"&gt;
&lt;h2&gt;A last hyper-slicing example&lt;/h2&gt;
&lt;p&gt;Let's see a final example showing the double partitioning working on a wide range of dimensions.  In this case we choose a 4-dimensional array with the following parameters:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code console"&gt;&lt;a id="rest_code_060b3dddc1c74d01a830e5db7a71a4c3-1" name="rest_code_060b3dddc1c74d01a830e5db7a71a4c3-1" href="https://blosc.org/posts/caterva-slicing-perf/#rest_code_060b3dddc1c74d01a830e5db7a71a4c3-1"&gt;&lt;/a&gt;&lt;span class="go"&gt;shape = (400, 80, 100, 50)&lt;/span&gt;
&lt;a id="rest_code_060b3dddc1c74d01a830e5db7a71a4c3-2" name="rest_code_060b3dddc1c74d01a830e5db7a71a4c3-2" href="https://blosc.org/posts/caterva-slicing-perf/#rest_code_060b3dddc1c74d01a830e5db7a71a4c3-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;chunkshape = (100, 40, 10, 50)&lt;/span&gt;
&lt;a id="rest_code_060b3dddc1c74d01a830e5db7a71a4c3-3" name="rest_code_060b3dddc1c74d01a830e5db7a71a4c3-3" href="https://blosc.org/posts/caterva-slicing-perf/#rest_code_060b3dddc1c74d01a830e5db7a71a4c3-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;blockshape = (30, 5, 2, 10)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here the last dimension (3) is not optimized for getting hyper-slices, specially in containers with just single partitioning (Zarr and HDF5).  However, Caterva should still perform well in this situation because of the double partitioning.&lt;/p&gt;
&lt;p&gt;The slices we are going to extract will be &lt;cite&gt;[i, :, :, :]&lt;/cite&gt;, &lt;cite&gt;[:, i, :, :]&lt;/cite&gt;, &lt;cite&gt;[:, :, i, :]&lt;/cite&gt; or &lt;cite&gt;[:, :, :, i]&lt;/cite&gt;. Let's see the execution times for slicing these hyperplanes:&lt;/p&gt;
&lt;img alt="/images/cat_slicing/4dim.png" class="align-center" src="https://blosc.org/images/cat_slicing/4dim.png" style="width: 80%;"&gt;
&lt;p&gt;As we can see, in this case Caterva outperforms Zarr and HDF5 in all dimensions.  However, the advantage is not that important for the last dimension.  The reason is that in this last dimension Caterva has a noticeably lower ratio between its shape and blockshape than in the other dimensions.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="final-thoughts"&gt;
&lt;h2&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;We have seen that adding a second partition is beneficial for improving slicing performance in general.  Of course, there are some situations where the overhead of the second partition can be noticeable, but the good news is that such an overhead does not get too large when compared with containers with only one level of partitioning.&lt;/p&gt;
&lt;p&gt;Finally, we can conclude that Caterva usually obtains better results due to its second partitioning, but when it shines the most is when the two levels of partitioning are well balanced among them and also with respect to the shape of the container.&lt;/p&gt;
&lt;p&gt;As always, there is no replacement for experimentation so, in case you want to try Caterva by yourself (and you should if you really care about this problem), you can use &lt;a class="reference external" href="https://github.com/Blosc/caterva-scipy21"&gt;our Caterva poster&lt;/a&gt;; it is based on a Jupyter notebook that you can adapt to your own scenarios.&lt;/p&gt;
&lt;/section&gt;</description><category>caterva slicing perf</category><guid>https://blosc.org/posts/caterva-slicing-perf/</guid><pubDate>Mon, 26 Jul 2021 04:32:20 GMT</pubDate></item></channel></rss>