-
Notifications
You must be signed in to change notification settings - Fork 1k
Casting support for RunEndEncoded arrays #8589
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
87e543d
to
f9ae6f9
Compare
Raised this PR to get Richard Baah's excellent work over the line! cc @albertlockett @brancz @alamb @Rich-T-kid |
There was a problem hiding this comment.
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!
arrow-cast/src/cast/run_array.rs
Outdated
(false, false) => match value_type { | ||
// Primitive types | ||
DataType::Boolean => { | ||
cast_array.as_boolean().value(i) == cast_array.as_boolean().value(i - 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we invert all these comparisons instead? As in switch on value types, and then cast once and perform all iterations. I find this both hard to read, and we probably perform extra unnecessary work because of that (it's possible the compiler might optimize it away, but I don't think so).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent idea!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did a quick and dirty one now in 88c0d8a, there might be a common trait I could use for arrays to avoid repeating a lot of the code - I haven't found that yet.
Casting then iterating instead of casting within the loop is a very good idea, sounds obvious after hearing it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did a second iteration this morning in 2358010 (I've replaced the commit above). Tried to follow a somewhat similar approach as is done with dictionaries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a macro in 692f6ea
Implement casting between REE arrays and other Arrow types. REE-to-REE casting validates run-end upcasts only (Int16→Int32, Int16→Int64, Int32→Int64) to prevent invalid sequences.
Implement casting between REE arrays and other Arrow types. REE-to-REE casting validates run-end upcasts only (Int16→Int32, Int16→Int64, Int32→Int64) to prevent invalid sequences. rebased changes
88c0d8a
to
2358010
Compare
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. |
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? |
Just this, quadratic codegen is typically severe enough to be easily measurable. |
The compile time increased by 2 seconds.
The size of
|
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. 🤔 |
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? |
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! |
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. |
The fact it is a macro is not the issue here, the problem is code generation based with complexity |
Got it. The way I see it, there are two paths. Either:
@vegarsti can you give the arrow-ord partitioning a try so we understand whether this would be a workable path? |
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 🙏🏻 |
Okay I have the scaffolding but tests fail. Stay tuned 👀 |
Which issue does this PR close?
RunArray
(Run Length Encoding (RLE) / Run End Encoding (REE) support) #3520, but no specific issue for casting.Rationale for this change
This PR implements casting support for RunEndEncoded arrays in Apache Arrow.
What changes are included in this PR?
Users can now cast RunEndEncoded arrays using the standard
arrow_cast::cast()
functionrun_end_encoded_cast()
: Casts values within existing RunEndEncoded arrays to different typescast_to_run_end_encoded()
: Converts regular arrays to RunEndEncoded format with run-end encodingcan_cast_types()
to support RunEndEncoded compatibility rules. Downcasting is not allowed.Are these changes tested?
Yes!
Are there any user-facing changes?
No breaking changes, just new functionality