Skip to content

Conversation

@danking
Copy link
Contributor

@danking danking commented Jan 14, 2025

The patches are now always non-nullable.

This required PrimitiveArray::patch to gracefully handle non-nullable patches when the array is nullable.

I modified the benchmarks to include patch manipulation time, but notice that the test data has no patches. The benchmarks measure the overhead of is_valid. If we had test data where the invalid positions contained exceptional values, I would expect a modest improvement in both decompression and compression time.

As discussed in slack, the is_valid is expensive for two reasons: (a) creation of a Scalar and (b) conversion to an Arrow BooleanBuffer. @gatesn is working on a FilterMask change that should subsume all our boolean compression strategies into a new "BoolMask". With that in place, we can require Validity::Array to hold a "BoolMask" and thus check validity without either of the issues above.

This PR

The parameters are: (number_of_array_elements, fraction_valid).

Timer precision: 41 ns
alp_compress               fastest       │ slowest       │ median        │ mean          │ samples │ iters
├─ compress_alp                          │               │               │               │         │
│  ├─ f32                                │               │               │               │         │
│  │  ├─ (100000, 0.25)    170.2 µs      │ 442.1 µs      │ 170.7 µs      │ 173.6 µs      │ 100     │ 100
│  │  ├─ (100000, 0.95)    169.3 µs      │ 202.2 µs      │ 170.4 µs      │ 171.7 µs      │ 100     │ 100
│  │  ├─ (100000, 1.0)     136.3 µs      │ 147.1 µs      │ 136.9 µs      │ 137.2 µs      │ 100     │ 100
│  │  ├─ (10000000, 0.25)  13.6 ms       │ 16.46 ms      │ 14.39 ms      │ 14.23 ms      │ 100     │ 100
│  │  ├─ (10000000, 0.95)  13.64 ms      │ 17.7 ms       │ 14.44 ms      │ 14.38 ms      │ 100     │ 100
│  │  ╰─ (10000000, 1.0)   13.55 ms      │ 14.97 ms      │ 14.35 ms      │ 14.23 ms      │ 100     │ 100
│  ╰─ f64                                │               │               │               │         │
│     ├─ (100000, 0.25)    240.7 µs      │ 385.7 µs      │ 247.8 µs      │ 249.1 µs      │ 100     │ 100
│     ├─ (100000, 0.95)    240.2 µs      │ 253.2 µs      │ 243.5 µs      │ 244.4 µs      │ 100     │ 100
│     ├─ (100000, 1.0)     172.6 µs      │ 184.4 µs      │ 175.2 µs      │ 175.5 µs      │ 100     │ 100
│     ├─ (10000000, 0.25)  15.95 ms      │ 24.24 ms      │ 16.61 ms      │ 17.25 ms      │ 100     │ 100
│     ├─ (10000000, 0.95)  15.95 ms      │ 21.34 ms      │ 16.39 ms      │ 16.85 ms      │ 100     │ 100
│     ╰─ (10000000, 1.0)   15.92 ms      │ 23.41 ms      │ 16.46 ms      │ 17.04 ms      │ 100     │ 100
╰─ decompress_alp                        │               │               │               │         │
   ├─ f32                                │               │               │               │         │
   │  ├─ (100000, 0.25)    12.2 µs       │ 34.7 µs       │ 12.29 µs      │ 12.52 µs      │ 100     │ 100
   │  ├─ (100000, 0.95)    12.12 µs      │ 12.74 µs      │ 12.35 µs      │ 12.37 µs      │ 100     │ 100
   │  ├─ (100000, 1.0)     12.16 µs      │ 12.95 µs      │ 12.37 µs      │ 12.4 µs       │ 100     │ 100
   │  ├─ (10000000, 0.25)  2.117 ms      │ 4.544 ms      │ 2.637 ms      │ 2.674 ms      │ 100     │ 100
   │  ├─ (10000000, 0.95)  2.085 ms      │ 4.458 ms      │ 2.362 ms      │ 2.504 ms      │ 100     │ 100
   │  ╰─ (10000000, 1.0)   2.097 ms      │ 3.875 ms      │ 2.229 ms      │ 2.338 ms      │ 100     │ 100
   ╰─ f64                                │               │               │               │         │
      ├─ (100000, 0.25)    23.41 µs      │ 25.16 µs      │ 23.66 µs      │ 23.68 µs      │ 100     │ 100
      ├─ (100000, 0.95)    23.2 µs       │ 24.7 µs       │ 24.06 µs      │ 24.05 µs      │ 100     │ 100
      ├─ (100000, 1.0)     22.79 µs      │ 26.08 µs      │ 22.91 µs      │ 22.95 µs      │ 100     │ 100
      ├─ (10000000, 0.25)  4.216 ms      │ 6.862 ms      │ 4.416 ms      │ 4.568 ms      │ 100     │ 100
      ├─ (10000000, 0.95)  4.242 ms      │ 7.647 ms      │ 4.59 ms       │ 4.827 ms      │ 100     │ 100
      ╰─ (10000000, 1.0)   4.236 ms      │ 8.129 ms      │ 4.377 ms      │ 4.507 ms      │ 100     │ 100

Develop

I patched develop with this PR's benchmark code

Timer precision: 41 ns
alp_compress               fastest       │ slowest       │ median        │ mean          │ samples │ iters
├─ compress_alp                          │               │               │               │         │
│  ├─ f32                                │               │               │               │         │
│  │  ├─ (100000, 0.25)    136 µs        │ 279.7 µs      │ 136.8 µs      │ 138.5 µs      │ 100     │ 100
│  │  ├─ (100000, 0.95)    136.2 µs      │ 149.7 µs      │ 136.9 µs      │ 137.6 µs      │ 100     │ 100
│  │  ├─ (100000, 1.0)     136 µs        │ 148.5 µs      │ 136.6 µs      │ 137.1 µs      │ 100     │ 100
│  │  ├─ (10000000, 0.25)  13.68 ms      │ 15.06 ms      │ 14.07 ms      │ 14.14 ms      │ 100     │ 100
│  │  ├─ (10000000, 0.95)  13.67 ms      │ 19.4 ms       │ 14.05 ms      │ 14.18 ms      │ 100     │ 100
│  │  ╰─ (10000000, 1.0)   13.66 ms      │ 14.73 ms      │ 13.87 ms      │ 14.04 ms      │ 100     │ 100
│  ╰─ f64                                │               │               │               │         │
│     ├─ (100000, 0.25)    167.7 µs      │ 301.4 µs      │ 172.7 µs      │ 173.2 µs      │ 100     │ 100
│     ├─ (100000, 0.95)    167.7 µs      │ 187.7 µs      │ 170.7 µs      │ 171.1 µs      │ 100     │ 100
│     ├─ (100000, 1.0)     167.6 µs      │ 183.7 µs      │ 170.9 µs      │ 171.1 µs      │ 100     │ 100
│     ├─ (10000000, 0.25)  15.54 ms      │ 21.9 ms       │ 15.92 ms      │ 16.05 ms      │ 100     │ 100
│     ├─ (10000000, 0.95)  15.61 ms      │ 16.74 ms      │ 15.97 ms      │ 16.03 ms      │ 100     │ 100
│     ╰─ (10000000, 1.0)   15.64 ms      │ 18.85 ms      │ 16.1 ms       │ 16.23 ms      │ 100     │ 100
╰─ decompress_alp                        │               │               │               │         │
   ├─ f32                                │               │               │               │         │
   │  ├─ (100000, 0.25)    12.37 µs      │ 85.49 µs      │ 12.49 µs      │ 13.22 µs      │ 100     │ 100
   │  ├─ (100000, 0.95)    12.29 µs      │ 12.74 µs      │ 12.43 µs      │ 12.44 µs      │ 100     │ 100
   │  ├─ (100000, 1.0)     11.7 µs       │ 11.95 µs      │ 11.83 µs      │ 11.81 µs      │ 100     │ 100
   │  ├─ (10000000, 0.25)  2.081 ms      │ 3.003 ms      │ 2.175 ms      │ 2.263 ms      │ 100     │ 100
   │  ├─ (10000000, 0.95)  2.082 ms      │ 2.228 ms      │ 2.109 ms      │ 2.124 ms      │ 100     │ 100
   │  ╰─ (10000000, 1.0)   2.082 ms      │ 2.904 ms      │ 2.13 ms       │ 2.202 ms      │ 100     │ 100
   ╰─ f64                                │               │               │               │         │
      ├─ (100000, 0.25)    23.66 µs      │ 25.66 µs      │ 23.83 µs      │ 23.86 µs      │ 100     │ 100
      ├─ (100000, 0.95)    23.16 µs      │ 24.62 µs      │ 24.04 µs      │ 23.89 µs      │ 100     │ 100
      ├─ (100000, 1.0)     22.87 µs      │ 23.49 µs      │ 23.04 µs      │ 23.03 µs      │ 100     │ 100
      ├─ (10000000, 0.25)  4.221 ms      │ 5.59 ms       │ 4.326 ms      │ 4.469 ms      │ 100     │ 100
      ├─ (10000000, 0.95)  4.242 ms      │ 5.319 ms      │ 4.545 ms      │ 4.536 ms      │ 100     │ 100
      ╰─ (10000000, 1.0)   4.228 ms      │ 5.652 ms      │ 4.342 ms      │ 4.519 ms      │ 100     │ 100

The patches are now always non-nullable.

This required PrimitiveArray::patch to gracefully handle non-nullable patches when the array is
nullable.

I modified the benchmarks to include patch manipulation time, but notice that the test data has no
patches. The benchmarks measure the overhead of `is_valid`. If we had test data where the invalid
positions contained exceptional values, I would expect a modest improvement in both decompression
and compression time.

As discussed [in slack](https://spiraldb.slack.com/archives/C07BV3GKAJ2/p1736894376100079), the
`is_valid` is expensive for two reasons: (a) creation of a Scalar and (b) conversion to an Arrow
BooleanBuffer. @gatesn is working on a FilterMask change that should subsume all our boolean
compression strategies into a new "BoolMask". With that in place, we can require Validity::Array to
hold a "BoolMask" and thus check validity without either of the issues above.

This PR
-------

```
Timer precision: 41 ns
alp_compress               fastest       │ slowest       │ median        │ mean          │ samples │ iters
├─ compress_alp                          │               │               │               │         │
│  ├─ f32                                │               │               │               │         │
│  │  ├─ (100000, 0.25)    170.2 µs      │ 442.1 µs      │ 170.7 µs      │ 173.6 µs      │ 100     │ 100
│  │  ├─ (100000, 0.95)    169.3 µs      │ 202.2 µs      │ 170.4 µs      │ 171.7 µs      │ 100     │ 100
│  │  ├─ (100000, 1.0)     136.3 µs      │ 147.1 µs      │ 136.9 µs      │ 137.2 µs      │ 100     │ 100
│  │  ├─ (10000000, 0.25)  13.6 ms       │ 16.46 ms      │ 14.39 ms      │ 14.23 ms      │ 100     │ 100
│  │  ├─ (10000000, 0.95)  13.64 ms      │ 17.7 ms       │ 14.44 ms      │ 14.38 ms      │ 100     │ 100
│  │  ╰─ (10000000, 1.0)   13.55 ms      │ 14.97 ms      │ 14.35 ms      │ 14.23 ms      │ 100     │ 100
│  ╰─ f64                                │               │               │               │         │
│     ├─ (100000, 0.25)    240.7 µs      │ 385.7 µs      │ 247.8 µs      │ 249.1 µs      │ 100     │ 100
│     ├─ (100000, 0.95)    240.2 µs      │ 253.2 µs      │ 243.5 µs      │ 244.4 µs      │ 100     │ 100
│     ├─ (100000, 1.0)     172.6 µs      │ 184.4 µs      │ 175.2 µs      │ 175.5 µs      │ 100     │ 100
│     ├─ (10000000, 0.25)  15.95 ms      │ 24.24 ms      │ 16.61 ms      │ 17.25 ms      │ 100     │ 100
│     ├─ (10000000, 0.95)  15.95 ms      │ 21.34 ms      │ 16.39 ms      │ 16.85 ms      │ 100     │ 100
│     ╰─ (10000000, 1.0)   15.92 ms      │ 23.41 ms      │ 16.46 ms      │ 17.04 ms      │ 100     │ 100
╰─ decompress_alp                        │               │               │               │         │
   ├─ f32                                │               │               │               │         │
   │  ├─ (100000, 0.25)    12.2 µs       │ 34.7 µs       │ 12.29 µs      │ 12.52 µs      │ 100     │ 100
   │  ├─ (100000, 0.95)    12.12 µs      │ 12.74 µs      │ 12.35 µs      │ 12.37 µs      │ 100     │ 100
   │  ├─ (100000, 1.0)     12.16 µs      │ 12.95 µs      │ 12.37 µs      │ 12.4 µs       │ 100     │ 100
   │  ├─ (10000000, 0.25)  2.117 ms      │ 4.544 ms      │ 2.637 ms      │ 2.674 ms      │ 100     │ 100
   │  ├─ (10000000, 0.95)  2.085 ms      │ 4.458 ms      │ 2.362 ms      │ 2.504 ms      │ 100     │ 100
   │  ╰─ (10000000, 1.0)   2.097 ms      │ 3.875 ms      │ 2.229 ms      │ 2.338 ms      │ 100     │ 100
   ╰─ f64                                │               │               │               │         │
      ├─ (100000, 0.25)    23.41 µs      │ 25.16 µs      │ 23.66 µs      │ 23.68 µs      │ 100     │ 100
      ├─ (100000, 0.95)    23.2 µs       │ 24.7 µs       │ 24.06 µs      │ 24.05 µs      │ 100     │ 100
      ├─ (100000, 1.0)     22.79 µs      │ 26.08 µs      │ 22.91 µs      │ 22.95 µs      │ 100     │ 100
      ├─ (10000000, 0.25)  4.216 ms      │ 6.862 ms      │ 4.416 ms      │ 4.568 ms      │ 100     │ 100
      ├─ (10000000, 0.95)  4.242 ms      │ 7.647 ms      │ 4.59 ms       │ 4.827 ms      │ 100     │ 100
      ╰─ (10000000, 1.0)   4.236 ms      │ 8.129 ms      │ 4.377 ms      │ 4.507 ms      │ 100     │ 100
```

Develop
-------

I patched develop with this PR's benchmark code

```
Timer precision: 41 ns
alp_compress               fastest       │ slowest       │ median        │ mean          │ samples │ iters
├─ compress_alp                          │               │               │               │         │
│  ├─ f32                                │               │               │               │         │
│  │  ├─ (100000, 0.25)    136 µs        │ 279.7 µs      │ 136.8 µs      │ 138.5 µs      │ 100     │ 100
│  │  ├─ (100000, 0.95)    136.2 µs      │ 149.7 µs      │ 136.9 µs      │ 137.6 µs      │ 100     │ 100
│  │  ├─ (100000, 1.0)     136 µs        │ 148.5 µs      │ 136.6 µs      │ 137.1 µs      │ 100     │ 100
│  │  ├─ (10000000, 0.25)  13.68 ms      │ 15.06 ms      │ 14.07 ms      │ 14.14 ms      │ 100     │ 100
│  │  ├─ (10000000, 0.95)  13.67 ms      │ 19.4 ms       │ 14.05 ms      │ 14.18 ms      │ 100     │ 100
│  │  ╰─ (10000000, 1.0)   13.66 ms      │ 14.73 ms      │ 13.87 ms      │ 14.04 ms      │ 100     │ 100
│  ╰─ f64                                │               │               │               │         │
│     ├─ (100000, 0.25)    167.7 µs      │ 301.4 µs      │ 172.7 µs      │ 173.2 µs      │ 100     │ 100
│     ├─ (100000, 0.95)    167.7 µs      │ 187.7 µs      │ 170.7 µs      │ 171.1 µs      │ 100     │ 100
│     ├─ (100000, 1.0)     167.6 µs      │ 183.7 µs      │ 170.9 µs      │ 171.1 µs      │ 100     │ 100
│     ├─ (10000000, 0.25)  15.54 ms      │ 21.9 ms       │ 15.92 ms      │ 16.05 ms      │ 100     │ 100
│     ├─ (10000000, 0.95)  15.61 ms      │ 16.74 ms      │ 15.97 ms      │ 16.03 ms      │ 100     │ 100
│     ╰─ (10000000, 1.0)   15.64 ms      │ 18.85 ms      │ 16.1 ms       │ 16.23 ms      │ 100     │ 100
╰─ decompress_alp                        │               │               │               │         │
   ├─ f32                                │               │               │               │         │
   │  ├─ (100000, 0.25)    12.37 µs      │ 85.49 µs      │ 12.49 µs      │ 13.22 µs      │ 100     │ 100
   │  ├─ (100000, 0.95)    12.29 µs      │ 12.74 µs      │ 12.43 µs      │ 12.44 µs      │ 100     │ 100
   │  ├─ (100000, 1.0)     11.7 µs       │ 11.95 µs      │ 11.83 µs      │ 11.81 µs      │ 100     │ 100
   │  ├─ (10000000, 0.25)  2.081 ms      │ 3.003 ms      │ 2.175 ms      │ 2.263 ms      │ 100     │ 100
   │  ├─ (10000000, 0.95)  2.082 ms      │ 2.228 ms      │ 2.109 ms      │ 2.124 ms      │ 100     │ 100
   │  ╰─ (10000000, 1.0)   2.082 ms      │ 2.904 ms      │ 2.13 ms       │ 2.202 ms      │ 100     │ 100
   ╰─ f64                                │               │               │               │         │
      ├─ (100000, 0.25)    23.66 µs      │ 25.66 µs      │ 23.83 µs      │ 23.86 µs      │ 100     │ 100
      ├─ (100000, 0.95)    23.16 µs      │ 24.62 µs      │ 24.04 µs      │ 23.89 µs      │ 100     │ 100
      ├─ (100000, 1.0)     22.87 µs      │ 23.49 µs      │ 23.04 µs      │ 23.03 µs      │ 100     │ 100
      ├─ (10000000, 0.25)  4.221 ms      │ 5.59 ms       │ 4.326 ms      │ 4.469 ms      │ 100     │ 100
      ├─ (10000000, 0.95)  4.242 ms      │ 5.319 ms      │ 4.545 ms      │ 4.536 ms      │ 100     │ 100
      ╰─ (10000000, 1.0)   4.228 ms      │ 5.652 ms      │ 4.342 ms      │ 4.519 ms      │ 100     │ 100
```
@danking danking marked this pull request as ready for review January 15, 2025 14:33
@danking danking requested review from gatesn and lwwmanning January 15, 2025 16:41
let mut children = Vec::with_capacity(2);
children.push(encoded);
if let Some(patches) = &patches {
if patches.dtype().is_nullable() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is the right way round. It's nice to have the patches dtype match the array dtype, and then assert that patches validity is AllValid.

}
}

fn find_best_exponents(values: &[Self], validity: &Validity) -> VortexResult<Exponents> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this take Validity?

I think we should assume, like with other non-nullable encodings, that the caller has inserted reasonable placeholder values.

For example, for BitPacking the placeholder should be zero. For RunEnd, the placeholder should be LastNonNull.

For ALP, I think the placeholder should also be zero. That way a null will never be an exception.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see three paths forward:

  1. ALPArray, at construction, zeros invalid values.
  2. ALPArray accepts arrays with invalid positions containing exceptional values. As a consequence either:
    1. ALPArray filters invalid positions from the patches.
    2. PrimitiveArray::patch has an option to ignore the patch validity.

This PR currently does 2.i, albeit it does the filtering in encode_chunk_unchecked which is rather deep in the logic of this function. I could move the filtering higher up the call stack, at the cost of filtering a vec. I suspect your retort is that the user should set zeros if they want it to be fast.

2.ii is not hard but I don't love that it complicates the Patches API.

1 is also fine. We maybe want an unsafe version of encode that assumes the user has already done this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pulled the filtering of the patches up to alp_encode_components in the latest commit d78ec6a . I agree the logic is easier to follow because the validity isn't mixed with the encoding work.


I think my instinct though is option 1. ALPArray has two constructors: the first zeros invalid values, the second just verifies that invalid values are zero.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, one thing we don't have a good convention for is what logic lives in the compressor vs in some sort of "encode" function. The problem is, everyone has their own options for that function so it's hard to write.

In this case, I think yes it's reasonable to have a version that zeros and a version that doesn't (basically just "fill_null(array, 0)"?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. We don't implement fill_null for primitive array and Arrow-rs doesn't have a fill_null AFAICT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not hard to add it? I didn't include it originally since we didn't have a usecase then

@danking danking force-pushed the dk/alp-validity-in-encoded-only branch from 2c4d537 to 9dee362 Compare January 16, 2025 17:18
@danking danking force-pushed the dk/alp-validity-in-encoded-only branch from 9dee362 to d78ec6a Compare January 16, 2025 17:20
@danking danking added the benchmark Run benchmarks on this branch label Jan 16, 2025
@github-actions github-actions bot removed the benchmark Run benchmarks on this branch label Jan 16, 2025
@danking danking requested a review from gatesn January 16, 2025 19:13
@github-actions
Copy link
Contributor

Benchmarks: TPC-H

Table of Results
name PR 571c917 base 55cf81a ratio (PR/base) unit
tpch_q01/arrow 578488029 5.38794e+08 1.07367 ns
tpch_q01/parquet 794638055 7.67708e+08 1.03508 ns
tpch_q01/vortex-file-compressed 538380745 4.97305e+08 1.0826 ns
tpch_q02/arrow 143281712 1.41798e+08 1.01046 ns
tpch_q02/parquet 176075177 1.74088e+08 1.01141 ns
tpch_q02/vortex-file-compressed 147016091 1.43094e+08 1.02741 ns
tpch_q03/arrow 175485133 1.78148e+08 0.985051 ns
tpch_q03/parquet 377208830 3.74988e+08 1.00592 ns
tpch_q03/vortex-file-compressed 239197162 2.36892e+08 1.00973 ns
tpch_q04/arrow 182757488 1.75221e+08 1.04301 ns
tpch_q04/parquet 221367033 2.17775e+08 1.01649 ns
tpch_q04/vortex-file-compressed 191923439 1.89794e+08 1.01122 ns
tpch_q05/arrow 332760152 3.29572e+08 1.00967 ns
tpch_q05/parquet 515483676 5.16251e+08 0.998513 ns
tpch_q05/vortex-file-compressed 380258207 3.59504e+08 1.05773 ns
tpch_q06/arrow 26381850 2.64463e+07 0.997562 ns
tpch_q06/parquet 152803926 1.50886e+08 1.01271 ns
tpch_q06/vortex-file-compressed 72079205 1.12733e+08 0.639381 ns
tpch_q07/arrow 637680867 6.07056e+08 1.05045 ns
tpch_q07/parquet 776938543 7.79404e+08 0.996837 ns
tpch_q07/vortex-file-compressed 651399008 6.35293e+08 1.02535 ns
tpch_q08/arrow 272796294 2.66681e+08 1.02293 ns
tpch_q08/parquet 552993533 5.46745e+08 1.01143 ns
tpch_q08/vortex-file-compressed 356026928 3.33966e+08 1.06606 ns
tpch_q09/arrow 481366966 4.87889e+08 0.986632 ns
tpch_q09/parquet 803654179 7.98715e+08 1.00618 ns
tpch_q09/vortex-file-compressed 598346751 5.67483e+08 1.05439 ns
tpch_q10/arrow 270811980 2.59886e+08 1.04204 ns
tpch_q10/parquet 518292954 5.01731e+08 1.03301 ns
tpch_q10/vortex-file-compressed 296142484 2.88631e+08 1.02602 ns
tpch_q11/arrow 148435878 1.39308e+08 1.06552 ns
tpch_q11/parquet 154183381 1.49126e+08 1.03391 ns
tpch_q11/vortex-file-compressed 135344278 1.33976e+08 1.01021 ns
tpch_q12/arrow 183558933 1.84455e+08 0.995142 ns
tpch_q12/parquet 329556957 3.2921e+08 1.00105 ns
tpch_q12/vortex-file-compressed 283254676 3.23178e+08 0.876467 ns
tpch_q13/arrow 170852576 1.62962e+08 1.04842 ns
tpch_q13/parquet 322361008 3.01753e+08 1.0683 ns
tpch_q13/vortex-file-compressed 188688205 1.83171e+08 1.03012 ns
tpch_q14/arrow 37609381 3.7223e+07 1.01038 ns
tpch_q14/parquet 234845677 2.32973e+08 1.00804 ns
tpch_q14/vortex-file-compressed 89777901 9.08667e+07 0.988018 ns
tpch_q15/arrow 69457275 6.97148e+07 0.996306 ns
tpch_q15/parquet 329523500 3.2461e+08 1.01514 ns
tpch_q15/vortex-file-compressed 154645416 1.53069e+08 1.0103 ns
tpch_q16/arrow 99973834 9.83157e+07 1.01687 ns
tpch_q16/parquet 114449246 1.1369e+08 1.00668 ns
tpch_q16/vortex-file-compressed 106154429 1.04108e+08 1.01966 ns
tpch_q17/arrow 633728048 6.05266e+08 1.04702 ns
tpch_q17/parquet 698795991 6.99263e+08 0.999331 ns
tpch_q17/vortex-file-compressed 614305368 5.84771e+08 1.05051 ns
tpch_q18/arrow 1154651548 1.10168e+09 1.04808 ns
tpch_q18/parquet 1365160475 1.3361e+09 1.02175 ns
tpch_q18/vortex-file-compressed 1168052552 1.1252e+09 1.03808 ns
tpch_q19/arrow 152702348 1.51024e+08 1.01112 ns
tpch_q19/parquet 421969362 4.23039e+08 0.997473 ns
tpch_q19/vortex-file-compressed 173511800 1.46807e+08 1.1819 ns
tpch_q20/arrow 184852844 1.75823e+08 1.05136 ns
tpch_q20/parquet 324015448 3.18568e+08 1.0171 ns
tpch_q20/vortex-file-compressed 225256898 2.21648e+08 1.01628 ns
tpch_q21/arrow 1009807723 1.0403e+09 0.970688 ns
tpch_q21/parquet 1136851066 1.16899e+09 0.972504 ns
tpch_q21/vortex-file-compressed 1014824155 1.07995e+09 0.939691 ns
tpch_q22/arrow 78506970 8.09701e+07 0.969579 ns
tpch_q22/parquet 109551261 1.17971e+08 0.928631 ns
tpch_q22/vortex-file-compressed 83925751 8.9394e+07 0.93883 ns

@github-actions
Copy link
Contributor

Benchmarks: Clickbench

Table of Results
name PR 571c917 base 55cf81a ratio (PR/base) unit
clickbench_q00/parquet 2039788 1.78497e+06 1.14276 ns
clickbench_q01/parquet 62873413 6.18138e+07 1.01714 ns
clickbench_q02/parquet 123793497 1.19709e+08 1.03412 ns
clickbench_q03/parquet 88093970 8.36378e+07 1.05328 ns
clickbench_q04/parquet 738276023 6.67803e+08 1.10553 ns
clickbench_q05/parquet 894469486 8.54713e+08 1.04651 ns
clickbench_q06/parquet 1995679 1.91723e+06 1.04092 ns
clickbench_q07/parquet 65113023 6.47838e+07 1.00508 ns
clickbench_q08/parquet 800278898 7.72274e+08 1.03626 ns
clickbench_q09/parquet 1140674549 1.08484e+09 1.05147 ns
clickbench_q10/parquet 267628302 2.63582e+08 1.01535 ns
clickbench_q11/parquet 316264761 3.02824e+08 1.04438 ns
clickbench_q12/parquet 892819191 8.45987e+08 1.05536 ns
clickbench_q13/parquet 1176850491 1.14135e+09 1.03111 ns
clickbench_q14/parquet 896644650 8.50139e+08 1.0547 ns
clickbench_q15/parquet 849803655 7.83295e+08 1.08491 ns
clickbench_q16/parquet 1800123956 1.70188e+09 1.05773 ns
clickbench_q17/parquet 1557182375 1.48007e+09 1.0521 ns
clickbench_q18/parquet 3240170241 3.10581e+09 1.04326 ns
clickbench_q19/parquet 69740410 6.64865e+07 1.04894 ns
clickbench_q20/parquet 1310374738 1.22505e+09 1.06965 ns
clickbench_q21/parquet 1454408699 1.3996e+09 1.03916 ns
clickbench_q22/parquet 2535691778 2.44428e+09 1.0374 ns
clickbench_q23/parquet 8775980384 8.46e+09 1.03735 ns
clickbench_q24/parquet 555297961 5.44866e+08 1.01915 ns
clickbench_q25/parquet 538226001 5.25877e+08 1.02348 ns
clickbench_q26/parquet 623109387 6.16527e+08 1.01068 ns
clickbench_q27/parquet 1726748060 1.71361e+09 1.00767 ns
clickbench_q28/parquet 11800225999 1.19554e+10 0.987021 ns
clickbench_q29/parquet 449604088 4.30973e+08 1.04323 ns
clickbench_q30/parquet 808519122 8.12181e+08 0.995491 ns
clickbench_q31/parquet 832165470 8.27125e+08 1.00609 ns
clickbench_q32/parquet 3031656743 3.00746e+09 1.00804 ns
clickbench_q33/parquet 3147732056 3.06139e+09 1.0282 ns
clickbench_q34/parquet 3119490384 3.0854e+09 1.01105 ns
clickbench_q35/parquet 921888882 9.32577e+08 0.988539 ns
clickbench_q36/parquet 189738637 1.88853e+08 1.00469 ns
clickbench_q37/parquet 93653201 9.09492e+07 1.02973 ns
clickbench_q38/parquet 124673575 1.19951e+08 1.03937 ns
clickbench_q39/parquet 357817660 3.52829e+08 1.01414 ns
clickbench_q40/parquet 53475801 5.61531e+07 0.952321 ns
clickbench_q41/parquet 52479513 5.1284e+07 1.02331 ns
clickbench_q42/parquet 70685816 7.12587e+07 0.991961 ns
clickbench_q00/vortex-file-compressed 2109898 1.97997e+06 1.06562 ns
clickbench_q01/vortex-file-compressed 72366212 5.39854e+07 1.34048 ns
clickbench_q02/vortex-file-compressed 162355929 2.01902e+08 0.804132 ns
clickbench_q03/vortex-file-compressed 105400338 1.01378e+08 1.03967 ns
clickbench_q04/vortex-file-compressed 712547113 6.66005e+08 1.06988 ns
clickbench_q05/vortex-file-compressed 891702330 8.36475e+08 1.06602 ns
clickbench_q06/vortex-file-compressed 2408829 2.07995e+06 1.15812 ns
clickbench_q07/vortex-file-compressed 115926574 9.84842e+07 1.17711 ns
clickbench_q08/vortex-file-compressed 834727520 7.6458e+08 1.09175 ns
clickbench_q09/vortex-file-compressed 1150521060 1.09036e+09 1.05517 ns
clickbench_q10/vortex-file-compressed 364271381 3.31862e+08 1.09766 ns
clickbench_q11/vortex-file-compressed 434773111 3.91405e+08 1.1108 ns
clickbench_q12/vortex-file-compressed 997863219 9.22638e+08 1.08153 ns
clickbench_q13/vortex-file-compressed 1439592141 1.37014e+09 1.05069 ns
clickbench_q14/vortex-file-compressed 998366173 9.40836e+08 1.06115 ns
clickbench_q15/vortex-file-compressed 871345229 8.00028e+08 1.08914 ns
clickbench_q16/vortex-file-compressed 1653765432 1.52604e+09 1.0837 ns
clickbench_q17/vortex-file-compressed 1571796057 1.43408e+09 1.09603 ns
clickbench_q18/vortex-file-compressed 3297783927 3.13325e+09 1.05251 ns
clickbench_q19/vortex-file-compressed 93945481 1.22327e+08 0.767987 ns
clickbench_q20/vortex-file-compressed 821187712 7.87321e+08 1.04301 ns
clickbench_q21/vortex-file-compressed 1056934312 1.21463e+09 0.870166 ns
clickbench_q22/vortex-file-compressed 2164327713 2.46755e+09 0.877117 ns
clickbench_q23/vortex-file-compressed 3853126203 5.2947e+09 0.727733 ns
clickbench_q24/vortex-file-compressed 739093564 7.00579e+08 1.05498 ns
clickbench_q25/vortex-file-compressed 688740624 6.5858e+08 1.0458 ns
clickbench_q26/vortex-file-compressed 786839164 7.41178e+08 1.06161 ns
clickbench_q27/vortex-file-compressed 1716355677 1.63522e+09 1.04962 ns
clickbench_q28/vortex-file-compressed 11771143494 1.18434e+10 0.9939 ns
clickbench_q29/vortex-file-compressed 451559061 4.56383e+08 0.989431 ns
clickbench_q30/vortex-file-compressed 915237144 8.95733e+08 1.02177 ns
clickbench_q31/vortex-file-compressed 1103208906 1.06241e+09 1.0384 ns
clickbench_q32/vortex-file-compressed 3488286956 3.33604e+09 1.04564 ns
clickbench_q33/vortex-file-compressed 2660271106 2.42347e+09 1.09771 ns
clickbench_q34/vortex-file-compressed 2661030925 2.40695e+09 1.10556 ns
clickbench_q35/vortex-file-compressed 1007295536 9.13495e+08 1.10268 ns
clickbench_q36/vortex-file-compressed 62128405 5.10723e+08 0.121648 ns
clickbench_q37/vortex-file-compressed 55646727 4.85299e+08 0.114665 ns
clickbench_q38/vortex-file-compressed 56215393 3.10203e+08 0.181221 ns
clickbench_q39/vortex-file-compressed 103287036 4.55469e+08 0.226771 ns
clickbench_q40/vortex-file-compressed 56172433 3.47014e+08 0.161874 ns
clickbench_q41/vortex-file-compressed 54782751 3.17023e+08 0.172804 ns
clickbench_q42/vortex-file-compressed 55139206 2.24432e+08 0.245683 ns

@github-actions
Copy link
Contributor

Benchmarks: datafusion

Table of Results
name PR 571c917 base 55cf81a ratio (PR/base) unit
arrow/planning 975719 935111 1.04343 ns
arrow/exec 2.1218e+06 1.98341e+06 1.06977 ns
vortex-pushdown-compressed/planning 593087 582530 1.01812 ns
vortex-pushdown-compressed/exec 3.07232e+06 2.73088e+06 1.12503 ns
vortex-pushdown-uncompressed/planning 594620 582944 1.02003 ns
vortex-pushdown-uncompressed/exec 1.63304e+06 1.54541e+06 1.0567 ns
vortex-nopushdown-compressed/planning 969143 947461 1.02288 ns
vortex-nopushdown-compressed/exec 4.52911e+06 3.25154e+06 1.39291 ns
vortex-nopushdown-uncompressed/planning 973086 944819 1.02992 ns
vortex-nopushdown-uncompressed/exec 6.67824e+06 5.19098e+06 1.28651 ns

@github-actions
Copy link
Contributor

Benchmarks: random_access

Table of Results
name PR 571c917 base 55cf81a ratio (PR/base) unit
random-access/vortex-tokio-local-disk 3.16037e+06 2.50799e+06 1.26012 ns
random-access/vortex-local-fs 3.17615e+06 2.60123e+06 1.22102 ns
random-access/parquet-tokio-local-disk 2.50201e+08 2.29731e+08 1.0891 ns

@github-actions
Copy link
Contributor

Benchmarks: compress

Table of Results
name PR 571c917 base 55cf81a ratio (PR/base) unit
compress time/taxi 1.63935e+09 1.67021e+09 0.981526 ns
compress time/taxi throughput 0.287194 0.281888 1.01882 bytes/ns
parquet_rs-zstd compress time/taxi 1.79037e+09 1.88202e+09 0.9513 ns
parquet_rs-zstd compress time/taxi throughput 0.262969 0.250162 1.05119 bytes/ns
decompress time/taxi 4.09509e+08 4.32905e+08 0.945957 ns
decompress time/taxi throughput 1.1497 1.08756 1.05713 bytes/ns
parquet_rs-zstd decompress time/taxi 3.05743e+08 3.14697e+08 0.971547 ns
parquet_rs-zstd decompress time/taxi throughput 1.53989 1.49607 1.02929 bytes/ns
compress time/AirlineSentiment 609305 561562 1.08502 ns
compress time/AirlineSentiment throughput 0.00338747 0.00367546 0.921644 bytes/ns
parquet_rs-zstd compress time/AirlineSentiment 56773.8 58021.3 0.978499 ns
parquet_rs-zstd compress time/AirlineSentiment throughput 0.0363548 0.0355731 1.02197 bytes/ns
decompress time/AirlineSentiment 99571.1 106019 0.939178 ns
decompress time/AirlineSentiment throughput 0.0207289 0.0194681 1.06476 bytes/ns
parquet_rs-zstd decompress time/AirlineSentiment 32341.9 33282.8 0.971732 ns
parquet_rs-zstd decompress time/AirlineSentiment throughput 0.0638181 0.0620141 1.02909 bytes/ns
compress time/Arade 2.77e+09 2.8945e+09 0.956986 ns
compress time/Arade throughput 0.284128 0.271907 1.04495 bytes/ns
parquet_rs-zstd compress time/Arade 3.00871e+09 3.19541e+09 0.941573 ns
parquet_rs-zstd compress time/Arade throughput 0.261586 0.246302 1.06205 bytes/ns
decompress time/Arade 6.10422e+08 5.53299e+08 1.10324 ns
decompress time/Arade throughput 1.28933 1.42244 0.90642 bytes/ns
parquet_rs-zstd decompress time/Arade 6.73787e+08 7.06954e+08 0.953085 ns
parquet_rs-zstd decompress time/Arade throughput 1.16808 1.11328 1.04922 bytes/ns
compress time/Bimbo 1.18298e+10 1.18307e+10 0.999921 ns
compress time/Bimbo throughput 0.601983 0.601936 1.00008 bytes/ns
parquet_rs-zstd compress time/Bimbo 2.16699e+10 2.19813e+10 0.985834 ns
parquet_rs-zstd compress time/Bimbo throughput 0.328628 0.323973 1.01437 bytes/ns
decompress time/Bimbo 3.19286e+09 3.47781e+09 0.918067 ns
decompress time/Bimbo throughput 2.2304 2.04765 1.08925 bytes/ns
parquet_rs-zstd decompress time/Bimbo 2.6869e+09 2.81571e+09 0.954253 ns
parquet_rs-zstd decompress time/Bimbo throughput 2.6504 2.52915 1.04794 bytes/ns
compress time/CMSprovider 1.36308e+10 1.36063e+10 1.0018 ns
compress time/CMSprovider throughput 0.377761 0.378443 0.998199 bytes/ns
parquet_rs-zstd compress time/CMSprovider 1.91483e+10 2.0216e+10 0.947181 ns
parquet_rs-zstd compress time/CMSprovider throughput 0.268912 0.254708 1.05576 bytes/ns
decompress time/CMSprovider 3.07142e+09 3.41631e+09 0.899047 ns
decompress time/CMSprovider throughput 1.67649 1.50724 1.11229 bytes/ns
parquet_rs-zstd decompress time/CMSprovider 5.4447e+09 6.45006e+09 0.844132 ns
parquet_rs-zstd decompress time/CMSprovider throughput 0.945726 0.798318 1.18465 bytes/ns
compress time/Euro2016 2.18498e+09 2.25145e+09 0.970478 ns
compress time/Euro2016 throughput 0.179982 0.174668 1.03042 bytes/ns
parquet_rs-zstd compress time/Euro2016 1.55528e+09 1.62787e+09 0.955405 ns
parquet_rs-zstd compress time/Euro2016 throughput 0.252853 0.241577 1.04668 bytes/ns
decompress time/Euro2016 2.28304e+08 2.49154e+08 0.916316 ns
decompress time/Euro2016 throughput 1.72251 1.57837 1.09133 bytes/ns
parquet_rs-zstd decompress time/Euro2016 4.91388e+08 5.4553e+08 0.900754 ns
parquet_rs-zstd decompress time/Euro2016 throughput 0.800296 0.72087 1.11018 bytes/ns
compress time/Food 1.10362e+09 1.12477e+09 0.981188 ns
compress time/Food throughput 0.301483 0.295812 1.01917 bytes/ns
parquet_rs-zstd compress time/Food 1.08126e+09 1.12485e+09 0.961244 ns
parquet_rs-zstd compress time/Food throughput 0.307717 0.295791 1.04032 bytes/ns
decompress time/Food 1.17911e+08 1.24322e+08 0.948436 ns
decompress time/Food throughput 2.82179 2.67629 1.05437 bytes/ns
parquet_rs-zstd decompress time/Food 2.22272e+08 2.33787e+08 0.950745 ns
parquet_rs-zstd decompress time/Food throughput 1.49691 1.42318 1.05181 bytes/ns
compress time/HashTags 2.53512e+09 2.63825e+09 0.960911 ns
compress time/HashTags throughput 0.317342 0.304938 1.04068 bytes/ns
parquet_rs-zstd compress time/HashTags 2.49267e+09 2.60185e+09 0.958034 ns
parquet_rs-zstd compress time/HashTags throughput 0.322748 0.309204 1.0438 bytes/ns
decompress time/HashTags 3.46813e+08 3.8641e+08 0.897524 ns
decompress time/HashTags throughput 2.3197 2.08199 1.11418 bytes/ns
parquet_rs-zstd decompress time/HashTags 7.9688e+08 9.24851e+08 0.86163 ns
parquet_rs-zstd decompress time/HashTags throughput 1.00957 0.869873 1.16059 bytes/ns
compress time/TPC-H l_comment chunked without fsst 2.96676e+09 3.47982e+09 0.852562 ns
compress time/TPC-H l_comment chunked without fsst throughput 0.0840045 0.0716191 1.17294 bytes/ns
parquet_rs-zstd compress time/TPC-H l_comment chunked without fsst 9.082e+08 9.4733e+08 0.958694 ns
parquet_rs-zstd compress time/TPC-H l_comment chunked without fsst throughput 0.274413 0.263078 1.04309 bytes/ns
decompress time/TPC-H l_comment chunked without fsst 5.60106e+07 2.09651e+07 2.67161 ns
decompress time/TPC-H l_comment chunked without fsst throughput 4.44954 11.8874 0.374306 bytes/ns
parquet_rs-zstd decompress time/TPC-H l_comment chunked without fsst 2.5208e+08 2.59059e+08 0.973061 ns
parquet_rs-zstd decompress time/TPC-H l_comment chunked without fsst throughput 0.98866 0.962026 1.02768 bytes/ns
compress time/TPC-H l_comment chunked 1.02356e+09 1.05191e+09 0.973045 ns
compress time/TPC-H l_comment chunked throughput 0.243486 0.236923 1.0277 bytes/ns
parquet_rs-zstd compress time/TPC-H l_comment chunked 9.04676e+08 9.64309e+08 0.93816 ns
parquet_rs-zstd compress time/TPC-H l_comment chunked throughput 0.275481 0.258446 1.06592 bytes/ns
decompress time/TPC-H l_comment chunked 8.05589e+07 9.54296e+07 0.84417 ns
decompress time/TPC-H l_comment chunked throughput 3.09366 2.61157 1.18459 bytes/ns
parquet_rs-zstd decompress time/TPC-H l_comment chunked 2.51705e+08 2.59491e+08 0.969994 ns
parquet_rs-zstd decompress time/TPC-H l_comment chunked throughput 0.990135 0.960425 1.03093 bytes/ns
compress time/TPC-H l_comment canonical 1.02687e+09 1.06627e+09 0.96305 ns
compress time/TPC-H l_comment canonical throughput 0.242698 0.233731 1.03837 bytes/ns
parquet_rs-zstd compress time/TPC-H l_comment canonical 9.09266e+08 9.57728e+08 0.949399 ns
parquet_rs-zstd compress time/TPC-H l_comment canonical throughput 0.27409 0.260221 1.0533 bytes/ns
decompress time/TPC-H l_comment canonical 8.00059e+07 9.64913e+07 0.829151 ns
decompress time/TPC-H l_comment canonical throughput 3.11503 2.58283 1.20605 bytes/ns
parquet_rs-zstd decompress time/TPC-H l_comment canonical 2.51912e+08 2.60925e+08 0.965457 ns
parquet_rs-zstd decompress time/TPC-H l_comment canonical throughput 0.989316 0.955142 1.03578 bytes/ns

@danking
Copy link
Contributor Author

danking commented Jan 16, 2025

Huh. I was expecting a speed penalty due to verification of the validity of the patches array, but actually, it made some things quite a bit faster. I filtered to rows whose ratio is greater than 0.1 from 1.

name	PR 571c917	base 55cf81a	ratio (PR/base)	unit
tpch_q06/vortex-file-compressed	72079205	1.12733e+08	0.639381	ns
tpch_q12/vortex-file-compressed	283254676	3.23178e+08	0.876467	ns
tpch_q19/vortex-file-compressed	173511800	1.46807e+08	1.1819	ns

@danking
Copy link
Contributor Author

danking commented Jan 16, 2025

Wow, wtf, click bench? Again, filtering to things outside of [0.9, 1.1]

clickbench_q01/vortex-file-compressed	72366212	5.39854e+07	1.34048		ns
clickbench_q02/vortex-file-compressed	162355929	2.01902e+08	0.804132	ns
clickbench_q06/vortex-file-compressed	2408829		2.07995e+06	1.15812		ns
clickbench_q07/vortex-file-compressed	115926574	9.84842e+07	1.17711		ns
clickbench_q11/vortex-file-compressed	434773111	3.91405e+08	1.1108		ns
clickbench_q19/vortex-file-compressed	93945481	1.22327e+08	0.767987	ns
clickbench_q21/vortex-file-compressed	1056934312	1.21463e+09	0.870166	ns
clickbench_q22/vortex-file-compressed	2164327713	2.46755e+09	0.877117	ns
clickbench_q23/vortex-file-compressed	3853126203	5.2947e+09	0.727733	ns
clickbench_q34/vortex-file-compressed	2661030925	2.40695e+09	1.10556		ns
clickbench_q35/vortex-file-compressed	1007295536	9.13495e+08	1.10268		ns
clickbench_q36/vortex-file-compressed	62128405	5.10723e+08	0.121648	ns
clickbench_q37/vortex-file-compressed	55646727	4.85299e+08	0.114665	ns
clickbench_q38/vortex-file-compressed	56215393	3.10203e+08	0.181221	ns
clickbench_q39/vortex-file-compressed	103287036	4.55469e+08	0.226771	ns
clickbench_q40/vortex-file-compressed	56172433	3.47014e+08	0.161874	ns
clickbench_q41/vortex-file-compressed	54782751	3.17023e+08	0.172804	ns
clickbench_q42/vortex-file-compressed	55139206	2.24432e+08	0.245683	ns

@robert3005
Copy link
Contributor

You changed a lot of binary searches to a constant lookup?

@danking
Copy link
Contributor Author

danking commented Jan 16, 2025

Compress isn't changed much but decompress is faster:

decompress time/taxi	4.09509e+08	4.32905e+08	0.945957	ns
decompress time/AirlineSentiment	99571.1	106019	0.939178	ns
decompress time/Arade	6.10422e+08	5.53299e+08	1.10324	ns
decompress time/Bimbo	3.19286e+09	3.47781e+09	0.918067	ns
decompress time/CMSprovider	3.07142e+09	3.41631e+09	0.899047	ns
decompress time/Euro2016	2.28304e+08	2.49154e+08	0.916316	ns
decompress time/Food	1.17911e+08	1.24322e+08	0.948436	ns
decompress time/HashTags	3.46813e+08	3.8641e+08	0.897524	ns
decompress time/TPC-H l_comment chunked	8.05589e+07	9.54296e+07	0.84417	ns

@danking
Copy link
Contributor Author

danking commented Jan 16, 2025

@robert3005 Hmm. It could be that RunEnd with bool values now reports true count properly and that makes a lot of things fast? The other big benefit is for ALP arrays that have lots of invalid positions whose values are exceptions. I'm having a hard time imagining how that happens though?

@robert3005
Copy link
Contributor

Runend compare now canonicalizes avoiding creating bool run end array

@danking
Copy link
Contributor Author

danking commented Jan 16, 2025

The RunEnd true count and null count changes alone are not the cause of the click bench improvements: #1982 (comment)

@danking danking mentioned this pull request Jan 16, 2025
&& array.logical_validity().all_valid(),
)),
Stat::TrueCount => match array.dtype() {
DType::Bool(_) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you still need to subtract nulls?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch, fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm gonna extract this to a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danking
Copy link
Contributor Author

danking commented Jan 17, 2025

Closing until #2007 merges.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants