-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Optimize TaskItem cloning between Engine and Tasks #11635
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
The failing tests all fail when I checked out upstream, so not sure what's going on there. Otherwise I've had great numbers building on other repos. I just tested a few runs on Orchard Core and it's around 37% cut from RAR alone, plus seems like every task is consistently faster. Multi-proc: Before:
After:
Single proc (still important for quickbuild): Before
After
|
kk going to leave this drafted for reference and get a parent item up tomorrow. I have detailed perf analysis that all looks great, but a couple parts look riskier than others so I do want to split it up so it's an easy revert in the worst case. I think those risky bits are:
Allocation wise looks much improved as well, despite creating a new |
Context
Currently the engine and task implementations of
ITaskItem
will always do a full rebuild of their backingImmutableDictionary<string, T>
. This is due to having different backing types (string vsProjectMetadataInstance
), plusCopyOnWriteDictionary<T>
type living underShared
, preventing type guards from working even if it did expose the bakcing dictionary.This affects both Tasks copying metadata within a task, as well as the engine whenever any task completes execution, as
Utilities.TaskItem
must always be converted back intoProjectItemInstance.TaskItem
. SeeGatherTaskItemOutputs
in a profiler to see how expensive this is, with most of the impact coming from RAR.Changes Made
This PR is largely two parts:
ImmutableDictionary
to allow reference equality checks.TaskITem
implementations the ability to clone from other implementations when given aCopyOnWriteDictionary
instance.Also adds a simple optimization in RAR to cache any
ProjectItemInstance.TaskItem
instances which came from a source file into temporaryUtilities.TaskItem
, avoiding the expensive enumeration. These are reused across references within each RAR invocation, and the sharedImmutableDictionary
types allow us to do this for free. (Could probably split that off into its own PR to wait till this is merged, but it's a couple lines and net negative perf without this so...)Testing
Example of spending 1718ms in

GatherTaskItemOutputs
alone:374 ms with this change. Note that the

TaskItem
copying doesn't even show up in the profile here since it's just a no-op clone, so it's effectively free and the rest is unrelated overhead.Need to test on more repos, and other hotspots are hard to track since these get passed around everywhere - but there's a pretty clear difference just running ad-hoc builds back-to-back:
This PR:
Mainline:
Notes
Pretty confident on the overall design, but still need to UT and see if there are places consolidate code since there's a fair amount of type casting and duplication in the TaskItem CopyTo() / ImportMetadata() methods that I think can be cleaned up still. May be able split this into A. Moves / modyfing the types + B. Optimized cloning