<?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 Marta Iborra, Francesc Alted)</title><link>https://blosc.org/</link><description></description><atom:link href="https://blosc.org/authors/marta-iborra-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>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>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>Dynamic plugins in C-Blosc2</title><link>https://blosc.org/posts/dynamic-plugins/</link><dc:creator>Marta Iborra, Francesc Alted</dc:creator><description>&lt;p&gt;&lt;strong&gt;Updated: 2023-08-03&lt;/strong&gt;
Added a new example of a dynamic filter for Python. Also, we have improved the content so that it can work more as a tutorial on how to make dynamic plugins for Blosc2. Finally, there is support now for dynamic plugins on Windows and MacOS/ARM64. Enjoy!&lt;/p&gt;
&lt;p&gt;The Blosc Development Team is excited to announce that the latest version of &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2"&gt;C-Blosc2&lt;/a&gt; and &lt;a class="reference external" href="https://github.com/Blosc/python-blosc2"&gt;Python-Blosc2&lt;/a&gt;
include a great new feature: the ability to dynamically load plugins, such as codecs and filters. This means that these codecs
or filters will only be loaded at runtime when they are needed. These C libraries will be easily distributed inside Python
wheels and be used from both C and Python code without problems.  Keep reading for a gentle introduction to this new feature.&lt;/p&gt;
&lt;section id="creating-a-dynamically-loaded-filter"&gt;
&lt;h2&gt;Creating a dynamically loaded filter&lt;/h2&gt;
&lt;p&gt;To learn how to create dynamic plugins, we'll use an &lt;a class="reference external" href="https://github.com/Blosc/blosc2_plugin_example"&gt;already created example&lt;/a&gt;.  Suppose you have a filter that you want Blosc2 to load dynamically only when it is used. In this case, you need to create a Python package to build a wheel and install it as a separate library. You can follow the structure used in &lt;a class="reference external" href="https://github.com/Blosc/blosc2_plugin_example"&gt;blosc2_plugin_example&lt;/a&gt; to do this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;├── CMakeLists.txt
├── README.md
├── blosc2_plugin_name
│   └── __init__.py
├── examples
│   ├── array_roundtrip.py
│   ├── schunk_roundtrip.py
│   └── test_plugin.c
├── pyproject.toml
├── requirements-build.txt
├── setup.py
└── src
    ├── CMakeLists.txt
    ├── urfilters.c
    └── urfilters.h&lt;/pre&gt;
&lt;p&gt;Note that the project name has to be &lt;cite&gt;blosc2_&lt;/cite&gt; followed by the plugin name (&lt;cite&gt;plugin_example&lt;/cite&gt; in this case). The corresponding functions will be defined in the &lt;cite&gt;src&lt;/cite&gt; folder, in our case in &lt;cite&gt;urfilters.c&lt;/cite&gt;, following the same format as functions for user-defined filters (see &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2/blob/main/plugins/README.md"&gt;https://github.com/Blosc/c-blosc2/blob/main/plugins/README.md&lt;/a&gt; for more information).  Here it is the sample code:&lt;/p&gt;
&lt;pre class="literal-block"&gt;int blosc2_plugin_example_forward(const uint8_t* src, uint8_t* dest,
                                  int32_t size, uint8_t meta,
                                  blosc2_cparams *cparams, uint8_t id) {
  blosc2_schunk *schunk = cparams-&amp;gt;schunk;

  for (int i = 0; i &amp;lt; size / schunk-&amp;gt;typesize; ++i) {
    switch (schunk-&amp;gt;typesize) {
      case 8:
        ((int64_t *) dest)[i] = ((int64_t *) src)[i] + 1;
        break;
      default:
        BLOSC_TRACE_ERROR("Item size %d not supported", schunk-&amp;gt;typesize);
        return BLOSC2_ERROR_FAILURE;
    }
  }
  return BLOSC2_ERROR_SUCCESS;
}


int blosc2_plugin_example_backward(const uint8_t* src, uint8_t* dest, int32_t size,
                                   uint8_t meta, blosc2_dparams *dparams, uint8_t id) {
  blosc2_schunk *schunk = dparams-&amp;gt;schunk;

  for (int i = 0; i &amp;lt; size / schunk-&amp;gt;typesize; ++i) {
    switch (schunk-&amp;gt;typesize) {
      case 8:
        ((int64_t *) dest)[i] = ((int64_t *) src)[i] - 1;
        break;
      default:
        BLOSC_TRACE_ERROR("Item size %d not supported", schunk-&amp;gt;typesize);
        return BLOSC2_ERROR_FAILURE;
    }
  }
  return BLOSC2_ERROR_SUCCESS;
}&lt;/pre&gt;
&lt;p&gt;In addition to these functions, we need to create a &lt;cite&gt;filter_info&lt;/cite&gt; (or &lt;cite&gt;codec_info&lt;/cite&gt; or &lt;cite&gt;tune_info&lt;/cite&gt; in each case) named &lt;cite&gt;info&lt;/cite&gt;. This variable will contain the names of the &lt;cite&gt;forward&lt;/cite&gt; and &lt;cite&gt;backward&lt;/cite&gt; functions. In our case, we will have:&lt;/p&gt;
&lt;pre class="literal-block"&gt;filter_info info  = {"blosc2_plugin_example_forward", "blosc2_plugin_example_backward"};&lt;/pre&gt;
&lt;p&gt;To find the functions, the variable must always be named &lt;cite&gt;info&lt;/cite&gt;. Furthermore, the symbols &lt;cite&gt;info&lt;/cite&gt; and the functions
&lt;cite&gt;forward&lt;/cite&gt; and &lt;cite&gt;backward&lt;/cite&gt; must be exported in order for Windows to find them. You can see all the details for doing that in
the &lt;a class="reference external" href="https://github.com/Blosc/blosc2_plugin_example/blob/cbbcab59a6abf5d1a0767604b1987edd34695fe8/src/urfilters.h#L46-L50"&gt;blosc2_plugin_example repository&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="creating-and-installing-the-wheel"&gt;
&lt;h2&gt;Creating and installing the wheel&lt;/h2&gt;
&lt;p&gt;Once the project is done, you can create a wheel and install it locally:&lt;/p&gt;
&lt;pre class="literal-block"&gt;python setup.py bdist_wheel
pip install dist/*.whl&lt;/pre&gt;
&lt;p&gt;This wheel can be uploaded to PyPI so that anybody can use it. Once tested and stable enough, you can &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2/blob/main/plugins/README.md"&gt;request the Blosc Team to register it globally&lt;/a&gt;. This way, an ID for the filter or codec will be booked so that the data will always be able to be encoded/decoded by the same code, ensuring portability.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="registering-the-plugin-in-c-blosc2"&gt;
&lt;h2&gt;Registering the plugin in C-Blosc2&lt;/h2&gt;
&lt;p&gt;After installation, and prior to use it, you must register it in C-Blosc2. This step is necessary only if the filter is not already registered globally by C-Blosc2, which is likely if you are testing it or you are not ready to share it with other users. To register it, follow the same process as registering a &lt;a class="reference external" href="https://www.blosc.org/posts/registering-plugins/"&gt;user-defined plugin&lt;/a&gt;, but leave the function pointers as NULL:&lt;/p&gt;
&lt;pre class="literal-block"&gt;blosc2_filter plugin_example;
plugin_example.id = 250;
plugin_example.name = "plugin_example";
plugin_example.version = 1;
plugin_example.forward = NULL;
plugin_example.backward = NULL;
blosc2_register_filter(&amp;amp;plugin_example);&lt;/pre&gt;
&lt;p&gt;When the filter is used for the first time, C-Blosc2 will automatically fill in the function pointers.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="registering-the-plugin-in-python-blosc2"&gt;
&lt;h2&gt;Registering the plugin in Python-Blosc2&lt;/h2&gt;
&lt;p&gt;The same applies for Python-Blosc2. You can register the filter as follows:&lt;/p&gt;
&lt;pre class="literal-block"&gt;import blosc2
blosc2.register_filter(250, None, None, "plugin_example")&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="using-the-plugin-in-c-blosc2"&gt;
&lt;h2&gt;Using the plugin in C-Blosc2&lt;/h2&gt;
&lt;p&gt;To use the plugin, simply set the filter ID in the filters pipeline, as you would do with user-defined filters:&lt;/p&gt;
&lt;pre class="literal-block"&gt;blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS;
cparams.filters[4] = 250;
cparams.filters_meta[4] = 0;

blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS;

blosc2_schunk* schunk;

/* Create a super-chunk container */
cparams.typesize = sizeof(int32_t);
blosc2_storage storage = {.cparams=&amp;amp;cparams, .dparams=&amp;amp;dparams};
schunk = blosc2_schunk_new(&amp;amp;storage);&lt;/pre&gt;
&lt;p&gt;To see a full usage example, refer to &lt;a class="reference external" href="https://github.com/Blosc/blosc2_plugin_example/blob/main/examples/test_plugin.c"&gt;https://github.com/Blosc/blosc2_plugin_example/blob/main/examples/test_plugin.c&lt;/a&gt;. Keep in mind that the executable using the plugin must be launched from the same virtual environment where the plugin wheel was installed. When compressing or decompressing, C-Blosc2 will dynamically load the library and call its functions automatically (as depicted below).&lt;/p&gt;
&lt;img alt="Dynamically loading filter" src="https://blosc.org/images/dynamic-plugins/dynamic-plugin.png" style="width: 100%;"&gt;
&lt;p&gt;Once you are satisfied with your plugin, you may choose to request the Blosc Development Team to register it as a global plugin. The only difference (aside from its ID number) is that users won't need to register it locally anymore. Also, a dynamic plugin will not be loaded until it is explicitly requested by any compression or decompression function, saving resources.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="using-the-plugin-in-python-blosc2"&gt;
&lt;h2&gt;Using the plugin in Python-Blosc2&lt;/h2&gt;
&lt;p&gt;As in C-Blosc2, just set the filter ID in the filters pipeline, as you would do with user-defined filters:&lt;/p&gt;
&lt;pre class="literal-block"&gt;shape = [100, 100]
size = int(np.prod(shape))
nparray = np.arange(size, dtype=np.int32).reshape(shape)
blosc2_array = blosc2.asarray(nparray, cparams={"filters": [250]})&lt;/pre&gt;
&lt;p&gt;To see a full usage example, refer to &lt;a class="reference external" href="https://github.com/Blosc/blosc2_plugin_example/blob/main/examples/array_roundtrip.py"&gt;https://github.com/Blosc/blosc2_plugin_example/blob/main/examples/array_roundtrip.py&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conclusions"&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;C-Blosc2's ability to support dynamically loaded plugins allows the library to grow in features without increasing the size and complexity of the library itself. For more information about user-defined plugins, refer to this &lt;a class="reference external" href="https://www.blosc.org/posts/registering-plugins/"&gt;blog entry&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We appreciate your interest in our project! If you find our work useful and valuable, we would be grateful if you could support us by &lt;a class="reference external" href="https://www.blosc.org/pages/donate/"&gt;making a donation&lt;/a&gt;. Your contribution will help us continue to develop and improve Blosc packages, making them more accessible and useful for everyone.  Our team is committed to creating high-quality and efficient software, and your support will help us to achieve this goal.&lt;/p&gt;
&lt;/section&gt;</description><category>blosc2 plugin dynamic</category><guid>https://blosc.org/posts/dynamic-plugins/</guid><pubDate>Wed, 10 May 2023 08:32:20 GMT</pubDate></item><item><title>New features in Python-Blosc2</title><link>https://blosc.org/posts/python-blosc2-improvements/</link><dc:creator>Marta Iborra, Francesc Alted</dc:creator><description>&lt;p&gt;The Blosc Development Team is happy to announce that the latest version of &lt;a class="reference external" href="https://github.com/Blosc/python-blosc2"&gt;Python-Blosc2 0.4&lt;/a&gt;.  It comes with new and exciting features, like a handier way of setting, expanding and getting the data and metadata from a super-chunk (&lt;cite&gt;SChunk&lt;/cite&gt;) instance.  Contrarily to chunks, a super-chunk can update and resize the data that it containes, supports user metadata, and it does not have the 2 GB storage limitation.&lt;/p&gt;
&lt;p&gt;Additionally, you can now convert now a &lt;cite&gt;SChunk&lt;/cite&gt; into a contiguous, serialized buffer (aka &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2/blob/main/README_CFRAME_FORMAT.rst"&gt;cframe&lt;/a&gt;) and vice-versa; as a bonus, this serialization process also works with a NumPy array at a blazing speed.&lt;/p&gt;
&lt;p&gt;Continue reading for knowing the new features a bit more in depth.&lt;/p&gt;
&lt;section id="retrieve-data-with-getitem-and-get-slice"&gt;
&lt;h2&gt;Retrieve data with &lt;cite&gt;__getitem__&lt;/cite&gt; and &lt;cite&gt;get_slice&lt;/cite&gt;&lt;/h2&gt;
&lt;p&gt;The most general way to store data in Python-Blosc2 is through a &lt;cite&gt;SChunk&lt;/cite&gt; (super-chunk) object. Here the data is split into chunks of the same size. So until now, the only way of working with it was chunk by chunk (see &lt;a class="reference external" href="https://www.blosc.org/python-blosc2/getting_started/tutorials/07.schunk-basics.html"&gt;tutorial&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;With the new version, you can get general data slices with the handy &lt;cite&gt;__getitem__()&lt;/cite&gt; method without having to mess with chunks manually.  The only inconvenience is that this returns a bytes object, which is difficult to read by humans.  To overcome this, we have also implemented the &lt;cite&gt;get_slice()&lt;/cite&gt; method; it comes with two optional params: &lt;cite&gt;start&lt;/cite&gt; and &lt;cite&gt;stop&lt;/cite&gt; for selecting the slice you are interested in.  Also, you can pass to &lt;cite&gt;out&lt;/cite&gt; any Python object supporting the &lt;a class="reference external" href="http://jakevdp.github.io/blog/2014/05/05/introduction-to-the-python-buffer-protocol/"&gt;Buffer Protocol&lt;/a&gt; and it will be filled with the data slice.  One common example is to pass a NumPy array in the &lt;cite&gt;out&lt;/cite&gt; argument:&lt;/p&gt;
&lt;pre class="literal-block"&gt;out_slice = numpy.empty(chunksize * nchunks, dtype=numpy.int32)
schunk.get_slice(out=out_slice)&lt;/pre&gt;
&lt;p&gt;We have now the &lt;cite&gt;out_slice&lt;/cite&gt; NumPy array filled with the &lt;cite&gt;schunk&lt;/cite&gt; data.  Easy and effective.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="set-data-with-setitem"&gt;
&lt;h2&gt;Set data with &lt;cite&gt;__setitem__&lt;/cite&gt;&lt;/h2&gt;
&lt;p&gt;Similarly, if we would like to set data, we had different ways of doing it, e.g. with the &lt;cite&gt;update_chunk()&lt;/cite&gt; or the &lt;cite&gt;update_data()&lt;/cite&gt; methods. But those work, again, chunk by chunk, which was a bummer. That's why we also implemented the convenient &lt;cite&gt;__setitem__()&lt;/cite&gt; method.  In a similar way to the &lt;cite&gt;get_slice()&lt;/cite&gt; method, the value to be set can be any Python object supporting the Buffer Protocol. In addition, this method is very flexible because it not only can set the data of an already existing slice of the SChunk, but it also can expand (and update at the same time) it.&lt;/p&gt;
&lt;p&gt;To do so, the &lt;cite&gt;stop&lt;/cite&gt; param will set the new number of items in SChunk:&lt;/p&gt;
&lt;pre class="literal-block"&gt;start = schunk_nelems - 123
stop = start + new_value.size   # new number of items
schunk[start:stop] = new_value&lt;/pre&gt;
&lt;p&gt;In the code above, the data between &lt;cite&gt;start&lt;/cite&gt; and the SChunk current size will be updated and then, the data between the previous SChunk &lt;cite&gt;size&lt;/cite&gt; and the new &lt;cite&gt;stop&lt;/cite&gt; will be appended automatically for you.  This is very handy indeed (note that the &lt;cite&gt;step&lt;/cite&gt; parameter is not yet supported though).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="serialize-schunk-from-to-a-contiguous-compressed-buffer"&gt;
&lt;h2&gt;Serialize SChunk from/to a contiguous compressed buffer&lt;/h2&gt;
&lt;p&gt;Super-chunks can be serialized in two slightly different format frames: contiguous and sparse.  A contiguous frame (aka &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2/blob/main/README_CFRAME_FORMAT.rst"&gt;cframe&lt;/a&gt;) serializes the whole super-chunk data and metadata into a sequential buffer, whereas the sparse frame (aka &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2/blob/main/README_SFRAME_FORMAT.rst"&gt;sframe&lt;/a&gt;) uses a contiguous frame for metadata and the data is stored separately in so-called &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2/blob/main/README_CHUNK_FORMAT.rst"&gt;chunks&lt;/a&gt;. Here it is how they look like:&lt;/p&gt;
&lt;img alt="Contiguous and sparse frames" class="align-center" src="https://blosc.org/images/python-blosc2-improvements/frame-blosc2.png"&gt;
&lt;p&gt;The contiguous and sparse formats come with its own pros and cons.  A contiguous frame is ideal for transmitting / storing data as a whole buffer / file, while the sparse one is better suited to act as a store while a super-chunk is being built.&lt;/p&gt;
&lt;p&gt;In this new version of Python-Blosc2 we have added a method to convert from a SChunk to a contiguous, serialized buffer:&lt;/p&gt;
&lt;pre class="literal-block"&gt;buf = schunk.to_cframe()&lt;/pre&gt;
&lt;p&gt;as well as a function to build back a &lt;cite&gt;SChunk&lt;/cite&gt; instance from that buffer:&lt;/p&gt;
&lt;pre class="literal-block"&gt;schunk = schunk_from_cframe(buf)&lt;/pre&gt;
&lt;p&gt;This allows for a nice way to serialize / deserialize super-chunks for transmission / storage purposes.  Also, and for performance reasons, and for reducing memory usage to a maximum, these functions avoid copies as much as possible.  For example, the &lt;a class="reference external" href="https://www.blosc.org/python-blosc2/reference/autofiles/utils/blosc2.schunk_from_cframe.html"&gt;schunk_from_cframe&lt;/a&gt; function can build a &lt;cite&gt;SChunk&lt;/cite&gt; instance without copying the data in &lt;cite&gt;cframe&lt;/cite&gt;.  Such a capability makes the use of cframes very desirable whenever you have to transmit and re-create data from one machine to another in a very efficient way.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="serializing-numpy-arrays"&gt;
&lt;h2&gt;Serializing NumPy arrays&lt;/h2&gt;
&lt;p&gt;Last but not least, you can also serialize NumPy arrays with the new pair of functions &lt;a class="reference external" href="https://www.blosc.org/python-blosc2/reference/autofiles/top_level/blosc2.pack_array2.html"&gt;pack_array2()&lt;/a&gt; / &lt;a class="reference external" href="https://www.blosc.org/python-blosc2/reference/autofiles/top_level/blosc2.unpack_array2.html"&gt;unpack_array2()&lt;/a&gt;. Although you could already do this with the existing &lt;a class="reference external" href="https://www.blosc.org/python-blosc2/reference/autofiles/top_level/blosc2.pack_array.html"&gt;pack_array()&lt;/a&gt; / &lt;a class="reference external" href="https://www.blosc.org/python-blosc2/reference/autofiles/top_level/blosc2.unpack_array.html"&gt;unpack_array()&lt;/a&gt; functions, the new ones are much faster and do not have the 2 GB size limitation.
To prove this, let's see its performance by looking at some benchmark results obtained with an Intel box (i9-10940X CPU @ 3.30GHz, 14 cores) running Ubuntu 22.04.&lt;/p&gt;
&lt;p&gt;In this benchmark we are comparing a plain NumPy array copy against compression/decompression through different compressors and functions (&lt;cite&gt;compress() / decompress()&lt;/cite&gt;, &lt;cite&gt;pack_array() / unpack_array()&lt;/cite&gt; and &lt;cite&gt;pack_array2() / unpack_array2()&lt;/cite&gt;). The data distribution for the plots below is for 3 different data distributions: &lt;a class="reference external" href="https://github.com/Blosc/python-blosc2/blob/main/bench/pack_compress.py"&gt;arange, linspace and random&lt;/a&gt;:&lt;/p&gt;
&lt;img alt="Compression ratio for different codecs" class="align-center" src="https://blosc.org/images/python-blosc2-improvements/cratios.png" style="width: 50%;"&gt;
&lt;p&gt;As can be seen, different codecs offer different compression ratios for the different distributions.  Note in particular how linear distributions (arange for int64 and linspace for float64) can reach really high compression ratios (very low entropy).&lt;/p&gt;
&lt;p&gt;Let's see the speed for compression / decompression; in order to not show too many info in this blog, we will show just the plots for the linspace linear distribution:&lt;/p&gt;
&lt;img alt="Compression speed for different codecs" src="https://blosc.org/images/python-blosc2-improvements/linspace-compress.png" style="width: 45%;"&gt;
&lt;img alt="Decompression speed for different codecs" src="https://blosc.org/images/python-blosc2-improvements/linspace-decompress.png" style="width: 45%;"&gt;
&lt;p&gt;Here we can see that the pair &lt;cite&gt;pack_array2() / unpack_array2()&lt;/cite&gt; is consistently (much) faster than their previous version &lt;cite&gt;pack_array() / unpack_array()&lt;/cite&gt;. Despite that, the fastest is the &lt;cite&gt;compress() / decompress()&lt;/cite&gt; pair; however this is not serializing all the properties of a NumPy array, and has the limitation of not being able to compress data larger than 2 GB.&lt;/p&gt;
&lt;p&gt;You can test the speed in your box by running the &lt;a class="reference external" href="https://github.com/Blosc/python-blosc2/blob/main/bench/pack_compress.py"&gt;pack_compress bench&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Also, if you would like to store the contiguous buffer on-disk, you can directly use the pair of functions &lt;a class="reference external" href="https://www.blosc.org/python-blosc2/reference/autofiles/top_level/blosc2.save_array.html#blosc2.save_array"&gt;save_array()&lt;/a&gt;, &lt;a class="reference external" href="https://www.blosc.org/python-blosc2/reference/autofiles/top_level/blosc2.save_array.html#blosc2.load_array"&gt;load_array()&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="native-performance-on-apple-m1-processors"&gt;
&lt;h2&gt;Native performance on Apple M1 processors&lt;/h2&gt;
&lt;p&gt;Contrariliy to Blosc1, Blosc2 comes with native support for ARM processors (it leverages the NEON SIMD instruction set there), and that means that it runs very fast in this architecture.  As an example, let's see how the new &lt;cite&gt;pack_array2() / unpack_array2()&lt;/cite&gt; works in an Apple M1 laptop (Macbook Air).&lt;/p&gt;
&lt;img alt="Compression speed for different codecs" src="https://blosc.org/images/python-blosc2-improvements/M1-i386-vs-arm64-pack.png" style="width: 45%;"&gt;
&lt;img alt="Decompression speed for different codecs" src="https://blosc.org/images/python-blosc2-improvements/M1-i386-vs-arm64-unpack.png" style="width: 45%;"&gt;
&lt;p&gt;As can be seen, running Blosc2 in native arm64 mode on M1 offers quite a bit more performance (specially during compression) than using the i386 emulation.  If speed is important to you, and you have a M1/M2 processor, make sure that you are running Blosc2 in native mode (arm64).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conclusions"&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;The new features added to python-blosc2 offer an easy way of creating, getting, setting and expanding data by using a &lt;cite&gt;SChunk&lt;/cite&gt; instance. Furthermore, you can get a contiguous compressed representation (aka &lt;a class="reference external" href="https://github.com/Blosc/c-blosc2/blob/main/README_CFRAME_FORMAT.rst"&gt;cframe&lt;/a&gt;) of it and re-create it again latter. And you can do the same with NumPy arrays (either in-memory or on-disk) faster than with the former functions, and even faster than a plain &lt;cite&gt;memcpy()&lt;/cite&gt;.&lt;/p&gt;
&lt;p&gt;For more info on how to use these useful new features, see this &lt;a class="reference external" href="https://github.com/Blosc/python-blosc2/blob/main/examples/slicing_and_beyond.ipynb"&gt;Jupyter notebook tutorial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, the complete documentation is at: &lt;a class="reference external" href="https://www.blosc.org/python-blosc2/python-blosc2.html"&gt;https://www.blosc.org/python-blosc2/python-blosc2.html&lt;/a&gt;.  Thanks to Marc Garcia (&lt;cite&gt;@datapythonista&lt;/cite&gt;) for his fine work and enthusiasm in helping us in providing a better structure to the Blosc documentation!&lt;/p&gt;
&lt;p&gt;This work has been possible thanks to a Small Development Grant from &lt;a class="reference external" href="https://numfocus.org"&gt;NumFOCUS&lt;/a&gt;.
NumFOCUS is a non-profit organization supporting open code for better science.  If you like the goal, consider giving a donation to NumFOCUS (you can optionally make it go to our project too, to which we would be very grateful indeed :-).&lt;/p&gt;
&lt;/section&gt;</description><category>blosc2 features performance</category><guid>https://blosc.org/posts/python-blosc2-improvements/</guid><pubDate>Thu, 06 Oct 2022 10:32:20 GMT</pubDate></item></channel></rss>