-
Notifications
You must be signed in to change notification settings - Fork 101
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
comparison of integral Quantities is very slow #54
Comments
So I hacked up my local fork of uom to have Eq and PartialEq that look like this: impl<D, Ul, V> $crate::lib::cmp::PartialEq for Quantity<D, Ul, V>
where
D: Dimension + ?Sized,
Ul: Units<V> + ?Sized,
V: $crate::num::Num + $crate::Conversion<V>,
{
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
// More methods elided
}
impl<D, Ul, V> $crate::lib::cmp::PartialOrd for Quantity<D, Ul, V>
where
D: Dimension + ?Sized,
Ul: Units<V> + ?Sized,
V: $crate::num::Num + $crate::Conversion<V> + $crate::lib::cmp::PartialOrd,
{
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<$crate::lib::cmp::Ordering>
{
self.value.partial_cmp(&other.value)
}
// More methods elided
} And my runtime (of course) goes back to normal (and no changes were required in my own codebase, since I didn't rely on inter-type comparisons). Do you think it's reasonable, or out of the question, to have an argument to |
This is also going to affect other operations like Using a non-default Cargo feature and duplicating #[cfg(all(feature = "unstable", feature = "simple-impl"))]
impl<D, U, V> PartialOrd for Quantity<D, U, V> { ... }
#[cfg(not(feature = "simple-impl"))]
impl<D, Ul, Ur, V> PartialOrd<Quantity<D, Ur, V>> for Quantity<D, Ul, V> { ... } If you have any other ideas let me know. If you want to put together a PR I'll accept it. |
Sadly I think using a cargo feature to make this change would break the rule of "cargo features should only be additive" -- for example see this unfortunate situation involving the I guess I can just keep using my fork until specialization happens but I guess it's going to be a long time before this gets solved for a large audience :( |
Good point in regards to features only being additive. What about keeping the current storage_types! {
types: Float;
impl<D, Ul, Ur> PartialOrd<Quantity<D, Ur, V>> for Quantity<D, Ul, V> { ... }
}
storage_types! {
types: PrimInt, BigInt, ...;
impl<D, U> PartialOrd for Quantity<D, U, V> { ... }
} |
That would ease my performance problems in uom, but it seems like a really surprising design -- having such different behavior between integers and floats. I guess at this point these performance problems have caused me to develop a bias towards completely disabling auto-conversion everywhere (it was a surprise to me anyways, since "zero-cost" is in the first sentence of the description of uom). But of course, I'm just a new user of your library; I really don't want to show up and start complaining about how it works! These are just my experiences. All that aside, any way for me to avoid auto-conversion on basic ops would solve my problems. |
Thinking about this one more the feature idea could work by making the absence of the feature just have |
Oh, yes! And it could be enabled by default, which would make it backwards compatible, I think...? Great idea! I can try implementing this this weekend. |
Fixes iliekturtles#54 autoconvert is enabled by default, but when it is disabled, trait implementations for binary operations do not support mixing different types of Quantity (of the same measurement units). This allows performance-sensitive code to avoid the potentially costly conversions done during these operations even when the same types are on both sides of the operation.
Well, backwards compatible for people who don't have |
Fixes iliekturtles#54 autoconvert is enabled by default, but when it is disabled, trait implementations for binary operations do not support mixing different types of Quantity (of the same measurement units). This allows performance-sensitive code to avoid the potentially costly conversions done during these operations even when the same types are on both sides of the operation.
Fixes iliekturtles#54 autoconvert is enabled by default, but when it is disabled, trait implementations for binary operations do not support mixing different types of Quantity (of the same measurement units). This allows performance-sensitive code to avoid the potentially costly conversions done during these operations even when the same types are on both sides of the operation.
@radix Did you still want to finish this effort or would you mind if I picked up where you left off? |
Hey @iliekturtles ! sorry I dropped the ball on this. As I recall, I got stuck in my efforts when I started thinking about how to support explicit conversion when autoconvert is off. I can't really remember how far I got. I believe some unit tests are failing depending on the features that are enabled. Here's the branch I was working on: https://github.com/radix/uom/commits/optional-autoconvert |
Fixes #54 autoconvert is enabled by default, but when it is disabled, trait implementations for binary operations do not support mixing different types of Quantity (of the same measurement units). This allows performance-sensitive code to avoid the potentially costly conversions done during these operations even when the same types are on both sides of the operation.
Fixes #54 autoconvert is enabled by default, but when it is disabled, trait implementations for binary operations do not support mixing different types of Quantity (of the same measurement units). This allows performance-sensitive code to avoid the potentially costly conversions done during these operations even when the same types are on both sides of the operation.
Fixes #54 autoconvert is enabled by default, but when it is disabled, trait implementations for binary operations do not support mixing different types of Quantity (of the same measurement units). This allows performance-sensitive code to avoid the potentially costly conversions done during these operations even when the same types are on both sides of the operation.
I've been busy with other things too. The |
@iliekturtles I'll take a look now, thanks! |
@iliekturtles it looks good to me. as I recall I was also thinking about how to implement |
Thanks for taking a look. I plan to do my final review tomorrow and merge into A custom |
The short version: comparing integral quantities with Eq can be many orders of magnitude slower than regular comparison. This is magnified for the bigger types, with i64 being about a few thousand times slower than comparing plain i64 values. I assume the same happens for the
PartialOrd
implementation.Here's the benchmark:
By my reading of the code, this stems from the fact that Quantities that aren't from the same
ISQ!
are compatible with each other -- but at the cost of having to perform a conversion, apparently usingnum-rational
. This is something I would be happy to forego - if uom's Eq implementation had aRhs = Self
, it could just perform a simple comparison. Perhaps a flag to theISQ!
macro could turn off this conversion generation and only support comparison of identical types?The text was updated successfully, but these errors were encountered: