PERF: delay preset sync until tab completion / attribute access, track file desync#1317
Conversation
|
I did some more digging (manual profiling because I'm dumb) as to what causes the remaining add-methods to take so long, and I think it boils down to |
ZLLentz
left a comment
There was a problem hiding this comment.
I like this and have a lot of opinions about presets in general.
stat() calls having awful performance
I agree this is a problem on NFS/WEKA.
Probably the main way for us to mitigate this would be reworking presets to use fewer files, but that has its own annoyances.
pcdsdevices/interface.py
Outdated
| def __dir__(self): | ||
| if not self._tab_initialized and hasattr(self, 'presets'): | ||
| self._tab_initialized = True | ||
| self.presets.sync() |
There was a problem hiding this comment.
Change/addition request: we need to identify other times where presets may need a late sync. For example, if someone is expecting presets to be available in a hutch-python function call they need a clear way to trigger this without tabbing (or we need to load them automatically).
There was a problem hiding this comment.
The device.presets.sync() call is still available, and seems to be how we suggest people sync presets across sections. I imagine we haven't advertised this as thoroughly as necessary.
There was a problem hiding this comment.
I guess we could overwrite __getattribute__ and catch attempts to access mv_*, umv_*, and wm_* methods, but are there any other access modes we can think of?
There was a problem hiding this comment.
Is it possible that doing this on __getattribute__ is actually most correct to make sure the loaded values are always up-to-date?
There was a problem hiding this comment.
My one qualm with this is that it does break the already fragile composition abstraction barrier we've set up with the Presets module. Maybe there's also a way around this.
Back to the mines!
…ning if sync is needed, use Preset.sync_needed to determine if Presets should be updated
|
Updated with some rather significant changes, now we store the last file modification times with every |
|
I think at least one of your commits has not been pushed |
|
I blame github |
ZLLentz
left a comment
There was a problem hiding this comment.
I like how this is implemented and I like how it speeds up the startup preset loading by 2x
| # deferred_fast_motor_preset must come last, | ||
| # to clear cache after motor is created (and sync-ed at init) | ||
| assert fast_motor.presets.sync_needed | ||
| fast_motor.__dir__() # mimic tab completion request |
There was a problem hiding this comment.
Nitpick: is dir(fast_motor) equivalent?
There was a problem hiding this comment.
I believe so, do we prefer that to the dunder-access? I used __dir__ just to match the code implementation.
|
First time seeing updates to pcdsdevices, so probably don't have good feedback this time. Are presets for device settings? Why does preset file reading need to be delayed until tab completion has been attempted? |
|
Presets are basically position shortcuts that users can save/load. They're dynamically added to the devices attributes, and are backed by simple yaml files. Because each device is backed by a file, we end up opening and reading a lot of files in some hutch-python sessions. So instead of loading them all at once, we defer loading until the user actually needs it |
| return state | ||
|
|
||
| @property | ||
| def sync_needed(self) -> bool: |
There was a problem hiding this comment.
One last tiny tiny nitpick: this is a property that touches the filesystem, usually I prefer things that may take time to be function calls. Usually we expect attribute access to be fast, so if they are not fast we can accidentally write code with performance issues.
There was a problem hiding this comment.
A good point. I'll do that
…ns, not properties
Description
Delays preset file reading until tab-completion has been attempted. Stashes this information on the device instance itself. This is not the default behavior, and you must opt into this by supplying the optional argument
defer_loadingtosetup_presets_pathsAlso tracks the modification time of the preset file on sync, making the
Presetinstance the source of truth to see if the presets need to be synced.Motivation and Context
Presets have taken a very long time to load recently, and the first place to look is often file i/o. This delays this as long as possible while attempting to preserve the behavior hutch scientists use: tab completion.
This has also been made optional. Both
setup_presets_pathsandPresets.sync()now take an optionaldefer_loadingargument, that if true will skip the preset data loading stepIn a nutshell:
the device's Preset instance tracks whether the preset is out of sync with the files. This is done by tracking file modification times
The device checks if a sync is necessary, and if so syncs with the files when:
__dir__is called (during tab-completion)__getattribute__is called with a preset-related prefix (wm_,umv_,mv_)Presets.sync()is also called on device init, since it's part ofPresets.__init__. This happens both on device-creation andsetup_presets_path, though on device-creation we don't do any file loading. This is a ~0.013s double-hit, split between the device load and presets load sections forFltMvInterfacedevicesHow Has This Been Tested?
Tests pass
Tested interactively through hutch-python, loading some subset of 559 xcs presets
There's some variation but I'm not going to do a battery of tests here
Why isn't this just 0 s?
There's still some path access going on in hutch-python, it's just stopping short of actually opening the presets file.
Where Has This Been Documented?
This PR, a stray comment
Pre-merge checklist