-
Notifications
You must be signed in to change notification settings - Fork 1.5k
chore: fix clippy::large_enum_variant for DataFusionError #15861
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
chore: fix clippy::large_enum_variant for DataFusionError #15861
Conversation
From the guidelines this should also have the "api-change" label but I don't think I have permission to add it. |
@@ -59,7 +59,7 @@ pub enum DataFusionError { | |||
ParquetError(ParquetError), | |||
/// Error when reading Avro data. | |||
#[cfg(feature = "avro")] | |||
AvroError(AvroError), | |||
AvroError(Box<AvroError>), |
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.
wondering why now the error needs to be boxed? 🤔
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 don't have the specific numbers but here's a verbose explanation of what the lint is trying to tell us about:
enum MyErrorType {
Something,
SomethingElse([u8; 4096])
}
We can see here that the size of MyErrorType
is 4096 (ish)
fn try_thing() -> Result<usize, MyErrorType> {
...
}
Consequently the size of Result<T, MyErrorType>
is always at least 4096. Result<usize, MyErrorType>
has size 4096. To call try_thing
we have to allocate memory to hold that size of result. We don't return the usize
in a register. And most of the time we expect to see Ok
rather than Err
so this makes the common case a lot worse.
Boxing the large variants fixes the issue since it reduces the size of the variant to a pointer, which fits in a register.
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.
Should we box other error type too?
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'd favor that. There might be another error variant which is less than the clippy threshold size but is still large enough to prevent certain optimizations. If all the error variants were boxed then we could be confident that DataFusionError could be copied around in registers and then perhaps Result<T, DataFusionError>
also could be for small T
.
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 personally suggest avoiding additional API churn (aka boxing all varaints) unless there is some particular problem we are trying to solve or improve performance in some way we can measure
Thanks @rroelke Btw we do box here |
I just checked AvroError, its actually the wrapper of apache-avro internal and some variants can be pretty large. to avoid possible stackoverflow it make sense moving it to heap like clippy suggested 🤔 btw we dont have a good bench against avro. Lets go with clippy suggestion here, although it is |
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.
lgtm thanks @rroelke for the first contribuion, since this is first contribution I need second pair of eyes, @jayzhan211 would you mind to help with review?
https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant Based on the description, I think we box is when
I think this error is not rarely used so I think it is better we have number shows this helps performance |
From the lint description:
That is to say, the presence of the large variant AvroError affects the whole layout of DataFusionError. Transitively, the presence of the large variant AvroError affects the whole layout of This related lint pull request elaborates more specifically:
As applied here:
Indeed DataFusionError is used nearly everywhere which is precisely the point. Whereas the DataFusion::AvroError is only produced by the avro reader but it affects every place where DataFusionError can appear. |
How about we convert the error into the string and wrap with other datafusion error, so we can avoid this variant entirely |
IMO we should not convert errors to strings at all, downstream library or application code should not be asked to possibly parse error messages. I would be happy to submit a follow-up PR to add a new variant wrapping But I don't see a reason that this PR should pivot in that direction. Can you articulate your objection to the current diff more precisely please? |
@@ -59,7 +59,7 @@ pub enum DataFusionError { | |||
ParquetError(ParquetError), | |||
/// Error when reading Avro data. | |||
#[cfg(feature = "avro")] | |||
AvroError(AvroError), | |||
AvroError(Box<AvroError>), |
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 personally suggest avoiding additional API churn (aka boxing all varaints) unless there is some particular problem we are trying to solve or improve performance in some way we can measure
Thank you @rroelke @comphead and @jayzhan211 |
Thanks everyone. |
Which issue does this PR close?
clippy::large_enum_variant
flags all uses ofResult<T, DataFusionError>
withfeatures = ["avro"]
#15860.Rationale for this change
Fixes
clippy::large_enum_variant
which has been enabled by default on nightly. Not only does it flag a problem when compiling withfeatures = ["avro"]
, it also propagates a lot of lint to downstream projects which useResult<T, DataFusionError>
as a return type.What changes are included in this PR?
The
DataFusionError::AvroError(AvroError)
variant is changed toDataFusionError::AvroError(Box<AvroError>)
.Are these changes tested?
Regression testing will validate that error messages from Avro are preserved.
I have tested using the reproducer from #15860 to verify that this change removes the lint. To add a regression test to verify this would require changes to project configuration or CI. I will implement this if requested but didn't see the point in putting in up-front effort.
Are there any user-facing changes?
Users invoking the
DataFusionError::AvroError
constructor directly must update their code to eitherDataFusionError::AvroError(Box::new(my_avro_error))
orDataFusionError::from(my_avro_error)
.