Skip to content

Cast support for RunEndEncoded arrays - #8589

Merged
alamb merged 24 commits into
apache:mainfrom
vegarsti:cast-run-end-encoded-arrays
Oct 26, 2025
Merged

Cast support for RunEndEncoded arrays#8589
alamb merged 24 commits into
apache:mainfrom
vegarsti:cast-run-end-encoded-arrays

Conversation

@vegarsti

@vegarsti vegarsti commented Oct 11, 2025

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

This PR implements casting support for RunEndEncoded arrays in Apache Arrow.

What changes are included in this PR?

  • run_end_encoded_cast in arrow-cast/src/cast/run_array.rs
  • cast_to_run_end_encoded in arrow-cast/src/cast/run_array.rs
  • Tests in arrow-cast/src/cast/mod.rs

Are these changes tested?

Yes!

Are there any user-facing changes?

No breaking changes, just new functionality

@github-actions github-actions Bot added the arrow Changes to the arrow crate label Oct 11, 2025
@vegarsti vegarsti changed the title Casting to/from RunEndEncoded arrays Casting support for RunEndEncoded arrays Oct 11, 2025
@vegarsti

Copy link
Copy Markdown
Contributor Author

Raised this PR to get Richard Baah's excellent work over the line! cc @albertlockett @brancz @alamb @Rich-T-kid

@brancz brancz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we're getting close to the finish line with these changes!

Comment thread arrow-cast/src/cast/run_array.rs Outdated
@tustvold

tustvold commented Oct 12, 2025

Copy link
Copy Markdown
Contributor

Is there some way we can avoid the quadratic codegen with code paths parameterized on both run end type and value type? Perhaps it'd be possible to identify where the transitions are, perhaps using the comparison kernels and comparing the array with a slice offset by one, and then use this to construct the indexes and a filter to construct the values array?

Have we done any empirical quantification into the impact this has on code bloat / compile times?

Edit: https://docs.rs/arrow-ord/latest/arrow_ord/partition/fn.partition.html is the function I'm thinking of.

@vegarsti

vegarsti commented Oct 13, 2025

Copy link
Copy Markdown
Contributor Author

Have we done any empirical quantification into the impact this has on code bloat / compile times?

I have not! Happy to do that though. Any pointers to how you'd like me to do that, from previous PRs for example? Or does a basic comparison of compile time and binary size on main and this branch suffice?

@tustvold

Copy link
Copy Markdown
Contributor

Or does a basic comparison of compile time and binary size on main and this branch suffice?

Just this, quadratic codegen is typically severe enough to be easily measurable.

@vegarsti

Copy link
Copy Markdown
Contributor Author

The compile time increased by 2 seconds.

         cargo build --release
main     569.35s user 23.69s system 863% cpu 1:08.66 total
branch   567.33s user 23.96s system 891% cpu 1:06.33 total

The size of libarrow_cast.rlib increased by 279kb (3.82%)

         libarrow_cast.rlib size
main     7,316,832
branch   7,596,568

@tustvold

tustvold commented Oct 13, 2025

Copy link
Copy Markdown
Contributor

Yeah... That's quite bad for a single kernel, especially given the relatively niche usage of RunEndEncodedArrays, I hope you can understand that we need to be careful to keep this under control.

What did you think of my suggestion about using the partition kernel to compute the run ends? It might actually be faster and would largely eliminate the additional codegen.

It would mean making arrow-cast depend on arrow-ord, which is a bit meh, but perhaps unavoidable. It could possibly be a feature flag. 🤔

@vegarsti

vegarsti commented Oct 13, 2025

Copy link
Copy Markdown
Contributor Author

I understand! I haven't had time to look into your suggestion, but I will.

Out of curiosity, though, the approach in this PR seems quite similar to the code for dictionaries. Does the dictionary code similarly bloat the binary, and if so, why is that acceptable but not for REE?

@tustvold

tustvold commented Oct 13, 2025

Copy link
Copy Markdown
Contributor

Out of curiosity, though, the approach in this PR seems quite similar to the code for dictionaries. Does the dictionary code similarly bloat the binary, and if so, why is that acceptable but not for REE?

Dictionaries run into similar challenges, and a lot of effort has been expended trying to mitigate the bloat they cause. For example #3616 #4705 #4701 to name a few. Ultimately it's a compromise, there isn't a way to avoid this bloat and support dictionaries so we pay the tax, with run-end encoded arrays the tax isn't necessary and so it is better we don't pay it.

@vegarsti

Copy link
Copy Markdown
Contributor Author

Out of curiosity, though, the approach in this PR seems quite similar to the code for dictionaries. Does the dictionary code similarly bloat the binary, and if so, why is that acceptable but not for REE?

Dictionaries run into similar challenges, and a lot of effort has been expended trying to mitigate the bloat they cause. For example #3616 #4705 #4701 to name a few. Ultimately it's a compromise, there isn't a way to avoid this bloat and support dictionaries so we pay the tax, with run-end encoded arrays the tax isn't necessary and so it is better we don't pay it.

Thanks for the context!

@brancz

brancz commented Oct 13, 2025

Copy link
Copy Markdown
Contributor

We're talking about the pack_runs macro right? I realize it's nice as a macro, but it also seems fine to just write out by hand.

@tustvold

Copy link
Copy Markdown
Contributor

The fact it is a macro is not the issue here, the problem is code generation based with complexity <Number of Index Types> * <Number of Value Types> which results in slow compilation and binary bloat. Whether this is achieved with macros, generics or copy paste doesn't change this 😄

@brancz

brancz commented Oct 14, 2025

Copy link
Copy Markdown
Contributor

Got it. The way I see it, there are two paths. Either:

  1. We manage to get it to work with arrow_ord's partition and avoid the amount of code required altogether (whether by macro or not)
  2. We do it via macro but put ree casting behind a feature flag, so users who wants this can opt into it (although it feels a little strange to hide features that are very reasonable and available for other types behind a feature flag, but I don't feel strongly one way or another as we'd just enable it and move on).

@vegarsti can you give the arrow-ord partitioning a try so we understand whether this would be a workable path?

@vegarsti

vegarsti commented Oct 14, 2025

Copy link
Copy Markdown
Contributor Author

Yeah, I will give the arrow-ord partitioning a try. Some time this week! It seems like a good approach, thanks @tustvold!

As for the feature flag, to me that seems a bit complicated - either the REE type should be supported or not, imo? Also, unless I'm missing something, whether to put this in a feature flag would apply to the whole REE epic #3520, so that should (eventually) be raised there.

It would be great with some guidelines for the arrow-rs project with regard to the tradeoff between features and size/compile times. I'm guessing opinions might vary a bit between maintainers as well. Guidelines might make it easier to come to alignment in such discussions.

In any case, maybe we get around this issue with the arrow-ord approach 🙏🏻

@brancz brancz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Love this approach with ord, also reads far simpler than before. Good suggestion @tustvold and nice work @vegarsti!

(I can't actually approve, but symbolically I'm approving this)

@vegarsti

Copy link
Copy Markdown
Contributor Author

Thanks for the symbolic approve! 😄

@alamb alamb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thank you @vegarsti @brancz and @tustvold

I went through this PR carefully and while I have a few suggestions for comments and additional tests I think they could be done as follow on PRs too if you prefer

What I suggest as a flow is:

  1. @vegarsti addresses any comments they would like
  2. File follow on tickets: benchmark for REE casting, and some ideas for performance optimizations.
  3. Merge this PR

Thanks again 🚀

Comment thread arrow-cast/Cargo.toml
arrow-array = { workspace = true }
arrow-buffer = { workspace = true }
arrow-data = { workspace = true }
arrow-ord = { workspace = true }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we are trying to keep the number of dependencies to a minimum

I see this is used to call partition which is clever but overly general. I think you can also partition a (single) column using eq and an offset to look for consecutive rows which are different.

Something like

let arr = ...;
let arr_shift1 = arr.slice(1, arr.len()-1);
let transitions = eq(arr, arr_shift_1);

However, the eq kernels is also in arrow-ord so I am not sure there is a way around it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oh, that's nice!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Does it suffice here to file an issue for potentially getting rid of this dependency?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The way I'm reading this is eq might be faster but won't get rid of the dependency, but getting rid of the dependency would be the best option. Is that correct?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Filed #8708 for getting rid of arrow-ord and #8707 for performance improvement

Comment thread arrow-cast/src/cast/run_array.rs Outdated
Comment thread arrow-cast/src/cast/run_array.rs Outdated
Comment thread arrow-cast/src/cast/run_array.rs Outdated
Comment thread arrow-cast/src/cast/run_array.rs Outdated
Comment thread arrow-cast/src/cast/run_array.rs Outdated
Comment thread arrow-cast/src/cast/mod.rs Outdated
Comment thread arrow-cast/src/cast/mod.rs
Comment thread arrow-cast/src/cast/mod.rs
Comment thread arrow-cast/src/cast/mod.rs Outdated
@alamb

alamb commented Oct 23, 2025

Copy link
Copy Markdown
Contributor

looks like there are some minor CI issues to address too

@vegarsti

vegarsti commented Oct 23, 2025

Copy link
Copy Markdown
Contributor Author

Looks like someone triggered CI - thank you! There are failures, will address tomorrow.

Edit: Oops, posted this before seeing the review.

@vegarsti

Copy link
Copy Markdown
Contributor Author

Thanks so much @alamb, this is excellent! Will ping you again after addressing.

@vegarsti

vegarsti commented Oct 25, 2025

Copy link
Copy Markdown
Contributor Author

@alamb I've addressed your very helpful review, thank you so much. I think this is ready to merge now - if you agree, that is. I've filed issues

@vegarsti vegarsti changed the title Casting support for RunEndEncoded arrays Cast support for RunEndEncoded arrays Oct 25, 2025
@vegarsti

Copy link
Copy Markdown
Contributor Author

@alamb I've addressed your very helpful review, thank you so much. I think this is ready to merge now - if you agree, that is. I've filed issues

PR for #8709: #8710

@alamb

alamb commented Oct 26, 2025

Copy link
Copy Markdown
Contributor

I pushed a commit to solve the RAT ci test, and merged up from main

https://github.com/apache/arrow-rs/actions/runs/18813730483/job/53685630690?pr=8589

@alamb
alamb merged commit c149027 into apache:main Oct 26, 2025
27 checks passed
@alamb

alamb commented Oct 26, 2025

Copy link
Copy Markdown
Contributor

Thank you @vegarsti

@vegarsti
vegarsti deleted the cast-run-end-encoded-arrays branch October 28, 2025 18:23
alamb pushed a commit that referenced this pull request Oct 30, 2025
Related to #8707.

Inspired by
#8716 (comment), a
follow up improvement to #8589: We already know what the length of the
two vectors will be, so we can create them with that capacity.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arrow Changes to the arrow crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants