9
9
import numpy as np
10
10
from psutil import disk_io_counters
11
11
from pyinstrument import Profiler
12
- from pyinstrument .renderers import HTMLRenderer
12
+ from pyinstrument .renderers import ConsoleRenderer , HTMLRenderer
13
13
from pyinstrument .session import Session
14
14
from scale_run import save_arguments_to_json , scale_run
15
15
16
+ try :
17
+ from ansi2html import Ansi2HTMLConverter
18
+ ANSI2HTML_AVAILABLE = True
19
+ except ImportError :
20
+ ANSI2HTML_AVAILABLE = False
21
+
16
22
from tlo import Simulation
17
23
18
24
_PROFILING_RESULTS : Path = (Path (__file__ ).parents [3 ] / "profiling_results" ).resolve ()
@@ -176,17 +182,26 @@ def run_profiling(
176
182
output_name : str = "profiling" ,
177
183
write_html : bool = False ,
178
184
write_pyisession : bool = False ,
185
+ write_flat_html : bool = True ,
179
186
interval : float = 2e-1 ,
180
187
initial_population : int = 50000 ,
181
188
simulation_years : int = 5 ,
182
189
simulation_months : int = 0 ,
183
190
mode_appt_constraints : Literal [0 , 1 , 2 ] = 2 ,
184
191
additional_stats : Optional [List [Tuple [str , str ]]] = None ,
192
+ show_progress_bar : bool = False ,
193
+ disable_log_output_to_stdout : bool = False ,
185
194
) -> None :
186
195
"""
187
196
Uses pyinstrument to profile the scale_run simulation,
188
197
writing the output in the requested formats.
189
198
"""
199
+ if write_flat_html and not ANSI2HTML_AVAILABLE :
200
+ # Check if flat HTML output requested but ansi2html module not available at
201
+ # _start_ of function to avoid erroring after a potentially long profiling run
202
+ msg = "ansi2html required for flat HTML output."
203
+ raise ValueError (msg )
204
+
190
205
additional_stats = dict (() if additional_stats is None else additional_stats )
191
206
192
207
# Create the profiler to record the stack
@@ -208,7 +223,7 @@ def run_profiling(
208
223
"log_filename" : "scale_run_profiling" ,
209
224
"log_level" : "WARNING" ,
210
225
"parse_log_file" : False ,
211
- "show_progress_bar" : True ,
226
+ "show_progress_bar" : show_progress_bar ,
212
227
"seed" : 0 ,
213
228
"disable_health_system" : False ,
214
229
"disable_spurious_symptoms" : False ,
@@ -218,6 +233,7 @@ def run_profiling(
218
233
"record_hsi_event_details" : False ,
219
234
"ignore_warnings" : True ,
220
235
"log_final_population_checksum" : False ,
236
+ "disable_log_output_to_stdout" : disable_log_output_to_stdout ,
221
237
}
222
238
223
239
output_arg_file = output_dir / f"{ output_name } .args.json"
@@ -253,7 +269,11 @@ def run_profiling(
253
269
# Renderer initialisation options:
254
270
# show_all: removes library calls where identifiable
255
271
# timeline: if true, samples are left in chronological order rather than total time
256
- html_renderer = HTMLRenderer (show_all = False , timeline = False )
272
+ html_renderer = HTMLRenderer (
273
+ show_all = False ,
274
+ timeline = False ,
275
+ processor_options = {"show_regex" : ".*/tlo/.*" , "hide_regex" : ".*/pandas/.*" }
276
+ )
257
277
print (f"Writing { output_html_file } " , end = "..." , flush = True )
258
278
with open (output_html_file , "w" ) as f :
259
279
f .write (html_renderer .render (scale_run_session ))
@@ -268,13 +288,29 @@ def run_profiling(
268
288
f"\t Was : { additional_stats ['html_output' ]} "
269
289
f"\t Replaced by: { output_html_file } "
270
290
)
271
- additional_stats ["html_output" ] = str (output_html_file )
291
+ additional_stats ["html_output" ] = str (output_html_file . name )
272
292
273
293
if write_pyisession :
274
294
output_ipysession_file = output_dir / f"{ output_name } .pyisession"
275
295
print (f"Writing { output_ipysession_file } " , end = "..." , flush = True )
276
296
scale_run_session .save (output_ipysession_file )
277
297
print ("done" )
298
+
299
+ if write_flat_html :
300
+ output_html_file = output_dir / f"{ output_name } .flat.html"
301
+ console_renderer = ConsoleRenderer (
302
+ show_all = False ,
303
+ timeline = False ,
304
+ color = True ,
305
+ flat = True ,
306
+ processor_options = {"show_regex" : ".*/tlo/.*" , "hide_regex" : ".*/pandas/.*" }
307
+ )
308
+ converter = Ansi2HTMLConverter (title = output_name )
309
+ print (f"Writing { output_html_file } " , end = "..." , flush = True )
310
+ with open (output_html_file , "w" ) as f :
311
+ f .write (converter .convert (console_renderer .render (scale_run_session )))
312
+ print ("done" )
313
+ additional_stats ["flat_html_output" ] = str (output_html_file .name )
278
314
279
315
# Write the statistics file, main output
280
316
output_stat_file = output_dir / f"{ output_name } .stats.json"
@@ -329,6 +365,12 @@ def run_profiling(
329
365
action = "store_true" ,
330
366
dest = "write_pyisession" ,
331
367
)
368
+ parser .add_argument (
369
+ "--flat-html" ,
370
+ action = "store_true" ,
371
+ help = "Write flat HTML output in addition to statistics output." ,
372
+ dest = "write_flat_html" ,
373
+ )
332
374
parser .add_argument (
333
375
"-i" ,
334
376
"--interval-seconds" ,
@@ -382,6 +424,16 @@ def run_profiling(
382
424
"as strings."
383
425
),
384
426
)
427
+ parser .add_argument (
428
+ "--show-progress-bar" ,
429
+ help = "Show simulation progress bar during simulation rather than log output" ,
430
+ action = "store_true" ,
431
+ )
432
+ parser .add_argument (
433
+ "--disable-log-output-to-stdout" ,
434
+ help = "Disable simulation log output being displayed in stdout stream" ,
435
+ action = "store_true" ,
436
+ )
385
437
386
438
args = parser .parse_args ()
387
439
0 commit comments