Skip to content

Commit 05bb0ec

Browse files
committed
docs: update benchmarks based on new performance gains
1 parent 77b7c14 commit 05bb0ec

File tree

8 files changed

+60
-52
lines changed

8 files changed

+60
-52
lines changed

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Rust-optimized quadtree with a clean Python API
3535
- Fast [serialization](https://elan456.github.io/fastquadtree/benchmark/#serialization-vs-rebuild) to/from bytes
3636
- Support for multiple data types (f32, f64, i32, i64) for coordinates
3737
- [100% test coverage](https://codecov.io/gh/Elan456/fastquadtree) and CI on GitHub Actions
38-
- Offers a drop-in [pyqtree shim](https://elan456.github.io/fastquadtree/benchmark/#pyqtree-drop-in-shim-performance-gains) that is ~6.5x faster while keeping the same API
38+
- Offers a drop-in [pyqtree shim](https://elan456.github.io/fastquadtree/benchmark/#pyqtree-drop-in-shim-performance-gains) that is ~10x faster while keeping the same API
3939

4040
----
4141

@@ -49,7 +49,7 @@ from fastquadtree import QuadTree # Point handling
4949
from fastquadtree import RectQuadTree # Bounding box handling
5050
from fastquadtree import QuadTreeObjects # Point handling with object tracking
5151
from fastquadtree import RectQuadTreeObjects # Bounding box handling with object tracking
52-
from fastquadtree.pyqtree import Index # Drop-in pyqtree shim (~6.5x faster while keeping the same API)
52+
from fastquadtree.pyqtree import Index # Drop-in pyqtree shim (~10x faster while keeping the same API)
5353
```
5454

5555

@@ -77,15 +77,15 @@ fastquadtree **outperforms** all other quadtree Python packages, including the R
7777

7878
| Library | Build (s) | Query (s) | Total (s) | Speed vs PyQtree |
7979
|---|---:|---:|---:|---:|
80-
| fastquadtree (np)[^fqtnp] | 0.057 | 0.021 | 0.078 | 54.45× |
81-
| fastquadtree[^fqt] | 0.060 | 0.189 | 0.249 | 17.04× |
82-
| Shapely STRtree[^npreturn] | 0.321 | 0.196 | 0.517 | 8.21× |
83-
| fastquadtree (obj tracking)[^fqto] | 0.437 | 0.239 | 0.675 | 6.28× |
84-
| Rtree | 1.796 | 0.561 | 2.357 | 1.80× |
85-
| nontree-QuadTree | 1.275 | 1.272 | 2.547 | 1.67× |
86-
| e-pyquadtree | 2.144 | 1.507 | 3.650 | 1.16× |
87-
| quads | 3.001 | 1.171 | 4.172 | 1.02× |
88-
| PyQtree | 3.677 | 0.565 | 4.242 | 1.00× |
80+
| fastquadtree (np)[^fqtnp] | 0.052 | 0.017 | 0.068 | 42.52× |
81+
| fastquadtree[^fqt] | 0.054 | 0.231 | 0.285 | 10.20× |
82+
| Shapely STRtree[^npreturn] | 0.200 | 0.110 | 0.309 | 9.40× |
83+
| fastquadtree (obj tracking)[^fqto] | 0.263 | 0.093 | 0.356 | 8.17× |
84+
| nontree-QuadTree | 0.826 | 0.844 | 1.670 | 1.74× |
85+
| Rtree | 1.805 | 0.546 | 2.351 | 1.24× |
86+
| e-pyquadtree | 1.530 | 0.941 | 2.471 | 1.18× |
87+
| quads | 1.907 | 0.759 | 2.667 | 1.09× |
88+
| PyQtree | 2.495 | 0.414 | 2.909 | 1.00× |
8989

9090
[^fqtnp]: Uses `query_np` for Numpy array return values rather than Python lists.
9191
[^fqt]: Uses standard `query` method returning Python lists.
23.9 KB
Loading

assets/quadtree_bench_time.png

46.6 KB
Loading

benchmarks/quadtree_bench/engines.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def query(qt, queries):
7373

7474
return Engine(
7575
"e-pyquadtree",
76-
"#1f77b4",
76+
"#5c8daf",
7777
build,
7878
query, # display name # color (blue)
7979
)
@@ -94,7 +94,7 @@ def query(qt, queries):
9494
for q in queries:
9595
_ = list(qt.intersect(q))
9696

97-
return Engine("PyQtree", "#2ca02c", build, query) # display name # color (green)
97+
return Engine("PyQtree", "#759d75", build, query) # display name # color (green)
9898

9999

100100
def _create_fastquadtree_np_engine(
@@ -113,7 +113,7 @@ def query(qt, queries):
113113

114114
return Engine(
115115
"fastquadtree (np)",
116-
"#e55100",
116+
"#FF3D00",
117117
build,
118118
query, # display name # color (orange)
119119
)
@@ -135,7 +135,7 @@ def query(qt, queries):
135135

136136
return Engine(
137137
"fastquadtree",
138-
"#ff9500",
138+
"#FF9100",
139139
build,
140140
query, # display name # color (orange)
141141
)
@@ -157,7 +157,7 @@ def query(qt, queries):
157157

158158
return Engine(
159159
"fastquadtree (objs)",
160-
"#ffc107",
160+
"#FFD166",
161161
build,
162162
query, # display name # color (orange)
163163
)
@@ -190,7 +190,7 @@ def query(tree, queries):
190190
bb = qd.BoundingBox(min_x=xmin, min_y=ymin, max_x=xmax, max_y=ymax)
191191
_ = tree.within_bb(bb)
192192

193-
return Engine("quads", "#8c564b", build, query) # display name # color (brown)
193+
return Engine("quads", "#8b6c66", build, query) # display name # color (brown)
194194

195195

196196
def _create_nontree_engine(
@@ -220,7 +220,7 @@ def query(tm: TreeMap, queries):
220220

221221
return Engine(
222222
"nontree-QuadTree",
223-
"#17becf",
223+
"#55c2ce",
224224
build,
225225
query, # display name # color (cyan)
226226
)
@@ -245,7 +245,7 @@ def query(points, queries):
245245

246246
return Engine(
247247
"Brute force",
248-
"#9467bd",
248+
"#a088b6",
249249
build,
250250
query, # display name # color (purple)
251251
)
@@ -336,13 +336,13 @@ def get_engines(
336336
"""
337337
# Always available engines
338338
engines = {
339-
"fastquadtree (np)": _create_fastquadtree_np_engine(
340-
bounds, max_points, max_depth
341-
),
342339
"fastquadtree": _create_fastquadtree_engine(bounds, max_points, max_depth),
343340
"fastquadtree (obj tracking)": _create_fastquadtree_items_engine(
344341
bounds, max_points, max_depth
345342
),
343+
"fastquadtree (np)": _create_fastquadtree_np_engine(
344+
bounds, max_points, max_depth
345+
),
346346
"e-pyquadtree": _create_e_pyquadtree_engine(bounds, max_points, max_depth),
347347
"PyQtree": _create_pyqtree_engine(bounds, max_points, max_depth),
348348
# "Brute force": _create_brute_force_engine(bounds, max_points, max_depth), # Brute force doesn't scale well on the graphs so omit it from the main set

benchmarks/quadtree_bench/plotting.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,14 @@ def add_time_traces(y_data: dict[str, list], col: int):
5757
# Update axes
5858
for col in (1, 2, 3):
5959
fig.update_xaxes(title_text="Number of points", row=1, col=col)
60-
fig.update_yaxes(title_text="Time (s)", row=1, col=col)
60+
fig.update_yaxes(
61+
title_text="Time (s)",
62+
row=1,
63+
col=col,
64+
type="log",
65+
tickformat=".3g",
66+
dtick=0.25,
67+
)
6168

6269
# Update layout
6370
fig.update_layout(
@@ -250,7 +257,6 @@ def create_comparison_plot(
250257
height=600,
251258
)
252259

253-
if "rate" in metric:
254-
fig.update_yaxes(type="log")
260+
fig.update_yaxes(type="log")
255261

256262
return fig

docs/benchmark.md

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@ Quadtrees are the focus of the benchmark, but Rtrees are included for reference.
1212

1313
### Summary (largest dataset, PyQtree baseline)
1414
- Points: **500,000**, Queries: **500**
15-
- Fastest total: **fastquadtree** at **0.078 s**
15+
- Fastest total: **fastquadtree** at **0.068 s**
1616

1717
| Library | Build (s) | Query (s) | Total (s) | Speed vs PyQtree |
1818
|---|---:|---:|---:|---:|
19-
| fastquadtree (np)[^fqtnp] | 0.057 | 0.021 | 0.078 | 54.45× |
20-
| fastquadtree[^fqt] | 0.060 | 0.189 | 0.249 | 17.04× |
21-
| Shapely STRtree[^npreturn] | 0.321 | 0.196 | 0.517 | 8.21× |
22-
| fastquadtree (obj tracking)[^fqto] | 0.437 | 0.239 | 0.675 | 6.28× |
23-
| Rtree | 1.796 | 0.561 | 2.357 | 1.80× |
24-
| nontree-QuadTree | 1.275 | 1.272 | 2.547 | 1.67× |
25-
| e-pyquadtree | 2.144 | 1.507 | 3.650 | 1.16× |
26-
| quads | 3.001 | 1.171 | 4.172 | 1.02× |
27-
| PyQtree | 3.677 | 0.565 | 4.242 | 1.00× |
19+
| fastquadtree (np)[^fqtnp] | 0.052 | 0.017 | 0.068 | 42.52× |
20+
| fastquadtree[^fqt] | 0.054 | 0.231 | 0.285 | 10.20× |
21+
| Shapely STRtree[^npreturn] | 0.200 | 0.110 | 0.309 | 9.40× |
22+
| fastquadtree (obj tracking)[^fqto] | 0.263 | 0.093 | 0.356 | 8.17× |
23+
| nontree-QuadTree | 0.826 | 0.844 | 1.670 | 1.74× |
24+
| Rtree | 1.805 | 0.546 | 2.351 | 1.24× |
25+
| e-pyquadtree | 1.530 | 0.941 | 2.471 | 1.18× |
26+
| quads | 1.907 | 0.759 | 2.667 | 1.09× |
27+
| PyQtree | 2.495 | 0.414 | 2.909 | 1.00× |
2828

2929
[^fqtnp]: Uses `query_np` for Numpy array return values rather than Python lists.
3030
[^fqt]: Uses standard `query` method returning Python lists.
@@ -57,19 +57,21 @@ Quadtrees are the focus of the benchmark, but Rtrees are included for reference.
5757

5858
| Variant | Build | Query | Total |
5959
|---|---:|---:|---:|
60-
| Native | 0.141 | 1.858 | 1.999 |
61-
| QuadTree (no objects) | 0.235 | 1.920 | 2.155 |
62-
| QuadTreeObjects | 0.927 | 2.052 | 2.979 |
63-
| QuadTree (numpy, no objects) | 0.046 | 0.252 | 0.297 |
60+
| Native | 0.140 | 2.364 | 2.504 |
61+
| Native (ID-only query) | 0.136 | 0.434 | 0.570 |
62+
| QuadTree (no objects) | 0.179 | 2.210 | 2.389 |
63+
| QuadTree insert_many (no objects) | 0.058 | 2.085 | 2.143 |
64+
| QuadTreeObjects | 0.599 | 0.732 | 1.331 |
65+
| QuadTree (numpy, no objects) | 0.032 | 0.102 | 0.134 |
6466

6567
### Summary
6668

67-
- The Python shim (QuadTree) is 1.078x slower than the native engine due to Python overhead.
69+
- The Python shim (QuadTree) is 0.954x slower than the native engine due to Python overhead.
6870

69-
- NumPy points are the fastest path: build is **5.157x faster** than the list path and queries are **7.627x faster**,
70-
for a **7.249x** total speedup vs the list path.
71+
- NumPy points are the fastest path: build is **5.536x faster** than the list path and queries are **21.733x faster**,
72+
for a **17.822x** total speedup vs the list path.
7173

72-
- QuadTreeObjects adds object association overhead. Build time increases significantly, query time is moderately slower.
74+
- QuadTreeObjects adds object association overhead. Build time increases significantly, query time much faster.
7375

7476
## pyqtree drop-in shim performance gains
7577

@@ -82,13 +84,13 @@ Quadtrees are the focus of the benchmark, but Rtrees are included for reference.
8284

8385
| Variant | Build | Query | Total |
8486
|---|---:|---:|---:|
85-
| pyqtree (fastquadtree) | 0.485 | 2.086 | 2.571 |
86-
| pyqtree (original) | 3.463 | 13.209 | 16.672 |
87+
| pyqtree (fastquadtree) | 0.326 | 0.801 | 1.127 |
88+
| pyqtree (original) | 2.111 | 9.536 | 11.647 |
8789

8890
### Summary
8991

90-
If you directly replace pyqtree with the drop-in `fastquadtree.pyqtree.Index` shim, you get a build time of 0.485s and query time of 2.086s.
91-
This is a **total speedup of 6.486x** compared to the original pyqtree and requires no code changes.
92+
If you directly replace pyqtree with the drop-in `fastquadtree.pyqtree.Index` shim, you get a build time of 0.326s and query time of 0.801s.
93+
This is a **total speedup of 10.333x** compared to the original pyqtree and requires no code changes.
9294

9395
---------
9496

@@ -144,10 +146,10 @@ If your data is already in a NumPy array, using the `insert_many_np` method dire
144146
----------------
145147

146148
## System Info
147-
- **OS**: Windows 11 AMD64
148-
- **Python**: CPython 3.12.2
149+
- **OS**: CachyOS 6.18.5-2-cachyos x86_64
150+
- **Python**: CPython 3.14.2
149151
- **CPU**: AMD Ryzen 7 3700X 8-Core Processor (16 threads)
150-
- **Memory**: 31.9 GB
152+
- **Memory**: 31.3 GB
151153
- **GPU**: NVIDIA GeForce RTX 5070 (11.9 GB)
152154

153155
## Running Benchmarks

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,5 @@ from fastquadtree import QuadTree # Point handling
6161
from fastquadtree import RectQuadTree # Bounding box handling
6262
from fastquadtree import QuadTreeObjects # Point handling with object tracking
6363
from fastquadtree import RectQuadTreeObjects # Bounding box handling with object tracking
64-
from fastquadtree.pyqtree import Index # Drop-in replacement for pyqtree (~6.5x faster while keeping the same API)
64+
from fastquadtree.pyqtree import Index # Drop-in replacement for pyqtree (~10x faster while keeping the same API)
6565
```

pysrc/fastquadtree/pyqtree.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class Index:
2525
2626
This class provides the same interface as pyqtree.Index but is backed by
2727
a high-performance Rust implementation. Based on benchmarks, this provides
28-
an overall performance boost of approximately 6.5x compared to the original
28+
an overall performance boost of approximately 10x compared to the original
2929
pure-Python implementation.
3030
3131
For new projects not requiring pyqtree compatibility, consider using

0 commit comments

Comments
 (0)