85 lines
2.1 KiB
Markdown
85 lines
2.1 KiB
Markdown
# PyQtGraph Optimization Examples
|
|
|
|
Real-world optimization case studies from piker.
|
|
|
|
## Case Study: Gap Annotations (1285 gaps)
|
|
|
|
### Before: Individual `pg.ArrowItem` + `SelectRect`
|
|
|
|
```
|
|
Total creation time: 6.6 seconds
|
|
Per-item overhead: ~5ms
|
|
Memory: 1285 ArrowItem + 1285 SelectRect objects
|
|
```
|
|
|
|
Each gap was rendered as two separate
|
|
`QGraphicsItem` instances (arrow + highlight rect),
|
|
resulting in 2570 Qt objects.
|
|
|
|
### After: Single `GapAnnotations` batch renderer
|
|
|
|
```
|
|
Total creation time:
|
|
104ms (server) + 376ms (client)
|
|
Effective per-item: ~0.08ms
|
|
Speedup: ~36x client, ~180x server
|
|
Memory: 1 GapAnnotations object
|
|
```
|
|
|
|
All 1285 gaps rendered via:
|
|
- One `PrimitiveArray` for all rectangles
|
|
- One `QPainterPath` for all arrows
|
|
- Shared pen/brush across all items
|
|
|
|
### Profiler Output (Client)
|
|
|
|
```
|
|
> Entering markup_gaps() for 1285 gaps
|
|
initial redraw: 0.20ms, tot:0.20
|
|
built annotation specs: 256.48ms, tot:256.68
|
|
batch IPC call complete: 119.26ms, tot:375.94
|
|
final redraw: 0.07ms, tot:376.02
|
|
< Exiting markup_gaps(), total: 376.04ms
|
|
```
|
|
|
|
### Profiler Output (Server)
|
|
|
|
```
|
|
> Entering Batch annotate 1285 gaps
|
|
`np.searchsorted()` complete!: 0.81ms, tot:0.81
|
|
`time_to_row` creation: 98.45ms, tot:99.28
|
|
created GapAnnotations item: 2.98ms, tot:102.26
|
|
< Exiting Batch annotate, total: 104.15ms
|
|
```
|
|
|
|
## Positioning/Update Pattern
|
|
|
|
For annotations that need repositioning when the
|
|
view scrolls or zooms:
|
|
|
|
```python
|
|
def reposition(self, array):
|
|
'''
|
|
Update positions based on new array data.
|
|
|
|
'''
|
|
# vectorized timestamp lookups (not linear!)
|
|
time_to_row = self._build_lookup(array)
|
|
|
|
# update rect array in-place
|
|
rect_memory = self._rectarray.ndarray()
|
|
for i, spec in enumerate(self._specs):
|
|
row = time_to_row.get(spec['time'])
|
|
if row:
|
|
rect_memory[i, 0] = row['index']
|
|
rect_memory[i, 1] = row['close']
|
|
# ... width, height
|
|
|
|
# trigger repaint (single call, not per-item)
|
|
self.update()
|
|
```
|
|
|
|
**Key insight:** Update the underlying memory
|
|
arrays directly, then call `.update()` once.
|
|
Never create/destroy Qt objects during reposition.
|