Skip to content

Stack allocate 2d points using tiny_vec. - #222

Merged
michaelkirk merged 1 commit into
georust:mainfrom
michaelkirk:mkirk/tiny-vec
Jan 12, 2026
Merged

michaelkirk merged 1 commit into
georust:mainfrom
michaelkirk:mkirk/tiny-vec

Conversation

@michaelkirk

@michaelkirk michaelkirk commented Apr 6, 2023 •

Copy link
Copy Markdown
Member
  • 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.

Position, which is the fundamental unit for all the various geojson geometries, is currently represented as a (heap allocated) Vec. This PR swaps it out to be a TinyVec which can be stack allocated up to a certain number of elements, before moving to the heap.

This PR also makes that fact private so we can make similar changes in the future in a non-breaking way.

Unlike #218, this branch maintains the ability to have n-dimensional points, building upon the branch I started in this comment.

This is a breaking change (maybe a substantial one for people relying on the Position type, but shouldn't affect people who are only using the high level APIs.). Switching to tiny_vec will be breaking for these people regardless, and the upside to making the struct opaque like this is that we can now more freely muck about with the internals, e.g. if we one day want to replace tiny_vec, or add a feature flag which allocates 3d stack allocations rather than 2, etc.

We could add a macro like geojson::position![1.0, 3.0] if people think the Position::from([1.0, 3.0]) is too verbose.

What do people think? I've wanted to do something like this for a long time, but kind of avoided it because it's a breaking change, and going to be annoying for anyone who was accessing the Position type directly.

I've tried to alleviate this a bit by implementing Index/Mut on Position. Are there other things I could do to make it break less? If you have a crate that uses geojson, I'd love to have some test cases to integrate this against to see how annoying it is.

bench improvements look similar, but slightly less than #218
$ cargo bench --bench="*" -- --baseline=main                                                                                                                                                                                           
   Compiling geojson v0.24.2 (/Users/mkirk/src/georust/geojson)                                                                                                                                                                        
    Finished `bench` profile [optimized] target(s) in 2.46s                                                                                                                                                                            
     Running benches/parse.rs (target/release/deps/parse-b354b8f499ffa7c3)                                                                                                                                                             
Gnuplot not found, using plotters backend                                                                                                                                                                                              
Benchmarking parse (countries.geojson): Warming up for 3.0000 s                                                                                                                                                                        
Warning: Unable to complete 100 samples in 5.0s. You may wish to increase target time to 7.1s, enable flat sampling, or reduce sample count to 50.                                                                                     
parse (countries.geojson)                                                                                                                                                                                                              
                        time:   [1.3919 ms 1.3952 ms 1.3985 ms]                                                                                                                                                                        
                        change: [-9.3613% -9.1041% -8.8765%] (p = 0.00 < 0.05)                                                                                                                                                         
                        Performance has improved.                                                                                                                                                                                      
Found 1 outliers among 100 measurements (1.00%)                                                                                                                                                                                        
  1 (1.00%) high mild                                                                                                                                                                                                                  
                                                                                                                                                                                                                                       
FeatureReader::features (countries.geojson)                                                                                                                                                                                            
                        time:   [5.1616 ms 5.1709 ms 5.1805 ms]                                                                                                                                                                        
                        change: [-3.7817% -3.4742% -3.1773%] (p = 0.00 < 0.05)                                                                                                                                                         
                        Performance has improved.                                                                                                                                                                                      
Found 1 outliers among 100 measurements (1.00%)                                                                                                                                                                                        
  1 (1.00%) high mild                                                                                                                                                                                                                  
                                                                                                                                                                                                                                       
FeatureReader::deserialize (countries.geojson)                                                                                                                                                                                         
                        time:   [6.2291 ms 6.2855 ms 6.3710 ms]                                                                                                                                                                        
                        change: [-8.4857% -7.0482% -5.4736%] (p = 0.00 < 0.05)                                                                                                                                                         
                        Performance has improved.                                                                                                                                                                                      
Found 13 outliers among 100 measurements (13.00%)                                                                                                                                                                                      
  4 (4.00%) high mild                                                                                                                                                                                                                  
  9 (9.00%) high severe                                                                                                                                                                                                                
                                                                                                                                                                                                                                       
FeatureReader::deserialize to geo-types (countries.geojson)                                                                                                                                                                            
                        time:   [6.2456 ms 6.2583 ms 6.2717 ms]                                                                                                                                                                        
                        change: [-4.2513% -3.9479% -3.6463%] (p = 0.00 < 0.05)                                                                                                                                                         
                        Performance has improved.                                                                                                                                                                                      
Found 6 outliers among 100 measurements (6.00%)                                                                                                                                                                                        
  2 (2.00%) low mild                                                                                                                                                                                                                   
  3 (3.00%) high mild                                                                                                                                                                                                                  
  1 (1.00%) high severe                                                                                                                                                                                                                
                                                                                                                                                                                                                                       
parse (geometry_collection.geojson)                                                                                                                                                                                                    
                        time:   [12.691 µs 12.717 µs 12.743 µs]                                                                                                                                                                        
                        change: [-5.6392% -5.3164% -5.0075%] (p = 0.00 < 0.05)                                                                                                                                                         
                        Performance has improved.                                                                                                                                                                                      
Found 4 outliers among 100 measurements (4.00%)
  2 (2.00%) high mild
  2 (2.00%) high severe

     Running benches/serialize.rs (target/release/deps/serialize-a9964390908de1d7)
Gnuplot not found, using plotters backend
serialize geojson::FeatureCollection struct (countries.geojson)
                        time:   [437.78 µs 440.68 µs 445.22 µs]
                        change: [-3.2203% -1.9939% -0.2756%] (p = 0.01 < 0.05)
                        Change within noise threshold.
Found 5 outliers among 100 measurements (5.00%)
  1 (1.00%) high mild
  4 (4.00%) high severe

serialize custom struct (countries.geojson)
                        time:   [913.53 µs 915.94 µs 918.12 µs]
                        change: [-11.700% -11.131% -10.615%] (p = 0.00 < 0.05)
                        Performance has improved.

serialize custom struct to geo-types (countries.geojson)
                        time:   [1.0228 ms 1.0247 ms 1.0266 ms]
                        change: [-18.538% -18.148% -17.790%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 2 outliers among 100 measurements (2.00%)
  1 (1.00%) high mild
  1 (1.00%) high severe

     Running benches/to_geo_types.rs (target/release/deps/to_geo_types-02cd8c6bf0d8fdf0)
Gnuplot not found, using plotters backend
Convert to geo-types    time:   [55.782 µs 55.926 µs 56.059 µs]
                        change: [-75.690% -75.627% -75.565%] (p = 0.00 < 0.05)
                        Performance has improved.

@michaelkirk

michaelkirk commented Apr 6, 2023 •

Copy link
Copy Markdown
Member Author

Another note, though this greatly speeds up (change: [-71.144% -71.004% -70.873%]) converting already parsed geojson to other formats, this doesn't seem to affect the actual parsing speeds all that much.

I'm not 100%, but I think this is because our parsing is still mostly building up a serde_json::JsonValue per Position, which creates a Vec anyway. I think with these stack allocatable Positions, we'll unlock some good reason to revisit our parsing code.

@michaelkirk
michaelkirk force-pushed the mkirk/tiny-vec branch 4 times, most recently from dc9a894 to d1e3dec Compare April 6, 2023 18:58
@michaelkirk
michaelkirk marked this pull request as draft April 6, 2023 19:19
@urschrei

Copy link
Copy Markdown
Member

As we did before, we could have a look to see who might be relying on Position using GH Code Search and give them a heads up. I would be surprised if there are many explicit uses, but who knows. Given the perf win, I think it's worth the potential downstream inconvenience, especially with the (potential) macro and Index* traits.

@michaelkirk
michaelkirk force-pushed the mkirk/tiny-vec branch 2 times, most recently from b8ce67c to 425bf00 Compare January 8, 2026 22:20
@michaelkirk
michaelkirk marked this pull request as ready for review January 8, 2026 22:25
@michaelkirk
michaelkirk force-pushed the mkirk/tiny-vec branch 2 times, most recently from bd9ce7b to 7425d48 Compare January 8, 2026 22:48
@michaelkirk

michaelkirk commented Jan 8, 2026 •

Copy link
Copy Markdown
Member Author

After 2.5 years of meditation I have rebased, and I think we should merge this.

There will be some breakage for those of our users interacting with Position directly: https://github.com/search?q=%22geojson%3A%3APosition%22+language%3Arust&type=code

It seems like most users are using higher levels of abstraction, like the Geometry or Feature api and converting to geo-types or what-have-you. They should be unaffected.

In particular I noticed:
https://github.com/galileo-map/galileo (/cc @Maximkaaa)
https://github.com/mapbox/Hecate (no updates in 7 years)

Happy to hear from any users if they have input on this breaking change in exchange for faster processing. Note I think this is a prerequisite for #223 which unlocks more performance gains.

edited: to clarify that I think most users will be unaffected.

Comment thread CHANGES.md
* BREAKING: Position is now a new type, rather than a type alias for Vec.
The new type allows for faster handling of GeoJSON in the common (2-D) case by avoid per-coordinate heap allocations.
```
# BEFORE: Position *was* a Vec

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

👏

@urschrei

urschrei commented Jan 8, 2026

Copy link
Copy Markdown
Member

Excellent. As I said in [checks] 2023, this is a great idea.

@Maximkaaa

Copy link
Copy Markdown

@michaelkirk Thank for the ping. This will not be a problem. We can just update the version of geojson crate and fix any incompatabilities on our next release.

…` with a tiny_vec rather than a Vec.

Breaking: Position becomes an opaque struct rather than a (public) type alias.
@michaelkirk
michaelkirk enabled auto-merge January 12, 2026 20:10
@michaelkirk
michaelkirk added this pull request to the merge queue Jan 12, 2026
Merged via the queue into georust:main with commit 44d45dc Jan 12, 2026
3 checks passed
@michaelkirk
michaelkirk deleted the mkirk/tiny-vec branch January 12, 2026 20:13
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.

3 participants