Skip to content

Commit f26cd85

Browse files
authored
Merge pull request #153 from pycompression/gzip_threaded
Threaded gzip reading and writing
2 parents 1a9462c + aa0e66c commit f26cd85

File tree

11 files changed

+723
-135
lines changed

11 files changed

+723
-135
lines changed

CHANGELOG.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ Changelog
99
1010
version 1.4.0-dev
1111
-----------------
12+
+ Added an experimental ``isal.igzip_threaded`` module which has an
13+
``open`` function.
14+
This can be used to read and write large files in a streaming fashion
15+
while escaping the GIL.
1216
+ The internal ``igzip._IGzipReader`` has been rewritten in C. As a result the
1317
overhead of decompressing files has significantly been reduced and
1418
``python -m isal.igzip`` is now very close to the C ``igzip`` application.

README.rst

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,17 @@ Acceleration Library (ISA-L) implements several key algorithms in `assembly
4545
language <https://en.wikipedia.org/wiki/Assembly_language>`_. This includes
4646
a variety of functions to provide zlib/gzip-compatible compression.
4747

48-
``python-isal`` provides the bindings by offering three modules:
48+
``python-isal`` provides the bindings by offering four modules:
4949

5050
+ ``isal_zlib``: A drop-in replacement for the zlib module that uses ISA-L to
5151
accelerate its performance.
5252
+ ``igzip``: A drop-in replacement for the gzip module that uses ``isal_zlib``
5353
instead of ``zlib`` to perform its compression and checksum tasks, which
5454
improves performance.
55+
+ ``igzip_threaded`` offers an ``open`` function which returns buffered read
56+
or write streams that can be used to read and write large files while
57+
escaping the GIL using one or multiple threads. This functionality only
58+
works for streaming, seeking is not supported.
5559
+ ``igzip_lib``: Provides compression functions which have full access to the
5660
API of ISA-L's compression functions.
5761

@@ -185,6 +189,15 @@ This project builds upon the software and experience of many. Many thanks to:
185189
<https://github.com/pycompression/xopen>`_ and by extension `cutadapt
186190
<https://github.com/marcelm/cutadapt>`_ projects. This gave python-isal its
187191
first users who used python-isal in production.
192+
+ Mark Adler (@madler) for the excellent comments in his pigz code which made
193+
it very easy to replicate the behaviour for writing gzip with multiple
194+
threads using the ``threading`` and ``isal_zlib`` modules. Another thanks
195+
for his permissive license, which allowed the crc32_combine code to be
196+
included in the project. (ISA-L does not provide a crc32_combine function,
197+
unlike zlib.) And yet another thanks to Mark Adler and also for
198+
Jean-loup Gailly for creating the gzip format which is very heavily used
199+
in bioinformatics. Without that, I would have never written this library
200+
from which I have learned so much.
188201
+ The `github actions team <https://github.com/orgs/actions/people>`_ for
189202
creating the actions CI service that enables building and testing on all
190203
three major operating systems.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import sys
2+
3+
from isal import igzip_threaded
4+
5+
with igzip_threaded.open(sys.argv[1], "rb") as gzip_file:
6+
while True:
7+
block = gzip_file.read(128 * 1024)
8+
if not block:
9+
break

docs/index.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,13 @@ API-documentation: igzip
124124
:members:
125125
:special-members: __init__
126126

127+
=================================
128+
API-documentation: igzip_threaded
129+
=================================
130+
131+
.. automodule:: isal.igzip_threaded
132+
:members: open
133+
127134
============================
128135
API Documentation: igzip_lib
129136
============================

src/isal/crc32_combine.h

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/* pigz.c -- parallel implementation of gzip
2+
* Copyright (C) 2007-2023 Mark Adler
3+
* Version 2.8 19 Aug 2023 Mark Adler
4+
*/
5+
6+
/*
7+
This software is provided 'as-is', without any express or implied
8+
warranty. In no event will the author be held liable for any damages
9+
arising from the use of this software.
10+
11+
Permission is granted to anyone to use this software for any purpose,
12+
including commercial applications, and to alter it and redistribute it
13+
freely, subject to the following restrictions:
14+
15+
1. The origin of this software must not be misrepresented; you must not
16+
claim that you wrote the original software. If you use this software
17+
in a product, an acknowledgment in the product documentation would be
18+
appreciated but is not required.
19+
2. Altered source versions must be plainly marked as such, and must not be
20+
misrepresented as being the original software.
21+
3. This notice may not be removed or altered from any source distribution.
22+
23+
Mark Adler
24+
25+
26+
*/
27+
28+
/*
29+
Alterations from original:
30+
- typedef for crc_t
31+
- local declarations replaced with static inline
32+
- g.block selector in crc32_comb removed
33+
*/
34+
35+
#include <stdint.h>
36+
#include <stddef.h>
37+
38+
typedef uint32_t crc_t;
39+
40+
// CRC-32 polynomial, reflected.
41+
#define POLY 0xedb88320
42+
43+
// Return a(x) multiplied by b(x) modulo p(x), where p(x) is the CRC
44+
// polynomial, reflected. For speed, this requires that a not be zero.
45+
static inline crc_t multmodp(crc_t a, crc_t b) {
46+
crc_t m = (crc_t)1 << 31;
47+
crc_t p = 0;
48+
for (;;) {
49+
if (a & m) {
50+
p ^= b;
51+
if ((a & (m - 1)) == 0)
52+
break;
53+
}
54+
m >>= 1;
55+
b = b & 1 ? (b >> 1) ^ POLY : b >> 1;
56+
}
57+
return p;
58+
}
59+
60+
// Table of x^2^n modulo p(x).
61+
static const crc_t x2n_table[] = {
62+
0x40000000, 0x20000000, 0x08000000, 0x00800000, 0x00008000,
63+
0xedb88320, 0xb1e6b092, 0xa06a2517, 0xed627dae, 0x88d14467,
64+
0xd7bbfe6a, 0xec447f11, 0x8e7ea170, 0x6427800e, 0x4d47bae0,
65+
0x09fe548f, 0x83852d0f, 0x30362f1a, 0x7b5a9cc3, 0x31fec169,
66+
0x9fec022a, 0x6c8dedc4, 0x15d6874d, 0x5fde7a4e, 0xbad90e37,
67+
0x2e4e5eef, 0x4eaba214, 0xa8a472c0, 0x429a969e, 0x148d302a,
68+
0xc40ba6d0, 0xc4e22c3c};
69+
70+
// Return x^(n*2^k) modulo p(x).
71+
static inline crc_t x2nmodp(size_t n, unsigned k) {
72+
crc_t p = (crc_t)1 << 31; // x^0 == 1
73+
while (n) {
74+
if (n & 1)
75+
p = multmodp(x2n_table[k & 31], p);
76+
n >>= 1;
77+
k++;
78+
}
79+
return p;
80+
}
81+
82+
// This uses the pre-computed g.shift value most of the time. Only the last
83+
// combination requires a new x2nmodp() calculation.
84+
static inline unsigned long crc32_comb(unsigned long crc1, unsigned long crc2,
85+
size_t len2) {
86+
return multmodp(x2nmodp(len2, 3), crc1) ^ crc2;
87+
}

0 commit comments

Comments
 (0)