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.339s)6 (0.163s)4 (0.225s)3 (0.274s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=499.5)5959557
Half (~span/4) (r=249.8)23823120428
Quarter (~span/8) (r=124.9)945927813119
Tiny (~span/1000) (r=1)98,29399,231136,85998,699
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=999.0x999.0)27039435917
Half (size=499.5x499.5)1,7741,8121,22469
Quarter (size=249.8x249.8)6,8747,1573,820361
Unit (size=1)141,582145,100184,680101,563
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
500 neighbors8,17016,23012,17159,089
100 neighbors67,99866,04466,325123,678
10 neighbors203,190195,156145,400170,276
1 neighbor263,680261,576143,886176,123

100,000 entries

Construction
Construction KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
100,000 entries49 (0.020s)84 (0.012s)49 (0.020s)50 (0.020s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=199.5)60060158574
Half (~span/4) (r=99.75)1,3541,3241,202185
Quarter (~span/8) (r=49.88)4,6415,0364,274721
Tiny (~span/1000) (r=1)120,657120,129168,546130,169
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=399.0x249.0)4,4944,5084,609236
Half (size=199.5x124.5)9,41711,7397,955957
Quarter (size=99.75x62.25)25,01831,75619,4443,787
Unit (size=1)172,229173,621226,206136,957
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
500 neighbors9,7469,64911,27959,259
100 neighbors45,03578,37548,623141,809
10 neighbors224,805201,634161,243191,622
1 neighbor204,442272,235186,148199,245

10,000 entries

Construction
Construction KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
10,000 entries547 (0.002s)205 (0.005s)534 (0.002s)510 (0.002s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=49.50)5,8355,9235,910716
Half (~span/4) (r=24.75)22,30922,36513,4922,851
Quarter (~span/8) (r=12.38)43,60050,53136,92311,864
Tiny (~span/1000) (r=1)158,469150,623212,018149,034
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=99.00x99.00)44,42744,16045,6292,400
Half (size=49.50x49.50)138,733137,56336,7649,208
Quarter (size=24.75x24.75)71,42299,96973,18834,689
Unit (size=1)217,195215,448288,202161,586
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
500 neighbors12,73712,61613,94655,527
100 neighbors55,27449,31378,099148,808
10 neighbors227,411158,790169,058209,840
1 neighbor223,606265,422200,809221,366

1,000 entries

Construction
Construction KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
1,000 entries5,151 (0.000s)7,776 (0.000s)4,694 (0.000s)4,555 (0.000s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=24.50)56,99556,23055,8967,058
Half (~span/4) (r=12.25)59,11274,55955,97913,922
Quarter (~span/8) (r=6.13)92,658104,92092,48635,680
Tiny (~span/1000) (r=1)222,202220,277297,368206,152
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=49.00x19.00)426,624431,008373,81323,621
Half (size=24.50x9.5)156,916256,359117,82471,241
Quarter (size=12.25x4.75)246,321255,452181,493157,622
Unit (size=1)299,240298,725399,346236,769
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
500 neighbors41,17342,77836,59459,730
100 neighbors68,90867,18674,762162,066
10 neighbors229,540233,936192,974227,546
1 neighbor306,128230,085193,384228,793

100 entries

Construction
Construction KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
100 entries39,370 (0.000s)22,675 (0.000s)14,641 (0.000s)21,231 (0.000s)
Elements In Range
Elements In Range KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (~span/2) (r=4.5)424,908350,023429,77668,536
Half (~span/4) (r=2.25)368,298327,663232,135198,728
Quarter (~span/8) (r=1.13)361,799333,252484,465270,330
Tiny (~span/1000) (r=1)366,765339,049491,007271,447
Get Elements In Bounds
Get Elements In Bounds KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
Full (size=9x9)1,213,3051,199,0911,381,012193,676
Half (size=4.5x4.5)366,200390,340324,182296,281
Quarter (size=2.25x2.25)389,918419,365622,169304,900
Unit (size=1)377,223449,661606,231303,985
Approximate Nearest Neighbors
Approximate Nearest Neighbors KDTree2D (Balanced) KDTree2D (Unbalanced) QuadTree2D RTree2D
100 neighbors (max)85,200110,718130,989171,955
10 neighbors175,016222,050233,907270,041
1 neighbor178,877289,379231,762284,316

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