-
Notifications
You must be signed in to change notification settings - Fork 8
Patch 1.1.1 #90
This patch improves simulation performance with lock-free parallel staging for intervention triggers and fixes to type instability in present_individuals! and setting activation.
Parallel Trigger Dispatch
Symptom and hospitalization triggers fired during the parallel individual-update loop now use a lock-free staging path rather than acquiring the event-queue lock on every event.
- Per-thread staging buffers:
EventQueuegains astagingfield — oneVector{Tuple{Event, Int16}}per thread, pre-allocated toThreads.maxthreadid()entries. Events staged into these buffers during a parallel loop are invisible to the queue untilflush_staging!merges them. stage!: Lock-free variant ofenqueue!. Appends the event and its target tick to the calling thread's staging buffer inO(1). Safe to call concurrently fromThreads.@threadsloops without any synchronization.flush_staging!: Merges all staging buffers into the tick buckets in thread-index order and clears them. Must be called single-threaded; takes no lock. Called automatically bystep!after the parallel individual-update loop completes.stagedkeyword ontriggerandtrigger_strategy: Passstaged = trueto route events throughstage!instead ofenqueue!. Set automatically when triggers are fired fromupdate_individual!, which runs insideThreads.@threads :static.empty!updated: Clears staging buffers in addition to tick buckets, so staged-but-unflushed events are also discarded.
Type-Stable Setting Traversal
-
Type instability in
present_individuals!:ContainerSettingstructs previously stored their child setting type in acontains_type::DataTypefield. Lookups via this field returned an abstractly-typedVector, preventing the compiler from specializing the iteration over contained settings inpresent_individuals!(and other hierarchy-traversal functions such asget_contained!,individuals!,open!,close!,size, andgeolocation).contains_typeandcontained_typeare now type-dispatch functions (contains_type(::Type{SchoolYear}) = SchoolClass, etc.), encoding the parent–child relationship as a compile-time constant. TheDataType-typed fields have been removed from allContainerSettingstructs; usecontains_type(setting)/contained_type(setting)in their place. -
Type instability in setting activation: When an individual was infected, their memberships were activated by iterating
settings_tuple(i)— a tuple of(DataType, id)pairs — and callingsettings(sim, type)[id]with a runtimeDataType. This made everyactivate!call dynamically dispatched.setting_idandsetting_id!are now per-type@inlinedispatch methods rather than a runtime if-else chain, and a newactivate_memberships!helper unrolls the activation loop overmembership_setting_types(Individual)at compile time, making every field access andactivate!call concretely typed. The same fix is applied in all four start-condition initializers (InfectedFraction,PatientZero,PatientZeros,RegionalSeeds) and inprocess_infections!.
Other Changes & API Cleanup
settings(sim, type::DataType)removed: The runtime-DataTypeoverload ofsettingshas been removed. Use the typed overloadsettings(sim, Household)for literal types, orget(settingscontainer(sim), type)where a runtimeDataTypevariable is genuinely needed.- Expanded test coverage: New tests cover the
stage!/flush_staging!staging pipeline (staged events not visible until flushed, double-flush idempotence,empty!clearing unflushed staged events), thesetting_id/setting_id!type-dispatch rewrites, and two previously untestedSettingsContaineredge cases — unmapped container IDs innew_setting_ids!and out-of-bounds contained IDs indelete_dangling_ids!. _insert!internal helper: The shared insertion logic betweenenqueue!andflush_staging!has been extracted into a non-exported_insert!function. No behavioral change.
Public API Surface Tightened
The export list has been audited and substantially trimmed. A large number of names that were internal helpers (utilities, post-processing intermediates, state-mutating setters on Individual, and similar implementation details) are no longer exported, and many have been renamed with a leading underscore to mark them as private.
This is a tightening of the public surface, not a removal of functionality. Every affected name remains accessible by qualifying it with the module, e.g. GEMS.functionname. Code that was calling these directly will keep working with that one change; the audit simply makes explicit which names are the supported, stable API and which are internal and may change without a version bump.
This discussion was created from the release Patch 1.1.1.