Profile#
The runtime profiler behind python -m modelly.profile. How to read its report is in
Profiling a model.
Runtime profiling for Modelly models: where each step’s time goes, in Modelly terms.
The runtime companion of python -m modelly.lint. Profiler times each
step, every AgentSet.do / shuffle_do / map call by agent class and
method, DataCollector.collect and the scheduled-event callbacks, and counts
births and deaths per step:
from modelly.profile import Profiler
with Profiler(model) as prof:
model.run_for(100)
print(prof.report())
From a shell: python -m modelly.profile path/to/model.py:MyModel --steps 100.
compare_sizes builds the model at several population sizes and compares the
cost per agent (--sizes 1000,4000 on the command line).
Nothing is patched outside the with block. The guide is docs/profile.md.
- class Profiler(model: Model, sample_every: int = 1)[source]#
Record where each step of
modelspends its time.Use it as a context manager around the run:
with Profiler(model) as prof: model.run_for(100) print(prof.report()) frame = prof.to_dataframe()
- Parameters:
model – the model to profile.
sample_every – profile one step in
sample_every(steps 1, 1 + N, …); the others run through the wrappers untimed.
Notes
The patched methods are class attributes, so while the block runs every agent set, collector and event in the process is timed, not only those of
model. Only one profiler can be active at a time. Times of nested loops (an agent’s step callingdoon another set) are inclusive.Prepare a profiler; nothing is patched until the
withblock starts.
- class SizeComparison(model: str, runs: list[SizeRun])[source]#
The profiles of one model built at several sizes, compared per agent.
runsare ordered by size, smallest first.- growths() list[Growth][source]#
The whole step, then each agent loop, costliest at the largest size first.
- report(top: int = 15) str[source]#
The us-per-agent table, one column per size, then the growth and the hints.
- class StepRecord(step: int, time: float, agents: int, wall: float = 0.0, cpu: float = 0.0, births: int = 0, deaths: int = 0, ops: dict[tuple[str, str, str], list] = <factory>)[source]#
What one profiled step cost.
opsmaps(kind, owner, name)to[calls, agents visited, wall s, CPU s].kindis an AgentSet method (do,shuffle_do,map),collectorevent;owneris the agent class (A|Bfor a mixed set) or the collector class. Births and deaths count from this step’s start to the next one’s, so agents created by events in between are included.
- compare_sizes(factory: Callable[[int], Model], sizes: Sequence[int], steps: int = 100, sample_every: int = 1) SizeComparison[source]#
Build the model with
factory(size)at each size, profilestepssteps of each.- Raises:
ValueError – when there are fewer than two different positive sizes.
The profiler: time a running model’s steps, agent-set loops, data collection and events.
Profiler replaces a handful of framework methods while its with block
runs and puts the originals back on exit, so a model that is not being profiled
runs exactly the code it always does. The wrappers never draw a random number
nor reorder a loop: a profiled run gives the same results as an unprofiled one.
- class StepRecord(step: int, time: float, agents: int, wall: float = 0.0, cpu: float = 0.0, births: int = 0, deaths: int = 0, ops: dict[tuple[str, str, str], list] = <factory>)[source]#
What one profiled step cost.
opsmaps(kind, owner, name)to[calls, agents visited, wall s, CPU s].kindis an AgentSet method (do,shuffle_do,map),collectorevent;owneris the agent class (A|Bfor a mixed set) or the collector class. Births and deaths count from this step’s start to the next one’s, so agents created by events in between are included.
- agent_classes(agentset: Any) str[source]#
The class names of the members of
agentset, sorted and joined by|.
- callable_name(fn: Any) str[source]#
A readable name for a method name, a function or an event callback.
- class Profiler(model: Model, sample_every: int = 1)[source]#
Record where each step of
modelspends its time.Use it as a context manager around the run:
with Profiler(model) as prof: model.run_for(100) print(prof.report()) frame = prof.to_dataframe()
- Parameters:
model – the model to profile.
sample_every – profile one step in
sample_every(steps 1, 1 + N, …); the others run through the wrappers untimed.
Notes
The patched methods are class attributes, so while the block runs every agent set, collector and event in the process is timed, not only those of
model. Only one profiler can be active at a time. Times of nested loops (an agent’s step callingdoon another set) are inclusive.Prepare a profiler; nothing is patched until the
withblock starts.