Drop x-range query from `ChartPlotWidget.maxmin()`

Move the `Viz.datums_range()` call into `Viz.maxmin()` itself thus
minimizing the chart `.maxmin()` method to an ultra light wrapper around
the viz call. Also move all profiling into the `Viz` method.

Adjust `Viz.maxmin()` to return both the (rounded) x-range values which
correspond to the range containing the y-domain min and max so that
it can be used for up and coming overlay group maxmin calcs.
overlays_interaction_latency_tuning
Tyler Goodlet 2023-01-14 16:11:25 -05:00
parent c09c3925a4
commit bcf2a9868d
2 changed files with 84 additions and 68 deletions

View File

@ -50,7 +50,6 @@ from ._cursor import (
ContentsLabel, ContentsLabel,
) )
from ..data._sharedmem import ShmArray from ..data._sharedmem import ShmArray
from ._l1 import L1Labels
from ._ohlc import BarItems from ._ohlc import BarItems
from ._curve import ( from ._curve import (
Curve, Curve,
@ -70,12 +69,10 @@ from ..data._source import Symbol
from ..log import get_logger from ..log import get_logger
from ._interaction import ChartView from ._interaction import ChartView
from ._forms import FieldsForm from ._forms import FieldsForm
from .._profile import pg_profile_enabled, ms_slower_then
from ._overlay import PlotItemOverlay from ._overlay import PlotItemOverlay
from ._dataviz import Viz from ._dataviz import Viz
from ._search import SearchWidget from ._search import SearchWidget
from . import _pg_overrides as pgo from . import _pg_overrides as pgo
from .._profile import Profiler
if TYPE_CHECKING: if TYPE_CHECKING:
from ._display import DisplayState from ._display import DisplayState
@ -857,17 +854,17 @@ class ChartPlotWidget(pg.PlotWidget):
self.sidepane: Optional[FieldsForm] = None self.sidepane: Optional[FieldsForm] = None
# source of our custom interactions # source of our custom interactions
self.cv = cv = self.mk_vb(name) self.cv = self.mk_vb(name)
pi = pgo.PlotItem( pi = pgo.PlotItem(
viewBox=cv, viewBox=self.cv,
name=name, name=name,
**kwargs, **kwargs,
) )
pi.chart_widget = self pi.chart_widget = self
super().__init__( super().__init__(
background=hcolor(view_color), background=hcolor(view_color),
viewBox=cv, viewBox=self.cv,
# parent=None, # parent=None,
# plotItem=None, # plotItem=None,
# antialias=True, # antialias=True,
@ -878,7 +875,9 @@ class ChartPlotWidget(pg.PlotWidget):
# give viewbox as reference to chart # give viewbox as reference to chart
# allowing for kb controls and interactions on **this** widget # allowing for kb controls and interactions on **this** widget
# (see our custom view mode in `._interactions.py`) # (see our custom view mode in `._interactions.py`)
cv.chart = self self.cv.chart = self
self.pi_overlay: PlotItemOverlay = PlotItemOverlay(self.plotItem)
# ensure internal pi matches # ensure internal pi matches
assert self.cv is self.plotItem.vb assert self.cv is self.plotItem.vb
@ -907,8 +906,6 @@ class ChartPlotWidget(pg.PlotWidget):
# show background grid # show background grid
self.showGrid(x=False, y=True, alpha=0.3) self.showGrid(x=False, y=True, alpha=0.3)
self.pi_overlay: PlotItemOverlay = PlotItemOverlay(self.plotItem)
# indempotent startup flag for auto-yrange subsys # indempotent startup flag for auto-yrange subsys
# to detect the "first time" y-domain graphics begin # to detect the "first time" y-domain graphics begin
# to be shown in the (main) graphics view. # to be shown in the (main) graphics view.
@ -1302,13 +1299,6 @@ class ChartPlotWidget(pg.PlotWidget):
If ``bars_range`` is provided use that range. If ``bars_range`` is provided use that range.
''' '''
profiler = Profiler(
msg=f'`{str(self)}.maxmin(name={name})`: `{self.name}`',
disabled=not pg_profile_enabled(),
ms_threshold=ms_slower_then,
delayed=True,
)
# TODO: here we should instead look up the ``Viz.shm.array`` # TODO: here we should instead look up the ``Viz.shm.array``
# and read directly from shm to avoid copying to memory first # and read directly from shm to avoid copying to memory first
# and then reading it again here. # and then reading it again here.
@ -1316,36 +1306,21 @@ class ChartPlotWidget(pg.PlotWidget):
viz = self._vizs.get(viz_key) viz = self._vizs.get(viz_key)
if viz is None: if viz is None:
log.error(f"viz {viz_key} doesn't exist in chart {self.name} !?") log.error(f"viz {viz_key} doesn't exist in chart {self.name} !?")
key = res = 0, 0 return 0, 0
else: res = viz.maxmin()
(
l,
_,
lbar,
rbar,
_,
r,
) = bars_range or viz.datums_range()
profiler(f'{self.name} got bars range')
key = lbar, rbar
res = viz.maxmin(*key)
if ( if (
res is None res is None
): ):
log.warning( mxmn = 0, 0
f"{viz_key} no mxmn for bars_range => {key} !?"
)
res = 0, 0
if not self._on_screen: if not self._on_screen:
self.default_view(do_ds=False) self.default_view(do_ds=False)
self._on_screen = True self._on_screen = True
else:
x_range, mxmn = res
profiler(f'yrange mxmn: {key} -> {res}') return mxmn
# print(f'{viz_key} yrange mxmn: {key} -> {res}')
return res
def get_viz( def get_viz(
self, self,

View File

@ -60,6 +60,7 @@ from ..log import get_logger
from .._profile import ( from .._profile import (
Profiler, Profiler,
pg_profile_enabled, pg_profile_enabled,
ms_slower_then,
) )
@ -276,7 +277,10 @@ class Viz(msgspec.Struct): # , frozen=True):
] = (None, None) ] = (None, None)
# cache of y-range values per x-range input. # cache of y-range values per x-range input.
_mxmns: dict[tuple[int, int], tuple[float, float]] = {} _mxmns: dict[
tuple[int, int],
tuple[float, float],
] = {}
@property @property
def shm(self) -> ShmArray: def shm(self) -> ShmArray:
@ -324,32 +328,29 @@ class Viz(msgspec.Struct): # , frozen=True):
def maxmin( def maxmin(
self, self,
lbar: int,
rbar: int,
# TODO: drop this right?
bars_range: Optional[tuple[
int, int, int, int, int, int
]] = None,
x_range: slice | tuple[int, int] | None = None,
use_caching: bool = True, use_caching: bool = True,
) -> Optional[tuple[float, float]]: ) -> tuple[float, float] | None:
''' '''
Compute the cached max and min y-range values for a given Compute the cached max and min y-range values for a given
x-range determined by ``lbar`` and ``rbar`` or ``None`` x-range determined by ``lbar`` and ``rbar`` or ``None``
if no range can be determined (yet). if no range can be determined (yet).
''' '''
# TODO: hash the slice instead maybe? name = self.name
# https://stackoverflow.com/a/29980872 profiler = Profiler(
rkey = (round(lbar), round(rbar)) msg=f'{name} -> `{str(self)}.maxmin()`',
disabled=not pg_profile_enabled(),
do_print: bool = False ms_threshold=ms_slower_then,
if use_caching: delayed=True,
cached_result = self._mxmns.get(rkey)
if cached_result:
if do_print:
print(
f'{self.name} CACHED maxmin\n'
f'{rkey} -> {cached_result}'
) )
return cached_result
shm = self.shm shm = self.shm
if shm is None: if shm is None:
@ -357,6 +358,41 @@ class Viz(msgspec.Struct): # , frozen=True):
arr = shm.array arr = shm.array
if x_range is None:
(
l,
_,
lbar,
rbar,
_,
r,
) = (
# TODO: drop this yah?
bars_range
or self.datums_range()
)
profiler(f'{self.name} got bars range')
x_range = lbar, rbar
# TODO: hash the slice instead maybe?
# https://stackoverflow.com/a/29980872
ixrng = (round(lbar), round(rbar))
do_print: bool = False
if use_caching:
cached_result = self._mxmns.get(ixrng)
if cached_result:
if do_print:
print(
f'{self.name} CACHED maxmin\n'
f'{ixrng} -> {cached_result}'
)
return (
ixrng,
cached_result,
)
# get relative slice indexes into array # get relative slice indexes into array
if self.index_field == 'time': if self.index_field == 'time':
read_slc = slice_from_time( read_slc = slice_from_time(
@ -376,7 +412,10 @@ class Viz(msgspec.Struct): # , frozen=True):
slice_view = arr[read_slc] slice_view = arr[read_slc]
if not slice_view.size: if not slice_view.size:
log.warning(f'{self.name} no maxmin in view?') log.warning(
f'{self.name} no maxmin in view?\n'
f"{name} no mxmn for bars_range => {ixrng} !?"
)
return None return None
elif self.yrange: elif self.yrange:
@ -384,9 +423,8 @@ class Viz(msgspec.Struct): # , frozen=True):
if do_print: if do_print:
print( print(
f'{self.name} M4 maxmin:\n' f'{self.name} M4 maxmin:\n'
f'{rkey} -> {mxmn}' f'{ixrng} -> {mxmn}'
) )
else: else:
if self.is_ohlc: if self.is_ohlc:
ylow = np.min(slice_view['low']) ylow = np.min(slice_view['low'])
@ -404,7 +442,7 @@ class Viz(msgspec.Struct): # , frozen=True):
s = 3 s = 3
print( print(
f'{self.name} MANUAL ohlc={self.is_ohlc} maxmin:\n' f'{self.name} MANUAL ohlc={self.is_ohlc} maxmin:\n'
f'{rkey} -> {mxmn}\n' f'{ixrng} -> {mxmn}\n'
f'read_slc: {read_slc}\n' f'read_slc: {read_slc}\n'
# f'abs_slc: {slice_view["index"]}\n' # f'abs_slc: {slice_view["index"]}\n'
f'first {s}:\n{slice_view[:s]}\n' f'first {s}:\n{slice_view[:s]}\n'
@ -413,9 +451,12 @@ class Viz(msgspec.Struct): # , frozen=True):
# cache result for input range # cache result for input range
assert mxmn assert mxmn
self._mxmns[rkey] = mxmn self._mxmns[ixrng] = mxmn
profiler(f'yrange mxmn cacheing: {x_range} -> {mxmn}')
return mxmn return (
ixrng,
mxmn,
)
def view_range(self) -> tuple[int, int]: def view_range(self) -> tuple[int, int]:
''' '''