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 entries4 (0.247s)2 (0.346s)4 (0.223s)2 (0.379s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=499.5)5856567
Half (~span/4) (r=249.8)23421521428
Quarter (~span/8) (r=124.9)909795785117
Tiny (~span/1000) (r=1)8,2805,5656,9736,724
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=999.0x999.0)33435531416
Half (size=499.5x499.5)1,3561,3671,00566
Quarter (size=249.8x249.8)3,1483,1832,281322
Unit (size=1)5,6125,6065,5772,834
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
500 neighbors3,0231,7332,1481,855
100 neighbors2,8281,8632,3081,802
10 neighbors1,9581,8991,6411,692
1 neighbor1,9611,9571,4641,465

100,000 entries

Construction
Construction KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
100,000 entries49 (0.020s)77 (0.013s)48 (0.021s)43 (0.023s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=199.5)53952753671
Half (~span/4) (r=99.75)1,0651,0731,012171
Quarter (~span/8) (r=49.88)2,4942,7102,448577
Tiny (~span/1000) (r=1)5,6075,6165,6602,872
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=399.0x249.0)2,4622,5242,549209
Half (size=199.5x124.5)3,5633,9013,304674
Quarter (size=99.75x62.25)4,7264,9344,5191,550
Unit (size=1)5,6255,6585,6612,850
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
500 neighbors1,5621,6121,2361,407
100 neighbors1,8611,8701,3971,439
10 neighbors1,9421,9041,4561,464
1 neighbor1,9111,9121,4611,462

10,000 entries

Construction
Construction KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
10,000 entries541 (0.002s)818 (0.001s)541 (0.002s)322 (0.003s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=49.50)2,9502,9512,873579
Half (~span/4) (r=24.75)4,6314,5484,0921,428
Quarter (~span/8) (r=12.38)5,1465,1645,0322,339
Tiny (~span/1000) (r=1)5,5815,6465,6802,865
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=99.00x99.00)5,0875,1675,1811,288
Half (size=49.50x49.50)5,6225,6525,0102,185
Quarter (size=24.75x24.75)5,4355,5615,4142,687
Unit (size=1)5,6995,7095,7492,817
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
500 neighbors1,6811,6961,3041,369
100 neighbors1,8931,8881,3871,445
10 neighbors1,9611,9551,4131,470
1 neighbor1,9711,9171,4221,471

1,000 entries

Construction
Construction KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
1,000 entries5,376 (0.000s)7,429 (0.000s)4,940 (0.000s)889 (0.001s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=24.50)5,3705,3675,3482,032
Half (~span/4) (r=12.25)5,3335,4215,1992,425
Quarter (~span/8) (r=6.13)5,5415,5675,4332,713
Tiny (~span/1000) (r=1)5,7025,6195,7402,874
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=49.00x19.00)5,7475,6905,8002,590
Half (size=24.50x9.5)5,5675,7755,6292,806
Quarter (size=12.25x4.75)5,6295,7395,6982,842
Unit (size=1)5,7295,7535,7512,868
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
500 neighbors1,8621,8711,3861,412
100 neighbors1,9001,8931,4181,401
10 neighbors1,9591,9651,4681,428
1 neighbor1,9701,9491,4341,434

100 entries

Construction
Construction KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
100 entries43,859 (0.000s)40,650 (0.000s)26,954 (0.000s)1,682 (0.001s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=4.5)5,8625,8295,8112,816
Half (~span/4) (r=2.25)5,7765,7345,6882,886
Quarter (~span/8) (r=1.13)5,7575,7235,7662,904
Tiny (~span/1000) (r=1)5,7275,5835,7652,904
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=9x9)5,8465,8215,9102,817
Half (size=4.5x4.5)5,7425,7915,7092,848
Quarter (size=2.25x2.25)5,7535,6865,7622,907
Unit (size=1)5,6845,7025,8352,898
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
100 neighbors (max)1,8691,9181,4471,447
10 neighbors1,9501,9601,4761,480
1 neighbor1,9681,9551,4751,478

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