Skip to content

bitSplit: vectorize the generic kernels used by BITSPLIT_TOP8 (#1200) - #1200

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

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

Conversation

@Cyan4973

@Cyan4973 Cyan4973 commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary:

X-link: #1200

The widths BITSPLIT_TOP8 picks match none of the 4 float patterns, so they ran in the generic kernels, one element at a time through a jump table per field. These kernels now process one field at a time over blocks of 2048 elements, so each loop vectorizes. The top-bits check and the top8 scan vectorize too. The wire format is unchanged.

Kernels, GB/s, in cache (clang, AVX2):

layout encode decode
u32 {24,8} 1.4 -> 26.8 1.6 -> 30.8
u32 {23,8} 1.4 -> 25.5 1.6 -> 30.0
u16 {8,8} 0.5 -> 14.8 0.7 -> 21.2
u64 {56,8} 2.4 -> 23.3 2.4 -> 26.4
u8 {5} 0.5 -> 15.7 0.6 -> 36.9
scan GB/s
top-bits check, u32 8.8 -> 79
top8 scan, u16 14 -> 116

End to end, top8 then FSE, on 964 float32 files (compression / decompression, MB/s):

files before after FLOAT32_DECONSTRUCT size vs deconstruct
all >= 0, max >= 2: {23,8} (415) 439 / 690 662 / 1113 703 / 1073 -0.40%
all >= 0, max < 2: {22,8} (141) 434 / 682 635 / 1078 656 / 1000 +0.06%
signed: {24,8} (408) 514 / 768 757 / 1285 755 / 1123 +0.05%

Trade-offs:

  • The 4 float patterns keep their specialized kernels, which stay faster in cache.
  • gcc vectorizes these loops only at -O3. At -O2, {24,8} only reaches 5.2 / 5.6 GB/s.

Reviewed By: kevinjzhang

Differential Revision: D127402945

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

meta-codesync Bot commented Oct 26, 2026

Copy link
Copy Markdown

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

Cyan4973 added a commit to Cyan4973/openzl that referenced this pull request Nov 3, 2026
Summary:
X-link: meta#1200

The widths `BITSPLIT_TOP8` picks match none of the 4 float patterns, so they ran in the generic kernels, one element at a time through a jump table per field. These kernels now process one field at a time over blocks of 2048 elements, so each loop vectorizes. The top-bits check and the top8 max scan vectorize too. The wire format is unchanged.

Kernels, GB/s, in cache (clang, AVX2):

| layout | encode | decode |
|---|---|---|
| u32 {24,8} | 1.4 -> 26.8 | 1.6 -> 30.8 |
| u32 {23,8} | 1.4 -> 25.5 | 1.6 -> 30.0 |
| u16 {8,8} | 0.5 -> 14.8 | 0.7 -> 21.2 |
| u64 {56,8} | 2.4 -> 23.3 | 2.4 -> 26.4 |
| u8 {5} | 0.5 -> 15.7 | 0.6 -> 36.9 |

| scan | GB/s |
|---|---|
| top-bits check, u32 | 8.8 -> 79 |
| top8 max, u16 | 14 -> 116 |

End to end, top8 then FSE, on 964 float32 files (compression / decompression, MB/s):

| files | before | after | `FLOAT32_DECONSTRUCT` | size vs deconstruct |
|---|---|---|---|---|
| all >= 0, max >= 2: {23,8} (415) | 439 / 690 | 662 / 1113 | 703 / 1073 | -0.40% |
| all >= 0, max < 2: {22,8} (141) | 434 / 682 | 635 / 1078 | 656 / 1000 | +0.06% |
| signed: {24,8} (408) | 514 / 768 | 757 / 1285 | 755 / 1123 | +0.05% |

Trade-offs:
- The 4 float patterns keep their specialized kernels, which stay faster in cache.
- gcc vectorizes these loops only at -O3. At -O2, {24,8} only reaches 5.2 / 5.6 GB/s.

Reviewed By: kevinjzhang

Differential Revision: D127402945
)

Summary:

X-link: meta#1200

The widths `BITSPLIT_TOP8` picks match none of the 4 float patterns, so they ran in the generic kernels, one element at a time through a jump table per field. These kernels now process one field at a time over blocks of 2048 elements, so each loop vectorizes. The top-bits check and the top8 scan vectorize too. The wire format is unchanged.

Kernels, GB/s, in cache (clang, AVX2):

| layout | encode | decode |
|---|---|---|
| u32 {24,8} | 1.4 -> 26.8 | 1.6 -> 30.8 |
| u32 {23,8} | 1.4 -> 25.5 | 1.6 -> 30.0 |
| u16 {8,8} | 0.5 -> 14.8 | 0.7 -> 21.2 |
| u64 {56,8} | 2.4 -> 23.3 | 2.4 -> 26.4 |
| u8 {5} | 0.5 -> 15.7 | 0.6 -> 36.9 |

| scan | GB/s |
|---|---|
| top-bits check, u32 | 8.8 -> 79 |
| top8 scan, u16 | 14 -> 116 |

End to end, top8 then FSE, on 964 float32 files (compression / decompression, MB/s):

| files | before | after | `FLOAT32_DECONSTRUCT` | size vs deconstruct |
|---|---|---|---|---|
| all >= 0, max >= 2: {23,8} (415) | 439 / 690 | 662 / 1113 | 703 / 1073 | -0.40% |
| all >= 0, max < 2: {22,8} (141) | 434 / 682 | 635 / 1078 | 656 / 1000 | +0.06% |
| signed: {24,8} (408) | 514 / 768 | 757 / 1285 | 755 / 1123 | +0.05% |

Trade-offs:
- The 4 float patterns keep their specialized kernels, which stay faster in cache.
- gcc vectorizes these loops only at -O3. At -O2, {24,8} only reaches 5.2 / 5.6 GB/s.

Reviewed By: kevinjzhang

Differential Revision: D127402945
@meta-codesync meta-codesync Bot changed the title bitSplit: vectorize the generic kernels used by BITSPLIT_TOP8 bitSplit: vectorize the generic kernels used by BITSPLIT_TOP8 (#1200) Nov 4, 2026
@meta-codesync meta-codesync Bot closed this in 8724633 Nov 4, 2026
@meta-codesync meta-codesync Bot added the Merged label Nov 4, 2026
@meta-codesync

meta-codesync Bot commented Nov 4, 2026

Copy link
Copy Markdown

This pull request has been merged in 8724633.

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