-
Notifications
You must be signed in to change notification settings - Fork 12
Performance Spatial Tree 2d Performance
github-actions[bot] edited this page May 8, 2026
·
9 revisions
- 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.
- QuadTree2D - Easiest to use, good all-around performance
- KdTree2D - Balanced and unbalanced variants available
- RTree2D - Optimized for bounding box queries
- 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.
| Construction | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| 1,000,000 entries | 2 (0.339s) | 6 (0.163s) | 4 (0.225s) | 3 (0.274s) |
| Elements In Range | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| Full (~span/2) (r=499.5) | 59 | 59 | 55 | 7 |
| Half (~span/4) (r=249.8) | 238 | 231 | 204 | 28 |
| Quarter (~span/8) (r=124.9) | 945 | 927 | 813 | 119 |
| Tiny (~span/1000) (r=1) | 98,293 | 99,231 | 136,859 | 98,699 |
| Get Elements In Bounds | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| Full (size=999.0x999.0) | 270 | 394 | 359 | 17 |
| Half (size=499.5x499.5) | 1,774 | 1,812 | 1,224 | 69 |
| Quarter (size=249.8x249.8) | 6,874 | 7,157 | 3,820 | 361 |
| Unit (size=1) | 141,582 | 145,100 | 184,680 | 101,563 |
| Approximate Nearest Neighbors | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| 500 neighbors | 8,170 | 16,230 | 12,171 | 59,089 |
| 100 neighbors | 67,998 | 66,044 | 66,325 | 123,678 |
| 10 neighbors | 203,190 | 195,156 | 145,400 | 170,276 |
| 1 neighbor | 263,680 | 261,576 | 143,886 | 176,123 |
| Construction | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| 100,000 entries | 49 (0.020s) | 84 (0.012s) | 49 (0.020s) | 50 (0.020s) |
| Elements In Range | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| Full (~span/2) (r=199.5) | 600 | 601 | 585 | 74 |
| Half (~span/4) (r=99.75) | 1,354 | 1,324 | 1,202 | 185 |
| Quarter (~span/8) (r=49.88) | 4,641 | 5,036 | 4,274 | 721 |
| Tiny (~span/1000) (r=1) | 120,657 | 120,129 | 168,546 | 130,169 |
| Get Elements In Bounds | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| Full (size=399.0x249.0) | 4,494 | 4,508 | 4,609 | 236 |
| Half (size=199.5x124.5) | 9,417 | 11,739 | 7,955 | 957 |
| Quarter (size=99.75x62.25) | 25,018 | 31,756 | 19,444 | 3,787 |
| Unit (size=1) | 172,229 | 173,621 | 226,206 | 136,957 |
| Approximate Nearest Neighbors | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| 500 neighbors | 9,746 | 9,649 | 11,279 | 59,259 |
| 100 neighbors | 45,035 | 78,375 | 48,623 | 141,809 |
| 10 neighbors | 224,805 | 201,634 | 161,243 | 191,622 |
| 1 neighbor | 204,442 | 272,235 | 186,148 | 199,245 |
| Construction | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| 10,000 entries | 547 (0.002s) | 205 (0.005s) | 534 (0.002s) | 510 (0.002s) |
| Elements In Range | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| Full (~span/2) (r=49.50) | 5,835 | 5,923 | 5,910 | 716 |
| Half (~span/4) (r=24.75) | 22,309 | 22,365 | 13,492 | 2,851 |
| Quarter (~span/8) (r=12.38) | 43,600 | 50,531 | 36,923 | 11,864 |
| Tiny (~span/1000) (r=1) | 158,469 | 150,623 | 212,018 | 149,034 |
| Get Elements In Bounds | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| Full (size=99.00x99.00) | 44,427 | 44,160 | 45,629 | 2,400 |
| Half (size=49.50x49.50) | 138,733 | 137,563 | 36,764 | 9,208 |
| Quarter (size=24.75x24.75) | 71,422 | 99,969 | 73,188 | 34,689 |
| Unit (size=1) | 217,195 | 215,448 | 288,202 | 161,586 |
| Approximate Nearest Neighbors | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| 500 neighbors | 12,737 | 12,616 | 13,946 | 55,527 |
| 100 neighbors | 55,274 | 49,313 | 78,099 | 148,808 |
| 10 neighbors | 227,411 | 158,790 | 169,058 | 209,840 |
| 1 neighbor | 223,606 | 265,422 | 200,809 | 221,366 |
| Construction | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| 1,000 entries | 5,151 (0.000s) | 7,776 (0.000s) | 4,694 (0.000s) | 4,555 (0.000s) |
| Elements In Range | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| Full (~span/2) (r=24.50) | 56,995 | 56,230 | 55,896 | 7,058 |
| Half (~span/4) (r=12.25) | 59,112 | 74,559 | 55,979 | 13,922 |
| Quarter (~span/8) (r=6.13) | 92,658 | 104,920 | 92,486 | 35,680 |
| Tiny (~span/1000) (r=1) | 222,202 | 220,277 | 297,368 | 206,152 |
| Get Elements In Bounds | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| Full (size=49.00x19.00) | 426,624 | 431,008 | 373,813 | 23,621 |
| Half (size=24.50x9.5) | 156,916 | 256,359 | 117,824 | 71,241 |
| Quarter (size=12.25x4.75) | 246,321 | 255,452 | 181,493 | 157,622 |
| Unit (size=1) | 299,240 | 298,725 | 399,346 | 236,769 |
| Approximate Nearest Neighbors | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| 500 neighbors | 41,173 | 42,778 | 36,594 | 59,730 |
| 100 neighbors | 68,908 | 67,186 | 74,762 | 162,066 |
| 10 neighbors | 229,540 | 233,936 | 192,974 | 227,546 |
| 1 neighbor | 306,128 | 230,085 | 193,384 | 228,793 |
| Construction | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| 100 entries | 39,370 (0.000s) | 22,675 (0.000s) | 14,641 (0.000s) | 21,231 (0.000s) |
| Elements In Range | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| Full (~span/2) (r=4.5) | 424,908 | 350,023 | 429,776 | 68,536 |
| Half (~span/4) (r=2.25) | 368,298 | 327,663 | 232,135 | 198,728 |
| Quarter (~span/8) (r=1.13) | 361,799 | 333,252 | 484,465 | 270,330 |
| Tiny (~span/1000) (r=1) | 366,765 | 339,049 | 491,007 | 271,447 |
| Get Elements In Bounds | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| Full (size=9x9) | 1,213,305 | 1,199,091 | 1,381,012 | 193,676 |
| Half (size=4.5x4.5) | 366,200 | 390,340 | 324,182 | 296,281 |
| Quarter (size=2.25x2.25) | 389,918 | 419,365 | 622,169 | 304,900 |
| Unit (size=1) | 377,223 | 449,661 | 606,231 | 303,985 |
| Approximate Nearest Neighbors | KDTree2D (Balanced) | KDTree2D (Unbalanced) | QuadTree2D | RTree2D |
|---|---|---|---|---|
| 100 neighbors (max) | 85,200 | 110,718 | 130,989 | 171,955 |
| 10 neighbors | 175,016 | 222,050 | 233,907 | 270,041 |
| 1 neighbor | 178,877 | 289,379 | 231,762 | 284,316 |
All numbers represent operations per second (higher is better), except for construction times which show operations per second and absolute time.
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
- 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
📦 Unity Helpers | 📖 Documentation | 🐛 Issues | 📜 MIT License
- Inspector Button
- Inspector Conditional Display
- Inspector Grouping Attributes
- Inspector Inline Editor
- Inspector Overview
- Inspector Selection Attributes
- Inspector Settings
- Inspector Validation Attributes
- Utility Components
- Visual Components
- Data Structures
- Helper Utilities
- Math And Extensions
- Pooling Guide
- Random Generators
- Reflection Helpers
- Singletons