Add display loop profiling
Probably the best place to root the profiler since we can get a better top down view of bottlenecks in the graphics stack. More, - add in draft M4 downsampling code (commented) after getting it mostly working; next step is to move this processing into an FSP subactor. - always update the vlm chart last y-axis sticky - set call `.default_view()` just before inf sleep on startupbig_data_lines
parent
f1f257d4a2
commit
8f26335aea
|
@ -29,6 +29,8 @@ from typing import Optional, Any, Callable
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import tractor
|
import tractor
|
||||||
import trio
|
import trio
|
||||||
|
import pyqtgraph as pg
|
||||||
|
from PyQt5.QtCore import QLineF
|
||||||
|
|
||||||
from .. import brokers
|
from .. import brokers
|
||||||
from ..data.feed import open_feed
|
from ..data.feed import open_feed
|
||||||
|
@ -178,7 +180,6 @@ async def graphics_update_loop(
|
||||||
vlm_sticky = vlm_chart._ysticks['volume']
|
vlm_sticky = vlm_chart._ysticks['volume']
|
||||||
|
|
||||||
maxmin = partial(chart_maxmin, chart, vlm_chart)
|
maxmin = partial(chart_maxmin, chart, vlm_chart)
|
||||||
chart.default_view()
|
|
||||||
last_bars_range: tuple[float, float]
|
last_bars_range: tuple[float, float]
|
||||||
(
|
(
|
||||||
last_bars_range,
|
last_bars_range,
|
||||||
|
@ -258,6 +259,8 @@ async def graphics_update_loop(
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
chart.default_view()
|
||||||
|
|
||||||
# main loop
|
# main loop
|
||||||
async for quotes in stream:
|
async for quotes in stream:
|
||||||
ds.quotes = quotes
|
ds.quotes = quotes
|
||||||
|
@ -295,6 +298,10 @@ def graphics_update_cycle(
|
||||||
# TODO: eventually optimize this whole graphics stack with ``numba``
|
# TODO: eventually optimize this whole graphics stack with ``numba``
|
||||||
# hopefully XD
|
# hopefully XD
|
||||||
|
|
||||||
|
profiler = pg.debug.Profiler(
|
||||||
|
disabled=False, # not pg_profile_enabled(),
|
||||||
|
delayed=False,
|
||||||
|
)
|
||||||
# unpack multi-referenced components
|
# unpack multi-referenced components
|
||||||
chart = ds.chart
|
chart = ds.chart
|
||||||
vlm_chart = ds.vlm_chart
|
vlm_chart = ds.vlm_chart
|
||||||
|
@ -305,6 +312,8 @@ def graphics_update_cycle(
|
||||||
vars = ds.vars
|
vars = ds.vars
|
||||||
tick_margin = vars['tick_margin']
|
tick_margin = vars['tick_margin']
|
||||||
|
|
||||||
|
update_uppx = 5
|
||||||
|
|
||||||
for sym, quote in ds.quotes.items():
|
for sym, quote in ds.quotes.items():
|
||||||
|
|
||||||
# NOTE: vlm may be written by the ``brokerd`` backend
|
# NOTE: vlm may be written by the ``brokerd`` backend
|
||||||
|
@ -319,10 +328,6 @@ def graphics_update_cycle(
|
||||||
# are diffed on each draw cycle anyway; so updates to the
|
# are diffed on each draw cycle anyway; so updates to the
|
||||||
# "curve" length is already automatic.
|
# "curve" length is already automatic.
|
||||||
|
|
||||||
# compute the first available graphic's x-units-per-pixel
|
|
||||||
xpx = vlm_chart.view.xs_in_px()
|
|
||||||
# print(r)
|
|
||||||
|
|
||||||
# increment the view position by the sample offset.
|
# increment the view position by the sample offset.
|
||||||
i_step = ohlcv.index
|
i_step = ohlcv.index
|
||||||
i_diff = i_step - vars['i_last']
|
i_diff = i_step - vars['i_last']
|
||||||
|
@ -338,8 +343,63 @@ def graphics_update_cycle(
|
||||||
l, lbar, rbar, r = brange
|
l, lbar, rbar, r = brange
|
||||||
mx = mx_in_view + tick_margin
|
mx = mx_in_view + tick_margin
|
||||||
mn = mn_in_view - tick_margin
|
mn = mn_in_view - tick_margin
|
||||||
|
profiler('maxmin call')
|
||||||
liv = r > i_step # the last datum is in view
|
liv = r > i_step # the last datum is in view
|
||||||
|
|
||||||
|
# compute the first available graphic's x-units-per-pixel
|
||||||
|
xpx = vlm_chart.view.xs_in_px()
|
||||||
|
|
||||||
|
in_view = chart.in_view(ohlcv.array)
|
||||||
|
|
||||||
|
if lbar != rbar:
|
||||||
|
# view box width in pxs
|
||||||
|
w = chart.view.boundingRect().width()
|
||||||
|
|
||||||
|
# TODO: a better way to get this?
|
||||||
|
# i would guess the esiest way is to just
|
||||||
|
# get the ``.boundingRect()`` of the curve
|
||||||
|
# in view but maybe there's something smarter?
|
||||||
|
# Currently we're just mapping the rbar, lbar to
|
||||||
|
# pixels via:
|
||||||
|
cw = chart.view.mapViewToDevice(QLineF(lbar, 0, rbar, 0)).length()
|
||||||
|
# is this faster?
|
||||||
|
# cw = chart.mapFromView(QLineF(lbar, 0 , rbar, 0)).length()
|
||||||
|
|
||||||
|
profiler(
|
||||||
|
f'view width pxs: {w}\n'
|
||||||
|
f'curve width pxs: {cw}\n'
|
||||||
|
f'sliced in view: {in_view.size}'
|
||||||
|
)
|
||||||
|
|
||||||
|
# compress bars to m4 line(s) if uppx is high enough
|
||||||
|
# if in_view.size > cw:
|
||||||
|
# from ._compression import ds_m4, hl2mxmn
|
||||||
|
|
||||||
|
# mxmn, x = hl2mxmn(in_view)
|
||||||
|
# profiler('hl tracer')
|
||||||
|
|
||||||
|
# nb, x, y = ds_m4(
|
||||||
|
# x=x,
|
||||||
|
# y=mxmn,
|
||||||
|
# # TODO: this needs to actually be the width
|
||||||
|
# # in pixels of the visible curve since we don't
|
||||||
|
# # want to downsample any 'zeros' around the curve,
|
||||||
|
# # just the values that make up the curve graphic,
|
||||||
|
# # i think?
|
||||||
|
# px_width=cw,
|
||||||
|
# )
|
||||||
|
# profiler(
|
||||||
|
# 'm4 downsampled\n'
|
||||||
|
# f' ds bins: {nb}\n'
|
||||||
|
# f' x.shape: {x.shape}\n'
|
||||||
|
# f' y.shape: {y.shape}\n'
|
||||||
|
# f' x: {x}\n'
|
||||||
|
# f' y: {y}\n'
|
||||||
|
# )
|
||||||
|
# breakpoint()
|
||||||
|
|
||||||
|
# assert y.size == mxmn.size
|
||||||
|
|
||||||
# don't real-time "shift" the curve to the
|
# don't real-time "shift" the curve to the
|
||||||
# left unless we get one of the following:
|
# left unless we get one of the following:
|
||||||
if (
|
if (
|
||||||
|
@ -355,17 +415,18 @@ def graphics_update_cycle(
|
||||||
# and then iff update curves and shift?
|
# and then iff update curves and shift?
|
||||||
chart.increment_view(steps=i_diff)
|
chart.increment_view(steps=i_diff)
|
||||||
|
|
||||||
if (
|
if vlm_chart:
|
||||||
vlm_chart
|
# always update y-label
|
||||||
# if zoomed out alot don't update the last "bar"
|
|
||||||
and xpx < 4
|
|
||||||
):
|
|
||||||
vlm_chart.update_curve_from_array('volume', array)
|
|
||||||
ds.vlm_sticky.update_from_data(*array[-1][['index', 'volume']])
|
ds.vlm_sticky.update_from_data(*array[-1][['index', 'volume']])
|
||||||
|
|
||||||
if (
|
if (
|
||||||
mx_vlm_in_view > vars['last_mx_vlm']
|
(xpx < update_uppx or i_diff > 0)
|
||||||
or trigger_all
|
or trigger_all
|
||||||
|
):
|
||||||
|
vlm_chart.update_curve_from_array('volume', array)
|
||||||
|
|
||||||
|
if (
|
||||||
|
mx_vlm_in_view != vars['last_mx_vlm']
|
||||||
):
|
):
|
||||||
# print(f'mx vlm: {last_mx_vlm} -> {mx_vlm_in_view}')
|
# print(f'mx vlm: {last_mx_vlm} -> {mx_vlm_in_view}')
|
||||||
vlm_chart.view._set_yrange(
|
vlm_chart.view._set_yrange(
|
||||||
|
@ -431,8 +492,10 @@ def graphics_update_cycle(
|
||||||
# for typ, tick in reversed(lasts.items()):
|
# for typ, tick in reversed(lasts.items()):
|
||||||
|
|
||||||
# update ohlc sampled price bars
|
# update ohlc sampled price bars
|
||||||
|
if (
|
||||||
if xpx < 4 or i_diff > 0:
|
xpx < update_uppx
|
||||||
|
or i_diff > 0
|
||||||
|
):
|
||||||
chart.update_ohlc_from_array(
|
chart.update_ohlc_from_array(
|
||||||
chart.name,
|
chart.name,
|
||||||
array,
|
array,
|
||||||
|
@ -600,8 +663,8 @@ async def display_symbol_data(
|
||||||
f'step:1s '
|
f'step:1s '
|
||||||
)
|
)
|
||||||
|
|
||||||
linkedsplits = godwidget.linkedsplits
|
linked = godwidget.linkedsplits
|
||||||
linkedsplits._symbol = symbol
|
linked._symbol = symbol
|
||||||
|
|
||||||
# generate order mode side-pane UI
|
# generate order mode side-pane UI
|
||||||
# A ``FieldsForm`` form to configure order entry
|
# A ``FieldsForm`` form to configure order entry
|
||||||
|
@ -611,7 +674,7 @@ async def display_symbol_data(
|
||||||
godwidget.pp_pane = pp_pane
|
godwidget.pp_pane = pp_pane
|
||||||
|
|
||||||
# create main OHLC chart
|
# create main OHLC chart
|
||||||
chart = linkedsplits.plot_ohlc_main(
|
chart = linked.plot_ohlc_main(
|
||||||
symbol,
|
symbol,
|
||||||
bars,
|
bars,
|
||||||
sidepane=pp_pane,
|
sidepane=pp_pane,
|
||||||
|
@ -641,8 +704,8 @@ async def display_symbol_data(
|
||||||
# NOTE: we must immediately tell Qt to show the OHLC chart
|
# NOTE: we must immediately tell Qt to show the OHLC chart
|
||||||
# to avoid a race where the subplots get added/shown to
|
# to avoid a race where the subplots get added/shown to
|
||||||
# the linked set *before* the main price chart!
|
# the linked set *before* the main price chart!
|
||||||
linkedsplits.show()
|
linked.show()
|
||||||
linkedsplits.focus()
|
linked.focus()
|
||||||
await trio.sleep(0)
|
await trio.sleep(0)
|
||||||
|
|
||||||
vlm_chart: Optional[ChartPlotWidget] = None
|
vlm_chart: Optional[ChartPlotWidget] = None
|
||||||
|
@ -652,7 +715,7 @@ async def display_symbol_data(
|
||||||
if has_vlm(ohlcv):
|
if has_vlm(ohlcv):
|
||||||
vlm_chart = await ln.start(
|
vlm_chart = await ln.start(
|
||||||
open_vlm_displays,
|
open_vlm_displays,
|
||||||
linkedsplits,
|
linked,
|
||||||
ohlcv,
|
ohlcv,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -660,7 +723,7 @@ async def display_symbol_data(
|
||||||
# from an input config.
|
# from an input config.
|
||||||
ln.start_soon(
|
ln.start_soon(
|
||||||
start_fsp_displays,
|
start_fsp_displays,
|
||||||
linkedsplits,
|
linked,
|
||||||
ohlcv,
|
ohlcv,
|
||||||
loading_sym_key,
|
loading_sym_key,
|
||||||
loglevel,
|
loglevel,
|
||||||
|
@ -669,7 +732,7 @@ async def display_symbol_data(
|
||||||
# start graphics update loop after receiving first live quote
|
# start graphics update loop after receiving first live quote
|
||||||
ln.start_soon(
|
ln.start_soon(
|
||||||
graphics_update_loop,
|
graphics_update_loop,
|
||||||
linkedsplits,
|
linked,
|
||||||
feed.stream,
|
feed.stream,
|
||||||
ohlcv,
|
ohlcv,
|
||||||
wap_in_history,
|
wap_in_history,
|
||||||
|
@ -687,17 +750,19 @@ async def display_symbol_data(
|
||||||
# let Qt run to render all widgets and make sure the
|
# let Qt run to render all widgets and make sure the
|
||||||
# sidepanes line up vertically.
|
# sidepanes line up vertically.
|
||||||
await trio.sleep(0)
|
await trio.sleep(0)
|
||||||
linkedsplits.resize_sidepanes()
|
linked.resize_sidepanes()
|
||||||
|
|
||||||
# NOTE: we pop the volume chart from the subplots set so
|
# NOTE: we pop the volume chart from the subplots set so
|
||||||
# that it isn't double rendered in the display loop
|
# that it isn't double rendered in the display loop
|
||||||
# above since we do a maxmin calc on the volume data to
|
# above since we do a maxmin calc on the volume data to
|
||||||
# determine if auto-range adjustements should be made.
|
# determine if auto-range adjustements should be made.
|
||||||
linkedsplits.subplots.pop('volume', None)
|
linked.subplots.pop('volume', None)
|
||||||
|
|
||||||
# TODO: make this not so shit XD
|
# TODO: make this not so shit XD
|
||||||
# close group status
|
# close group status
|
||||||
sbar._status_groups[loading_sym_key][1]()
|
sbar._status_groups[loading_sym_key][1]()
|
||||||
|
|
||||||
# let the app run.. bby
|
# let the app run.. bby
|
||||||
|
chart.default_view()
|
||||||
|
# linked.graphics_cycle()
|
||||||
await trio.sleep_forever()
|
await trio.sleep_forever()
|
||||||
|
|
Loading…
Reference in New Issue