Skip to content

2D Spatial Tree Performance Benchmarks

TL;DR: What Problem This Solves

  • Fast range/bounds/nearest‑neighbor queries on 2D data without scanning everything.
  • Quick picks: QuadTree2D for broad‑phase; KdTree2D (Balanced) for NN; KdTree2D (Unbalanced) for fast rebuilds; RTree2D for bounds‑based data.

This document contains performance benchmarks for the 2D spatial tree implementations in Unity Helpers.

Available 2D Spatial Trees

  • QuadTree2D - Easiest to use, good all-around performance
  • KdTree2D - Balanced and unbalanced variants available
  • RTree2D - Optimized for bounding box queries

Correctness & Semantics

  • QuadTree2D and KdTree2D (balanced and unbalanced) guarantee the same results for the same input data and the same queries. They are both point-based trees and differ only in construction/query performance characteristics.
  • RTree2D is bounds-based (stores rectangles/AABBs), not points. Its spatial knowledge and query semantics operate on rectangles, so its results will intentionally differ for sized objects and bounds intersection queries.

Performance Benchmarks

Datasets

1,000,000 entries

Construction
Construction KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
1,000,000 entries2 (0.362s)5 (0.195s)1 (0.745s)3 (0.313s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=499.5)100989216
Half (~span/4) (r=249.8)40640940578
Quarter (~span/8) (r=124.9)1,6001,5941,644344
Tiny (~span/1000) (r=1)161,731159,685237,950147,807
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=999.0x999.0)31536232820
Half (size=499.5x499.5)1,7461,7231,772108
Quarter (size=249.8x249.8)6,7876,9166,997546
Unit (size=1)198,766194,827261,709152,855
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
500 neighbors16,77535,37426,2983,663
100 neighbors161,271131,713147,74918,238
10 neighbors506,799494,356261,77429,127
1 neighbor618,345606,530272,36429,740

100,000 entries

Construction
Construction KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
100,000 entries44 (0.023s)67 (0.015s)15 (0.064s)37 (0.026s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=199.5)1,0201,0041,021215
Half (~span/4) (r=99.75)2,2812,3062,341538
Quarter (~span/8) (r=49.88)7,8048,7049,3762,105
Tiny (~span/1000) (r=1)195,261196,197279,042194,307
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=399.0x249.0)4,4824,3794,487341
Half (size=199.5x124.5)11,22812,98214,7421,412
Quarter (size=99.75x62.25)31,18637,44643,4195,631
Unit (size=1)228,484225,721315,803205,613
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
500 neighbors23,57622,76824,2825,045
100 neighbors107,485190,99899,60817,425
10 neighbors481,852501,441292,28640,726
1 neighbor596,868616,172301,25443,377

10,000 entries

Construction
Construction KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
10,000 entries491 (0.002s)669 (0.001s)202 (0.005s)413 (0.002s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=49.50)10,05910,06310,0242,149
Half (~span/4) (r=24.75)38,19437,81639,6388,431
Quarter (~span/8) (r=12.38)70,93083,93999,25533,540
Tiny (~span/1000) (r=1)247,372246,625341,658226,037
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=99.00x99.00)44,81744,11344,3943,536
Half (size=49.50x49.50)164,390168,864172,05013,410
Quarter (size=24.75x24.75)98,908135,348171,08050,695
Unit (size=1)292,361283,248379,910236,640
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
500 neighbors30,99030,43529,9425,229
100 neighbors135,108123,954159,35223,612
10 neighbors495,078493,259327,23754,246
1 neighbor631,140530,323390,29961,864

1,000 entries

Construction
Construction KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
1,000 entries4,416 (0.000s)6,591 (0.000s)1,976 (0.001s)3,907 (0.000s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=24.50)95,71597,16997,91821,417
Half (~span/4) (r=12.25)94,861123,468119,43640,197
Quarter (~span/8) (r=6.13)147,619169,956181,77784,623
Tiny (~span/1000) (r=1)346,917348,862458,277320,193
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=49.00x19.00)432,109441,180465,70835,018
Half (size=24.50x9.5)209,301357,349363,098105,115
Quarter (size=12.25x4.75)334,684364,976466,838228,618
Unit (size=1)404,675390,143502,011337,746
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
500 neighbors38,04141,05139,0065,841
100 neighbors156,877152,010160,80524,720
10 neighbors568,521609,294405,81792,332
1 neighbor531,965641,229334,066106,525

100 entries

Construction
Construction KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
100 entries38,314 (0.000s)37,593 (0.000s)18,315 (0.000s)18,382 (0.000s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=4.5)707,466707,344698,430190,481
Half (~span/4) (r=2.25)579,526574,963734,467379,167
Quarter (~span/8) (r=1.13)580,850585,912742,405427,445
Tiny (~span/1000) (r=1)576,125583,627744,381429,480
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=9x9)1,564,4741,567,5971,584,871282,253
Half (size=4.5x4.5)641,201644,062792,659419,895
Quarter (size=2.25x2.25)650,761653,857788,861438,508
Unit (size=1)647,476664,227787,188436,589
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
100 neighbors (max)191,767191,405180,671154,573
10 neighbors640,162540,897472,754265,960
1 neighbor658,223565,971517,711350,069

Interpreting the Results

All numbers represent operations per second (higher is better), except for construction times which show operations per second and absolute time.

Choosing the Right Tree

QuadTree2D:

  • Best for: General-purpose 2D spatial queries
  • Strengths: Balanced performance across all operation types, simple to use
  • Weaknesses: Slightly slower than KdTree for point queries

KdTree2D (Balanced):

  • Best for: When you need consistent query performance
  • Strengths: Fast nearest-neighbor queries, good for smaller datasets
  • Weaknesses: Slower construction time

KdTree2D (Unbalanced):

  • Best for: When you need fast construction and will rebuild frequently
  • Strengths: Fastest construction, similar query performance to balanced
  • Weaknesses: May degrade on pathological data distributions

RTree2D:

  • Best for: Bounding box queries, especially with large query areas
  • Strengths: Excellent for large bounding box queries, handles overlapping objects well
  • Weaknesses: Slower for point queries and small ranges

Important Notes

  • All spatial trees assume immutable positional data
  • If positions change, you must reconstruct the tree
  • Spatial queries are O(log n) vs O(n) for linear search
  • Construction cost is amortized over many queries