# Profiling Patterns Detailed profiling patterns for use with `piker.toolz.profile.Profiler`. ## Pattern: Function Entry/Exit ```python async def my_function(): profiler = Profiler( msg='my_function()', disabled=False, ms_threshold=0.0, ) step1() profiler('step1') step2() profiler('step2') # auto-prints on exit ``` ## Pattern: Loop Iterations ```python # DON'T profile inside tight loops (overhead!) for i in range(1000): profiler(f'iteration {i}') # NO! # DO profile around loops profiler = Profiler(msg='processing 1000 items') for i in range(1000): process(item[i]) profiler('processed all items') ``` ## Pattern: Conditional Profiling ```python # only profile when investigating specific issue DEBUG_REPOSITION = True def reposition(self, array): if DEBUG_REPOSITION: profiler = Profiler( msg='GapAnnotations.reposition()', disabled=False, ) # ... do work if DEBUG_REPOSITION: profiler('completed reposition') ``` ## Pattern: Teardown/Cleanup Profiling ```python try: # ... main work pass finally: profiler = Profiler( msg='Annotation teardown', disabled=False, ms_threshold=0.0, ) cleanup_resources() profiler('resources cleaned') close_connections() profiler('connections closed') ``` ## Pattern: Distributed IPC Profiling ### Server-side (chart actor) ```python # piker/ui/_remote_ctl.py @tractor.context async def remote_annotate(ctx): async with ctx.open_stream() as stream: async for msg in stream: profiler = Profiler( msg=f'Batch annotate {n} gaps', disabled=False, ms_threshold=0.0, ) result = await handle_request(msg) profiler('request handled') await stream.send(result) profiler('result sent') ``` ### Client-side (analysis script) ```python # piker/tsp/_annotate.py async def markup_gaps(...): profiler = Profiler( msg=f'markup_gaps() for {n} gaps', disabled=False, ms_threshold=0.0, ) await actl.redraw() profiler('initial redraw') specs = build_specs(gaps) profiler('built annotation specs') # IPC round-trip! result = await actl.add_batch(specs) profiler('batch IPC call complete') await actl.redraw() profiler('final redraw') ``` ## Common Use Cases ### IPC Request/Response Timing ```python # Client side profiler = Profiler(msg='Remote request') result = await remote_call() profiler('got response') # Server side (in handler) profiler = Profiler(msg='Handle request') process_request() profiler('request processed') ``` ### Batch Operation Optimization ```python profiler = Profiler(msg='Batch processing') items = collect_all() profiler(f'collected {len(items)} items') results = numpy_batch_op(items) profiler('numpy op complete') output = { k: v for k, v in zip(keys, results) } profiler('dict built') ``` ### Startup/Initialization Timing ```python async def __aenter__(self): profiler = Profiler(msg='Service startup') await connect_to_broker() profiler('broker connected') await load_config() profiler('config loaded') await start_feeds() profiler('feeds started') return self ``` ## Debugging Performance Regressions When profiler shows unexpected slowness: ### 1. Add finer-grained checkpoints ```python # was: result = big_function() profiler('big_function done') # now: profiler = Profiler( msg='big_function internals', ) step1 = part_a() profiler('part_a') step2 = part_b() profiler('part_b') step3 = part_c() profiler('part_c') ``` ### 2. Check for hidden iterations ```python # looks simple but might be slow! result = array[array['time'] == timestamp] profiler('array lookup') # reveals O(n) scan per call for ts in timestamps: # outer loop row = array[array['time'] == ts] # O(n)! ``` ### 3. Isolate IPC from computation ```python # was: can't tell where time is spent result = await remote_call(data) profiler('remote call done') # now: separate phases payload = prepare_payload(data) profiler('payload prepared') result = await remote_call(payload) profiler('IPC complete') parsed = parse_result(result) profiler('result parsed') ```