Profiling OpenMed Performance: A Complete Guide to the Built-In Profiler
OpenMed provides a lightweight, zero-dependency profiling framework in openmed/utils/profiling.py that uses Timer, Profiler, and the @profile decorator to measure execution time with nanosecond precision and zero overhead when disabled.
OpenMed, the open-source medical NLP repository developed by maziyarpanahi, ships with sophisticated performance profiling utilities designed specifically for analyzing biomedical text processing pipelines. The profiling system centers on a global singleton pattern that allows developers to toggle instrumentation at runtime without modifying core application logic. All profiling classes reside in openmed/utils/profiling.py and depend only on Python standard library modules including time, contextlib, and dataclasses.
Core Profiling Data Structures
The foundation of OpenMed's profiling system rests on two primary data classes that capture and aggregate timing measurements.
TimingResult Dataclass
Individual measurements are stored as TimingResult instances, defined at lines 21-27 of openmed/utils/profiling.py. This dataclass records the operation name, duration in seconds, and optional metadata dictionary.
@dataclass
class TimingResult:
name: str
duration: float # seconds
metadata: Optional[Dict[str, Any]] = None
ProfileReport Aggregation
The ProfileReport class (lines 39-87) aggregates multiple TimingResult entries into a comprehensive summary. It provides computed properties including total_duration, timing_count, and formatted output methods such as summary() and format_report().
The Profiler Class Implementation
The Profiler class serves as the primary instrumentation engine, implementing context managers and manual entry APIs for flexible timing capture alongside the simpler Timer utility.
Session Control Methods
Each Profiler instance maintains session-level metadata through start() and stop() methods. The start() method records the wall-clock time for the entire profiling session, while stop() calculates the overall duration and stores it in the report metadata.
Measuring Code Blocks with measure()
The measure(name, metadata=None) context manager (implementation around lines 59-86) captures high-resolution timestamps before and after code block execution:
with profiler.measure("model_load"):
model = load_medical_ner_model()
When the context exits, the elapsed time is automatically converted to a TimingResult and appended to the internal _timings list.
Manual Timing and Metadata APIs
For scenarios requiring external timing sources, the add_timing(name, duration, metadata) method allows manual insertion of pre-calculated durations. Additionally, add_metadata(key, value) attaches global key-value pairs to the final ProfileReport.
Global Profiler Utilities
OpenMed exposes profiling functionality through a global singleton pattern that enables application-wide instrumentation without explicit instance passing.
Enabling and Disabling Profiling
The get_profiler() function lazily initializes a disabled global instance. To activate instrumentation:
from openmed.utils.profiling import enable_profiling, disable_profiling
profiler = enable_profiling() # Creates enabled instance and calls start()
# ... application code ...
disable_profiling() # Stops session and disables further timing
The enable_profiling() function replaces the global singleton with an enabled Profiler and immediately invokes start(), while disable_profiling() halts the session and prevents further timing overhead.
The @profile Decorator
The @profile(name=None) decorator automatically wraps function execution in a Profiler.measure block. Applied to any function, it records entry-to-exit timing without cluttering the function body:
from openmed.utils.profiling import profile
@profile("tokenize")
def tokenize_medical_text(text: str):
return text.split()
Decorated functions automatically report their timing to the global profiler singleton upon completion.
Practical Profiling Workflows
The following patterns demonstrate typical OpenMed performance profiling scenarios for medical NLP pipelines.
Profiling Model Inference Pipelines
Wrap distinct pipeline stages (model loading, preprocessing, inference, postprocessing) using the context manager to identify latency bottlenecks:
from openmed.utils.profiling import enable_profiling, get_profile_report
profiler = enable_profiling()
def run_inference(text: str):
with profiler.measure("model_load"):
model = load_my_model()
with profiler.measure("preprocess"):
tokens = tokenize(text)
with profiler.measure("inference"):
entities = model.predict(tokens)
with profiler.measure("postprocess"):
result = format_entities(entities)
return result
run_inference("Patient has hypertension.")
print(get_profile_report().format_report(include_metadata=True))
Automatic Function Instrumentation
For repetitive utility functions, use the decorator pattern to maintain clean calling code:
from openmed.utils.profiling import enable_profiling, profile, get_profile_report
profiler = enable_profiling()
@profile("tokenize")
def tokenize(text: str):
return text.split()
@profile("predict")
def predict(tokens):
return ["ENTITY"] * len(tokens)
def pipeline(text: str):
return predict(tokenize(text))
pipeline("The patient was prescribed ibuprofen.")
print(get_profile_report().format_report())
Batch Processing Throughput Analysis
For large-scale benchmark dashboards, combine the profiler with the BatchMetrics dataclass (lines 335-388) to calculate items-per-second and characters-per-second metrics:
from openmed.utils.profiling import enable_profiling, BatchMetrics, InferenceMetrics
profiler = enable_profiling()
def process_batch(texts):
batch_metrics = BatchMetrics()
for txt in texts:
with profiler.measure("single_item"):
metric = InferenceMetrics(
text_length=len(txt),
token_count=len(txt.split()),
entity_count=5,
inference_time_ms=30.0,
preprocessing_time_ms=5.0,
postprocessing_time_ms=2.0,
total_time_ms=37.0,
)
batch_metrics.items.append(metric)
batch_metrics.total_time_ms = sum(m.total_time_ms for m in batch_metrics.items)
return batch_metrics.format_report()
print(process_batch([
"Patient: John Doe. Diagnosis: Diabetes.",
"No acute disease identified."
]))
Summary
- OpenMed's profiler resides in
openmed/utils/profiling.pyand requires no external dependencies, utilizing only Python's standard library. - Three instrumentation patterns are available: direct
Timerusage, theProfiler.measure()context manager, and the@profiledecorator for automatic function wrapping. - Global singleton management through
enable_profiling()anddisable_profiling()allows runtime toggling without code changes, ensuring zero overhead when profiling is inactive. - BatchMetrics and ProfileReport classes provide formatted summaries including total duration, timing counts, and throughput metrics essential for optimizing medical NLP pipelines.
- Unit tests in
tests/unit/test_profiling.pyvalidate the profiler's behavior and report formatting for production reliability.
Frequently Asked Questions
How do I enable profiling in an OpenMed script?
Call enable_profiling() from openmed/utils/profiling.py at the start of your script. This function instantiates a global Profiler, marks it as enabled, and starts the session timer. You can then access this instance via get_profiler() or use the decorator/context manager APIs that automatically report to the global singleton.
What is the performance overhead when profiling is disabled?
When profiling is disabled via disable_profiling() or before enable_profiling() is called, the framework incurs virtually zero runtime cost. The global singleton defaults to a disabled state, and the measure context manager checks the enabled flag before entering timing logic, ensuring production deployments remain unaffected.
Can I profile specific functions without modifying their internals?
Yes. Apply the @profile decorator (imported from openmed/utils/profiling.py) to any function definition. This decorator automatically wraps the function body in a timing measurement block using the global profiler, recording the function's execution duration without requiring manual context manager insertion at every call site.
Where are the profiling utilities tested in the OpenMed repository?
Unit tests validating the profiler's behavior, report formatting, and decorator functionality are located in tests/unit/test_profiling.py. These tests verify that TimingResult objects aggregate correctly, that ProfileReport.format_report() produces expected output strings, and that the global profiler singleton maintains proper state across enable/disable cycles.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →