# Exception Flow: Performance Patterns **Domain:** Exception handling overhead **Source:** python-numbers-everyone-should-know benchmarks (Python 3.14.2, Apple Silicon) --- ## TL;DR - **try/except with no exception**: Nearly free (1.1 ns overhead) - **Raising an exception**: 6.5x slower than the happy path (139 ns vs 21.5 ns) - **EAFP is fine when exceptions are rare** (<5% of calls) - **Use LBYL for expected failures** (dict key lookup, file existence) - **Never use exceptions for normal control flow** --- ## The Numbers ### Happy Path (No Exception Raised) | Operation | Time | Overhead vs Baseline | |-----------|------|---------------------| | Function call (no try/except) | 20.4 ns | baseline | | try/except (no exception raised) | 21.5 ns | +1.1 ns (+5%) | | try/except ValueError (specific) | 22.9 ns | +2.5 ns (+12%) | | try/except/finally | 22.1 ns | +1.7 ns (+8%) | **Key insight:** The try block itself is essentially free. ### Sad Path (Exception Raised) | Operation | Time | Slowdown vs Happy Path | |-----------|------|----------------------| | raise + catch ValueError | 139 ns | **6.5x slower** | | raise + catch (base Exception) | 140 ns | 6.5x slower | | raise + catch custom exception | 146 ns | 6.8x slower | | raise + catch with `as e` | 148 ns | 6.9x slower | **Key insight:** The 6.5x overhead comes from: 1. Creating the exception object (~40 ns) 2. Capturing the traceback (~70 ns) 3. Stack unwinding and handler lookup (~30 ns) --- ## EAFP vs LBYL: When to Use Which ### EAFP (Easier to Ask Forgiveness than Permission) ```python try: value = data[key] except KeyError: value = default ``` **Use when:** Exceptions are rare (<5% of calls) ### LBYL (Look Before You Leap) ```python if key in data: value = data[key] else: value = default ``` **Use when:** The failure case is common (>15% of calls) ### Crossover Point **Rule of thumb:** If exceptions occur more than 15% of the time, use LBYL. ### dict.get() Beats Both ```python # Best: Use .get() - 26.3 ns, no exception possible config = settings.get('database', {}) ``` --- ## Practical Rules for Coding Agents 1. **try/except blocks are free** - don't avoid them for performance 2. **Raising exceptions costs 6.5x** - only raise for truly exceptional cases 3. **Use .get() for dicts** - beats both EAFP and LBYL 4. **Return Optional for expected missing** - not exceptions 5. **EAFP for file ops** - TOCTOU protection matters more than perf 6. **LBYL when failures are common** (>15% of calls) 7. **Never use exceptions for control flow** --- ## Summary | Scenario | Recommendation | |----------|----------------| | Exception rate <5% | EAFP (try/except) | | Exception rate >15% | LBYL (check first) | | Dict key lookup | Use `.get()` | | Optional return value | Return `None`, not exception | | File operations | EAFP (TOCTOU protection) | | Control flow | Never use exceptions | **The core insight:** try/except is free; raising is not. Design APIs to minimize raises, not to avoid try blocks. --- *Benchmark source: python-numbers-everyone-should-know*