Skip to content
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

Inline coords to boost perf improvements #812

Merged
merged 1 commit into from
Apr 23, 2022
Merged

Conversation

nyurik
Copy link
Member

@nyurik nyurik commented Apr 18, 2022

  • Inline all simple coordinate methods
  • use new() method in some macros

Performance improvements

test improvement
concave hull f32 -1.8840%
point outside polygon -4.9574%
Polygon Euclidean distance rotating calipers f64 -1.6313%
simplify vwp f32 -7.0465%

the rest were statistically unchanged

  • I agree to follow the project's code of conduct.
  • [ ] I added an entry to CHANGES.md if knowledge of this change could be valuable to users.

@nyurik nyurik force-pushed the prep-coordinates branch 2 times, most recently from bae9026 to 6e3ba30 Compare April 18, 2022 05:09
@nyurik nyurik changed the title Bump proj, perf improvements Inline coords to boost perf improvements Apr 18, 2022
geo-types/src/coordinate.rs Outdated Show resolved Hide resolved
impl<T: CoordNum> From<(T, T)> for Coordinate<T> {
#[inline]
Copy link
Contributor

Choose a reason for hiding this comment

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

These are good inlines IMO! Thanks!

Copy link
Member

Choose a reason for hiding this comment

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

Does #[inline] even do much for generic code?

Copy link
Member Author

Choose a reason for hiding this comment

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

@lnicola that's a good question - but would require too substantial of a research to pinpoint why adding these inlines boosts performance. I think it is far easier to just add inline everywhere it seems relevant (tiny code, frequent use).

* Inline all simple coordinate methods
* use `new()` method in some macros

test | improvement
---|---
concave hull f32 | -1.8840%
point outside polygon | -4.9574%
Polygon Euclidean distance rotating calipers f64 | -1.6313%
simplify vwp f32 | -7.0465%

the rest were statistically unchanged
@nyurik nyurik requested a review from rmanoka April 19, 2022 17:52
nyurik added a commit to nyurik/geo that referenced this pull request Apr 21, 2022
This PR includes georust#772 and georust#812

---

This PR focuses on `geo-types` only. This is a part of georust#742.

This PR changes the underlying geo-type data structures to support 3D data and measurement values (M and Z values). The PR attempts to cause relatively minor disruptions to the existing users (see breaking changes below). My knowledge of the actual geo algorithms is limited, so please ping me for any specific algo change.

## geo-types restructuring
All geo type structs have been renamed from `Foo<T>(...)` to `Foo<T, Z=NoValue, M=NoValue>(...)`, and several type aliases were added:

```rust
// old
pub struct LineString<T: CoordNum>(pub Vec<Coordinate<T>>);

// new
pub struct LineString<T: CoordNum, Z: ZCoord=NoValue, M: Measure=NoValue>(pub Vec<Coordinate<T, Z, M>>);

pub type LineStringM<T, M=T> = LineString<T, NoValue, M>;
pub type LineString3D<T> = LineString<T, T, NoValue>;
pub type LineString3DM<T, M=T> = LineString<T, T, M>;
```

## NoValue magic
`NoValue` is an empty struct that behaves just like a number. It supports all math and comparison operations. This means that a `Z` or `M` value can be manipulated without checking if it is actually there.  This code works for Z and M being either a number or a NoValue:

```rust
pub struct NoValue;

impl<T: CoordNum, Z: ZCoord, M: Measure> Sub for Coordinate<T, Z, M> {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self {
        coord! {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
            z: self.z - rhs.z,
            m: self.m - rhs.m,
        }
    }
}
```

## Variant algorithm implementations
Function implementations can keep just the original 2D `<T>` variant, or add support for 3D `<Z>` and/or the Measure `<M>`. The above example works for all combinations of objects. This function only works for 2D objects with and without the Measure.

```rust
impl<T: CoordNum, M: Measure> Line<T, NoValue, M> {
    /// Calculate the slope (Δy/Δx).
    pub fn slope(&self) -> T {
        self.dy() / self.dx()
    }
}
```

## Breaking changes
* It is no longer possible to create Coordinate with just `x` and `y` values using implicit tuple constructor. Use `coord!` instead.
nyurik added a commit to nyurik/geo that referenced this pull request Apr 21, 2022
This PR includes georust#772 and georust#812

---

This PR focuses on `geo-types` only. This is a part of georust#742.

This PR changes the underlying geo-type data structures to support 3D data and measurement values (M and Z values). The PR attempts to cause relatively minor disruptions to the existing users (see breaking changes below). My knowledge of the actual geo algorithms is limited, so please ping me for any specific algo change.

## geo-types restructuring
All geo type structs have been renamed from `Foo<T>(...)` to `Foo<T, Z=NoValue, M=NoValue>(...)`, and several type aliases were added:

```rust
// old
pub struct LineString<T: CoordNum>(pub Vec<Coordinate<T>>);

// new
pub struct LineString<T: CoordNum, Z: ZCoord=NoValue, M: Measure=NoValue>(pub Vec<Coordinate<T, Z, M>>);

pub type LineStringM<T, M=T> = LineString<T, NoValue, M>;
pub type LineString3D<T> = LineString<T, T, NoValue>;
pub type LineString3DM<T, M=T> = LineString<T, T, M>;
```

## NoValue magic
`NoValue` is an empty struct that behaves just like a number. It supports all math and comparison operations. This means that a `Z` or `M` value can be manipulated without checking if it is actually there.  This code works for Z and M being either a number or a NoValue:

```rust
pub struct NoValue;

impl<T: CoordNum, Z: ZCoord, M: Measure> Sub for Coordinate<T, Z, M> {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self {
        coord! {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
            z: self.z - rhs.z,
            m: self.m - rhs.m,
        }
    }
}
```

## Variant algorithm implementations
Function implementations can keep just the original 2D `<T>` variant, or add support for 3D `<Z>` and/or the Measure `<M>`. The above example works for all combinations of objects. This function only works for 2D objects with and without the Measure.

```rust
impl<T: CoordNum, M: Measure> Line<T, NoValue, M> {
    /// Calculate the slope (Δy/Δx).
    pub fn slope(&self) -> T {
        self.dy() / self.dx()
    }
}
```

## Breaking changes
* It is no longer possible to create Coordinate with just `x` and `y` values using implicit tuple constructor. Use `coord!` instead.
@nyurik
Copy link
Member Author

nyurik commented Apr 21, 2022

Note that the change requested by @rmanoka has been done (I simply removed the code in question) - i'm not sure if I should resolve it or if @rmanoka should click "ok"?

@rmanoka
Copy link
Contributor

rmanoka commented Apr 21, 2022

LGTM. Let's give it a couple of days for a few more eyes and merge it if no one has further suggestions

@michaelkirk
Copy link
Member

bors r+

@bors
Copy link
Contributor

bors bot commented Apr 23, 2022

Build succeeded:

@bors bors bot merged commit 68f6e84 into georust:main Apr 23, 2022
@nyurik nyurik deleted the prep-coordinates branch July 31, 2024 23:17
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.

5 participants