Skip to content

Add a NumPy parser and CLI profile (#1211) - #1211

Closed
Cyan4973 wants to merge 1 commit into
meta:devfrom
Cyan4973:export-D127896258
Closed

Cyan4973 wants to merge 1 commit into
meta:devfrom
Cyan4973:export-D127896258

Conversation

@Cyan4973

@Cyan4973 Cyan4973 commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary:

X-link: #1211

NumPy files hold the arrays of many scientific datasets and some model checkpoints, such as the JAX checkpoints of ViT and GenCast. A .npy file holds one array, and a .npz archive is a zip of .npy members. OpenZL had no profile for them, so they went through generic compression, which does no better than zstd on them.

This diff adds a numpy CLI profile and ZL_NumPy_registerGraph().

  • A small lexer reads the header of each .npy file, or of each stored .npy member of a .npz archive (through zip_lexer), to find the type and shape of its array.
  • Arrays of numbers (booleans, integers, floats, complex numbers, datetimes) go to the Numeric Transformer, which picks the transforms for each array. Other arrays, headers and zip records are compressed apart.
  • Chunking reuses the tensor chunker of the model parsers.
File zstd -19 -p serial -p numpy vs zstd -19
ViT-B/16 JAX checkpoint, float32 .npz (413 MB) x1.289 x1.286 x1.427 +10.7%
GenCast JAX checkpoint, float32 .npz (230 MB) x1.075 x1.073 x1.190 +10.7%
rMD17 aspirin trajectories, float64 .npz (154 MB) x1.168 x1.048 x1.589 +36.0%
rMD17 benzene trajectories, float64 .npz (89 MB) x1.607 x1.348 x2.533 +57.7%
Neuropixels templates, float32 .npy (15 MB) x11.25 x10.30 x13.77 +22.4%
QuickDraw cat bitmaps, uint8 .npy (97 MB) x2.699 x2.460 x2.784 +3.2%

Trade-offs:

  • The Numeric Transformer compresses at 25-50 MB/s on these files; decompression runs at 0.3-2.5 GB/s. On the float32 weights of ViT, the tensor graphs of the model parsers compress 14 to 18 times faster, for a ratio within 1%. Speeding up the Transformer is a separate effort, so the model parsers keep their tuned graphs.
  • Members deflated by np.savez_compressed (MedMNIST, Keras MNIST) are not inflated, so they gain nothing.
  • In a .npz archive, arrays smaller than 16 KiB share a stream with the headers around them, which the generic graph compresses: 0.1-0.2% of the bytes of ViT and GenCast.
  • Strings, objects, structured types and big-endian arrays are compressed as raw bytes.

Differential Revision: D127896258

@meta-cla meta-cla Bot added the cla signed label Oct 28, 2026
@meta-codesync

meta-codesync Bot commented Oct 28, 2026

Copy link
Copy Markdown

@Cyan4973 has exported this pull request. If you are a Meta employee, you can view the originating Diff in D127896258.

@Cyan4973 Cyan4973 self-assigned this Oct 28, 2026
@meta-codesync meta-codesync Bot changed the title Add a NumPy parser and CLI profile Add a NumPy parser and CLI profile (#1211) Oct 28, 2026
Cyan4973 added a commit to Cyan4973/openzl that referenced this pull request Oct 28, 2026
Summary:

NumPy files hold the arrays of many scientific datasets and some model checkpoints, such as the JAX checkpoints of ViT and GenCast. A `.npy` file holds one array, and a `.npz` archive is a zip of `.npy` members. OpenZL had no profile for them, so they went through generic compression, which does no better than zstd on them.

This diff adds a `numpy` CLI profile and `ZL_NumPy_registerGraph()`.
- A small lexer reads the header of each `.npy` file, or of each stored `.npy` member of a `.npz` archive (through `zip_lexer`), to find the type and shape of its array.
- Arrays of numbers (booleans, integers, floats, complex numbers, datetimes) go to the Numeric Transformer, which picks the transforms for each array. Other arrays, headers and zip records are compressed apart.
- Chunking reuses the tensor chunker of the model parsers.

| File | zstd -19 | `-p serial` | `-p numpy` | vs zstd -19 |
|---|---|---|---|---|
| ViT-B/16 JAX checkpoint, float32 `.npz` (413 MB) | x1.289 | x1.286 | **x1.427** | +10.7% |
| GenCast JAX checkpoint, float32 `.npz` (230 MB) | x1.075 | x1.073 | **x1.190** | +10.7% |
| rMD17 aspirin trajectories, float64 `.npz` (154 MB) | x1.168 | x1.048 | **x1.589** | +36.0% |
| rMD17 benzene trajectories, float64 `.npz` (89 MB) | x1.607 | x1.348 | **x2.533** | +57.7% |
| Neuropixels templates, float32 `.npy` (15 MB) | x11.25 | x10.30 | **x13.77** | +22.4% |
| QuickDraw cat bitmaps, uint8 `.npy` (97 MB) | x2.699 | x2.460 | **x2.784** | +3.2% |

Trade-offs:
- The Numeric Transformer compresses at 25-50 MB/s on these files; decompression runs at 0.3-2.5 GB/s. On the float32 weights of ViT, the tensor graphs of the model parsers compress 14 to 18 times faster, for a ratio within 1%. Speeding up the Transformer is a separate effort, so the model parsers keep their tuned graphs.
- Members deflated by `np.savez_compressed` (MedMNIST, Keras MNIST) are not inflated, so they gain nothing.
- In a `.npz` archive, arrays smaller than 16 KiB share a stream with the headers around them, which the generic graph compresses: 0.1-0.2% of the bytes of ViT and GenCast.
- Strings, objects, structured types and big-endian arrays are compressed as raw bytes.

Differential Revision: D127896258
@meta-codesync meta-codesync Bot changed the title Add a NumPy parser and CLI profile (#1211) Add a NumPy parser and CLI profile Oct 29, 2026
Cyan4973 added a commit to Cyan4973/openzl that referenced this pull request Oct 29, 2026
Summary:
X-link: meta#1211

NumPy files hold the arrays of many scientific datasets and some model checkpoints, such as the JAX checkpoints of ViT and GenCast. A `.npy` file holds one array, and a `.npz` archive is a zip of `.npy` members. OpenZL had no profile for them, so they went through generic compression, which does no better than zstd on them.

This diff adds a `numpy` CLI profile and `ZL_NumPy_registerGraph()`.
- A small lexer reads the header of each `.npy` file, or of each stored `.npy` member of a `.npz` archive (through `zip_lexer`), to find the type and shape of its array.
- Arrays of numbers (booleans, integers, floats, complex numbers, datetimes) go to the Numeric Transformer, which picks the transforms for each array. Other arrays, headers and zip records are compressed apart.
- Chunking reuses the tensor chunker of the model parsers.

| File | zstd -19 | `-p serial` | `-p numpy` | vs zstd -19 |
|---|---|---|---|---|
| ViT-B/16 JAX checkpoint, float32 `.npz` (413 MB) | x1.289 | x1.286 | **x1.427** | +10.7% |
| GenCast JAX checkpoint, float32 `.npz` (230 MB) | x1.075 | x1.073 | **x1.190** | +10.7% |
| rMD17 aspirin trajectories, float64 `.npz` (154 MB) | x1.168 | x1.048 | **x1.589** | +36.0% |
| rMD17 benzene trajectories, float64 `.npz` (89 MB) | x1.607 | x1.348 | **x2.533** | +57.7% |
| Neuropixels templates, float32 `.npy` (15 MB) | x11.25 | x10.30 | **x13.77** | +22.4% |
| QuickDraw cat bitmaps, uint8 `.npy` (97 MB) | x2.699 | x2.460 | **x2.784** | +3.2% |

Trade-offs:
- The Numeric Transformer compresses at 25-50 MB/s on these files; decompression runs at 0.3-2.5 GB/s. On the float32 weights of ViT, the tensor graphs of the model parsers compress 14 to 18 times faster, for a ratio within 1%. Speeding up the Transformer is a separate effort, so the model parsers keep their tuned graphs.
- Members deflated by `np.savez_compressed` (MedMNIST, Keras MNIST) are not inflated, so they gain nothing.
- In a `.npz` archive, arrays smaller than 16 KiB share a stream with the headers around them, which the generic graph compresses: 0.1-0.2% of the bytes of ViT and GenCast.
- Strings, objects, structured types and big-endian arrays are compressed as raw bytes.

Differential Revision: D127896258
@meta-codesync meta-codesync Bot changed the title Add a NumPy parser and CLI profile Add a NumPy parser and CLI profile (#1211) Nov 2, 2026
Cyan4973 added a commit to Cyan4973/openzl that referenced this pull request Nov 2, 2026
Summary:

X-link: meta#1211

NumPy files hold the arrays of many scientific datasets and some model checkpoints, such as the JAX checkpoints of ViT and GenCast. A `.npy` file holds one array, and a `.npz` archive is a zip of `.npy` members. OpenZL had no profile for them, so they went through generic compression, which does no better than zstd on them.

This diff adds a `numpy` CLI profile and `ZL_NumPy_registerGraph()`.
- A small lexer reads the header of each `.npy` file, or of each stored `.npy` member of a `.npz` archive (through `zip_lexer`), to find the type and shape of its array.
- Arrays of numbers (booleans, integers, floats, complex numbers, datetimes) go to the Numeric Transformer, which picks the transforms for each array. Other arrays, headers and zip records are compressed apart.
- Chunking reuses the tensor chunker of the model parsers.

| File | zstd -19 | `-p serial` | `-p numpy` | vs zstd -19 |
|---|---|---|---|---|
| ViT-B/16 JAX checkpoint, float32 `.npz` (413 MB) | x1.289 | x1.286 | **x1.427** | +10.7% |
| GenCast JAX checkpoint, float32 `.npz` (230 MB) | x1.075 | x1.073 | **x1.190** | +10.7% |
| rMD17 aspirin trajectories, float64 `.npz` (154 MB) | x1.168 | x1.048 | **x1.589** | +36.0% |
| rMD17 benzene trajectories, float64 `.npz` (89 MB) | x1.607 | x1.348 | **x2.533** | +57.7% |
| Neuropixels templates, float32 `.npy` (15 MB) | x11.25 | x10.30 | **x13.77** | +22.4% |
| QuickDraw cat bitmaps, uint8 `.npy` (97 MB) | x2.699 | x2.460 | **x2.784** | +3.2% |

Trade-offs:
- The Numeric Transformer compresses at 25-50 MB/s on these files; decompression runs at 0.3-2.5 GB/s. On the float32 weights of ViT, the tensor graphs of the model parsers compress 14 to 18 times faster, for a ratio within 1%. Speeding up the Transformer is a separate effort, so the model parsers keep their tuned graphs.
- Members deflated by `np.savez_compressed` (MedMNIST, Keras MNIST) are not inflated, so they gain nothing.
- In a `.npz` archive, arrays smaller than 16 KiB share a stream with the headers around them, which the generic graph compresses: 0.1-0.2% of the bytes of ViT and GenCast.
- Strings, objects, structured types and big-endian arrays are compressed as raw bytes.

Differential Revision: D127896258
Summary:

X-link: meta#1211

NumPy files hold the arrays of many scientific datasets and some model checkpoints, such as the JAX checkpoints of ViT and GenCast. A `.npy` file holds one array, and a `.npz` archive is a zip of `.npy` members. OpenZL had no profile for them, so they went through generic compression, which does no better than zstd on them.

This diff adds a `numpy` CLI profile and `ZL_NumPy_registerGraph()`.
- A small lexer reads the header of each `.npy` file, or of each stored `.npy` member of a `.npz` archive (through `zip_lexer`), to find the type and shape of its array.
- Arrays of numbers (booleans, integers, floats, complex numbers, datetimes) go to the Numeric Transformer, which picks the transforms for each array. Other arrays, headers and zip records are compressed apart.
- Chunking reuses the tensor chunker of the model parsers.

| File | zstd -19 | `-p serial` | `-p numpy` | vs zstd -19 |
|---|---|---|---|---|
| ViT-B/16 JAX checkpoint, float32 `.npz` (413 MB) | x1.289 | x1.286 | **x1.427** | +10.7% |
| GenCast JAX checkpoint, float32 `.npz` (230 MB) | x1.075 | x1.073 | **x1.190** | +10.7% |
| rMD17 aspirin trajectories, float64 `.npz` (154 MB) | x1.168 | x1.048 | **x1.589** | +36.0% |
| rMD17 benzene trajectories, float64 `.npz` (89 MB) | x1.607 | x1.348 | **x2.533** | +57.7% |
| Neuropixels templates, float32 `.npy` (15 MB) | x11.25 | x10.30 | **x13.77** | +22.4% |
| QuickDraw cat bitmaps, uint8 `.npy` (97 MB) | x2.699 | x2.460 | **x2.784** | +3.2% |

Trade-offs:
- The Numeric Transformer compresses at 25-50 MB/s on these files; decompression runs at 0.3-2.5 GB/s. On the float32 weights of ViT, the tensor graphs of the model parsers compress 14 to 18 times faster, for a ratio within 1%. Speeding up the Transformer is a separate effort, so the model parsers keep their tuned graphs.
- Members deflated by `np.savez_compressed` (MedMNIST, Keras MNIST) are not inflated, so they gain nothing.
- In a `.npz` archive, arrays smaller than 16 KiB share a stream with the headers around them, which the generic graph compresses: 0.1-0.2% of the bytes of ViT and GenCast.
- Strings, objects, structured types and big-endian arrays are compressed as raw bytes.

Differential Revision: D127896258
@meta-codesync

meta-codesync Bot commented Nov 4, 2026

Copy link
Copy Markdown

This pull request has been merged in f83c3b2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant