Skip to content

Commit 33fb046

Browse files
committed
Readd profiler.
1 parent fb100a6 commit 33fb046

File tree

3 files changed

+110
-9
lines changed

3 files changed

+110
-9
lines changed

src/py/kaleido/_kaleido_tab/_tab.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,9 @@ async def _calc_fig(
113113
self,
114114
spec: _fig_tools.Spec,
115115
*,
116-
topojson: str | None = None,
117-
stepper=False,
116+
topojson: str | None,
117+
render_prof,
118+
stepper,
118119
) -> bytes:
119120
# js script
120121
kaleido_js_fn = (
@@ -123,7 +124,7 @@ async def _calc_fig(
123124
r"return kaleido_scopes.plotly(spec, ...args).then(JSON.stringify);"
124125
r"}"
125126
)
126-
127+
render_prof.profile_log.tick("sending javascript")
127128
result = await _dtools.exec_js_fn(
128129
self.tab,
129130
self._current_js_id,
@@ -133,16 +134,23 @@ async def _calc_fig(
133134
stepper,
134135
)
135136
_raise_error(result)
137+
render_prof.profile_log.tick("javascript sent")
136138

137139
_logger.debug2(f"Result of function call: {result}")
138140
js_response = _dtools.check_kaleido_js_response(result)
139141

140142
if (response_format := js_response.get("format")) == "pdf":
143+
render_prof.profile_log.tick("printing pdf")
141144
img_raw = await _dtools.print_pdf(self.tab)
145+
render_prof.profile_log.tick("pdf printed")
142146
else:
143147
img_raw = js_response["result"]
144148

145149
if response_format not in _TEXT_FORMATS:
146-
return base64.b64decode(img_raw)
150+
res = base64.b64decode(img_raw)
147151
else:
148-
return str.encode(img_raw)
152+
res = str.encode(img_raw)
153+
154+
render_prof.data_out_size = len(res)
155+
render_prof.js_log = self.js_logger.log
156+
return res

src/py/kaleido/_profiler.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from __future__ import annotations
2+
3+
import time
4+
from typing import TYPE_CHECKING
5+
6+
if TYPE_CHECKING:
7+
from pathlib import Path
8+
from typing import Any
9+
10+
from . import _fig_tools
11+
12+
Event = str
13+
14+
15+
class WriteCall:
16+
name: str
17+
renders: list[RenderTaskProfile]
18+
19+
__slots__ = tuple(__annotations__)
20+
21+
def __init__(self, name: str):
22+
self.name = name
23+
self.renders = []
24+
25+
26+
class RenderTaskProfile:
27+
info: dict[str, Any] # literal?
28+
error: None | BaseException
29+
js_log: list[str]
30+
profile_log: ProfileLog
31+
data_in_size: int | None
32+
data_out_size: int | None
33+
34+
__slots__ = tuple(__annotations__)
35+
36+
def __init__(
37+
self,
38+
spec: _fig_tools.Spec,
39+
full_path: Path | None,
40+
tab_id: str,
41+
) -> None:
42+
self.info = {}
43+
self.error = None
44+
self.js_log = []
45+
self.profile_log = ProfileLog()
46+
self.data_in_size = None # need to get this from choreographer
47+
self.data_out_size = None
48+
49+
self.info.update(
50+
{k: v for k, v in spec.items() if k != "data"},
51+
)
52+
self.info["path"] = full_path
53+
self.info["tab"] = tab_id
54+
55+
56+
class ProfileLog:
57+
_logs: dict[Event, float]
58+
59+
__slots__ = tuple(__annotations__)
60+
61+
def __init__(self) -> None:
62+
self._logs = {}
63+
64+
def tick(self, name: str) -> None:
65+
self._logs[name] = time.perf_counter()
66+
67+
def get_logs(self) -> dict[Event, float]:
68+
return self._logs

src/py/kaleido/kaleido.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import asyncio
6+
from collections import deque
67
from collections.abc import AsyncIterable, Iterable
78
from pathlib import Path
89
from typing import TYPE_CHECKING, TypedDict, cast, overload
@@ -12,7 +13,7 @@
1213
from choreographer.errors import ChromeNotFoundError
1314
from choreographer.utils import TmpDirectory
1415

15-
from . import _fig_tools, _path_tools, _utils
16+
from . import _fig_tools, _path_tools, _profiler, _utils
1617
from ._kaleido_tab import _KaleidoTab
1718
from ._page_generator import PageGenerator
1819

@@ -155,6 +156,7 @@ def __init__(
155156
self.tabs_ready = asyncio.Queue(maxsize=0)
156157
self._total_tabs = 0 # tabs properly registered
157158
self._html_tmp_dir = None
159+
self._task_profiler: deque[_profiler.WriteCall] = deque(maxlen=5)
158160

159161
# Kaleido Config
160162
page = page_generator
@@ -305,8 +307,10 @@ async def _render_task(
305307
self,
306308
fig_arg: FigureDict,
307309
*,
310+
topojson: str | None,
308311
_write: bool,
309-
**kwargs: Any,
312+
profiler: _profiler.WriteCall,
313+
stepper: bool,
310314
) -> None | bytes:
311315
spec = _fig_tools.coerce_for_js(
312316
fig_arg.get("fig"),
@@ -324,25 +328,40 @@ async def _render_task(
324328

325329
tab = await self._get_kaleido_tab()
326330

331+
render_prof = _profiler.RenderTaskProfile(
332+
spec,
333+
full_path if _write else None,
334+
tab.tab.target_id,
335+
)
336+
profiler.renders.append(render_prof)
337+
327338
try:
328339
img_bytes = await asyncio.wait_for(
329340
tab._calc_fig( # noqa: SLF001
330341
spec,
331-
**kwargs,
342+
topojson=topojson,
343+
render_prof=render_prof,
344+
stepper=stepper,
332345
),
333346
self._timeout,
334347
)
335348
if _write:
349+
render_prof.profile_log.tick("starting file write")
336350
await _utils.to_thread(full_path.write_bytes, img_bytes)
351+
render_prof.profile_log.tick("file write done")
337352
return None
338353
else:
339354
return img_bytes
340-
except:
355+
except BaseException as e:
356+
render_prof.profile_log.tick("errored out")
341357
if _write:
342358
full_path.unlink() # failure, no write
359+
render_prof.error = e
343360
raise
344361
finally:
362+
render_prof.profile_log.tick("returning tab")
345363
await self._return_kaleido_tab(tab)
364+
render_prof.profile_log.tick("tab returned")
346365

347366
### API ###
348367
@overload
@@ -400,8 +419,13 @@ async def write_fig_from_object(
400419
if _is_figuredict(fig_dicts):
401420
fig_dicts = [fig_dicts]
402421

422+
name = "No Name"
403423
if main_task := asyncio.current_task():
404424
self._main_render_coroutines.add(main_task)
425+
name = main_task.get_name()
426+
427+
profiler = _profiler.WriteCall(name)
428+
self._task_profiler.append(profiler)
405429

406430
tasks: set[asyncio.Task] = set()
407431

@@ -412,6 +436,7 @@ async def write_fig_from_object(
412436
fig_arg=fig_arg,
413437
topojson=fig_arg.get("topojson"),
414438
_write=_write, # backwards compatibility
439+
profiler=profiler,
415440
stepper=stepper,
416441
),
417442
)

0 commit comments

Comments
 (0)