Replies: 1 comment
-
|
I don't think it's that easy... we will probably need some sort of object/dict for tracking names and vertices (or children-parents) |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Removing the Weakref-ed GlobalObject
What it actually does
The singleton
GlobalObject+Mapserves three purposes:Unique name generation :
generate_unique_name()checks existing names in the map to produceClassName_0,ClassName_1, etc.Object graph tracking :
Mapa. stores parent -> child edges
b. supports path-finding
c. supports connectivity checks
d. supports type classification (
argument,created,returned).Undo/redo :
GlobalObject.stackholds anUndoStackthat records property changes for revert.The legitimate use cases are:
WeakValueDictionaryget_item_by_keyinWeakValueDictionaryUndoStackreferenced viaglobal_object.stackScriptManagerlogs operations for reproducibilityGood reasons to get rid of it
1. Constant concurrency bugs!
We've suffered from
RuntimeError: dictionary changed size during iteration.WeakValueDictionaryentries vanish when GC runs — even mid-iteration. The retry-loop fix is a band-aid, not a cure; it hides races rather than eliminating them.2. Hidden global mutable state
Every
NewBase.__init__mutates a process-wide singleton. This makes the code hard to test (you need_clear()between tests), hard to reason about, and impossible to run two independent models in the same process without name collisions. This will be especially important if we are to use multiprocessing.3. Tight coupling
Every object knows about
global_objectat construction time. This pulls inMap,UndoStack,ScriptManager, andLoggeras transitive dependencies for even the simplest parameter object.4. Weakrefs + finalizers are fragile
weakref.finalizecallbacks run at unpredictable times (during GC, possibly on a different thread in CPython). Theprune_type_dictfinalizer modifying__type_dictwhile other code iterates it is a textbook example of this fragility.5. The graph features are barely used
find_path,find_all_paths,is_connected,reverse_routeare generic graph algorithms but typical usage only needs parent - child edges for serialization. Maintaining a full adjacency structure for that is over-engineered.6. Potential memory/performance overhead
Every object creation does a dict insert + a
weakref.finalizeregistration. For models with thousands of parameters this can adds up.7. Name-generation is the only hard requirement
And it doesn't need a global graph to work.
Simpler alternatives
1. Class-level counter for unique names without global map
A single class-variable
dict[str, int]wjcich replaces the entireMapfor name generation. Of course all the uniqeness checks need to be added.2. Explicit parent-child ownership instead of a global graph
Instead of a process-wide
Maptracking edges, let containers (EasyList, model classes) hold direct references to their children. Serialization already walks the object tree viato_dict()so it doesn't need the global graph.Maybe we can drop path-finding entirely?
If we actually need "which objects depend on parameter X?" for fitting, we cam model that explicitly (e.g., each
Parameterstores a back-reference to its parent). The generic graph algorithms inMap(find_path,is_connected, etc.) are unlikely to be needed if ownership is explicit.Current code that would need to change
NewBase.__init__(registers with global map)Map.add_vertex(creates weakref + finalizer)GlobalObject.generate_unique_name(iterates weakref dict)Migration summary
GlobalObject.generate_unique_name->Map.vertices()->WeakValueDictionarydict[str, int])Mapwith weakrefs, finalizers, graph algorithmsMap.get_item_by_keyIssues to solve
Beta Was this translation helpful? Give feedback.
All reactions