-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Split internal pipeline into exp and prod for vs16.11 branch with PR #12414 improvements #12399
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
Conversation
`build.sh` brings down a .NET SDK, so put it on PATH for Copilot's use. ------- Co-authored-by: Rainer Sigwald <[email protected]>
…a since we don't always need it. (#11958)
Some tests were failing when run from Visual Studio because they were running in a .NET 9 process launched as `TestHost.exe`, not in a `dotnet.exe` host. That caused TaskHost launching to try to run `TestHost.exe MSBuild.dll` which failed, as TestHost isn't a general-purpose launcher. Add another fallback to `GetCurrentHost` to fall back to the current shared framework's parent `dotnet.exe`, if we can find it.
…11816) Co-authored-by: huulinh99 <[email protected]>
MSBuild 17.11.31
…y bigger allocation of list, leaving as default allocation size.
…at might crop up in the future
* Removed lazy from exclude tester function since it was not needed since lifetime of lazy object was within the method itself. * switched from linq clause for add range to manually adding items, because the linq version caused a closure from a method it did not have context with.
Sdk 9.0.200 and vs17.13 are out of support. Co-authored-by: Rainer Sigwald <[email protected]>
Uses [SDK hint paths][] to tell modern SDK resolvers (.NET CLI in 10 previews, and unreleased VS versions at present) to use the SDK from the `.dotnet` folder that Arcade restores, instead of the default behavior. Should be silently ignored by older resolvers, so shouldn't change CI behavior. [SDK hint paths]: https://github.com/dotnet/designs/blob/238ca0202ea5df96d378a06c01960fc289aba166/accepted/2025/local-sdk-global-json.md
…602.2 (#11979) Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.25271.1 -> To Version 9.0.0-beta.25302.2 Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>
Fixes # Captures that lead to allocations. ### Context Based on a trace was able to see that allocations from captures that were from using linq without the correct context. ### Changes Made Manually created a list instead of using the where clause to create one. Also moved over to grabbing the underlying struct enumerator when possible to avoid boxing. ### Testing Looked at the dll in ILSPY to verify change in closures. Before. (Display Class shows a capture)  After (no more DisplayClass)  ### Notes
Fixes # ### Context Based on some traces there were some closures from a linq call without the right context. This caused extra allocations and this addresses it. ### Changes Made Instead of using linq to filter ites, just used a for loop to create a filtered list ### Testing Compared with ILSpy that there were less closures and allocations (DisplayName) Before  After  ### Notes
…6.15.0.78 (#11980) NuGet.Build.Tasks From Version 6.15.0-preview.1.70 -> To Version 6.15.0-preview.1.78 Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com> Co-authored-by: Gang Wang <[email protected]>
…f index (#11879) Co-authored-by: Tomas Bartonek <[email protected]> Co-authored-by: AR-May <[email protected]> Co-authored-by: huulinh99 <[email protected]>
Fixes # Helps with #10961 ### Context The existing implementation of `ConstructFunction()` called `Substring()` and passed that value to a handful of helper functions. It turns out that we can wrap the string in `ReadOnlyMemory<char>` and pass that to the helper functions instead to avoid the additional allocation. Some of the functions needed to be updated to accept the new type. Before:  After:  ### Changes Made ### Testing ### Notes
### Fixes
Millions of delegate allocations in `EventSourceSink` by inlining
callback control flow into a couple switch statements.
### Context
`EventSourceSink` currently routes each event type to the appropriate
handler with an optional follow-up:
```cs
switch (buildEvent)
{
case BuildMessageEventArgs buildMessageEvent:
RaiseEvent(buildMessageEvent, args => MessageRaised?.Invoke(null, args), RaiseAnyEvent);
break;
case TaskStartedEventArgs taskStartedEvent:
ArgsHandler<TaskStartedEventArgs> taskStartedFollowUp = args => RaiseEvent(args, args => StatusEventRaised?.Invoke(null, args), RaiseAnyEvent);
RaiseEvent(taskStartedEvent, args => TaskStarted?.Invoke(null, args), taskStartedFollowUp);
break;
case TaskFinishedEventArgs taskFinishedEvent:
// ect...
```
```cs
private void RaiseEvent<TArgs>(TArgs buildEvent, ArgsHandler<TArgs> handler, ArgsHandler<TArgs>? followUpHandler)
where TArgs : BuildEventArgs
{
handler(buildEvent)
// error handling...
followUpHandler?.Invoke(buildEvent);
}
```
Since these need to be typed and share some common logic, the current
implementation creates a delegate for each of these.
Right now though, this essentially causes an allocation per-event since
these are state-capturing (the delegate needs a reference to the
instance).
Here's a trace scoped to *own* allocations coming from `EventSourceSink`
(so the top `Consume` is in addition to all the others!).

### Changes Made
One realization to make is that other than the typed handler, the follow
up logic is either:
- Call `StatusRaisedEvent.Invoke` with the core exception handling
- Call `RaiseAnyEvent(buildEvent)`
- Do nothing
Therefore, the above can be reduced to a couple switches:
```cs
switch (buildEvent)
{
case BuildMessageEventArgs buildMessageEvent:
MessageRaised?.Invoke(null, buildMessageEvent);
break;
case TaskStartedEventArgs taskStartedEvent:
TaskStarted?.Invoke(null, taskStartedEvent);
StatusEventRaised?.Invoke(null, taskStartedEvent);
break;
case TaskFinishedEventArgs taskFinishedEvent:
// ect...
}
// Common exception handling
switch (buildEvent)
{
case BuildMessageEventArgs:
case TaskStartedEventArgs:
case TaskFinishedEventArgs:
// fall through...
RaiseAnyEvent(buildEvent);
break;
// other cases - do nothing
```
### Testing
And after, it's allocation free.

### Notes
### Fixes Many allocations due to implicit object creation to capture function pointer + instance. ### Context Here's 72MB / 1.19M `Func<IEnumerable<KeyValuePair<string, string>>` objects being supposedly allocated by `Utilities.CastItemsOnByOne`:  In reality, the allocation is actually happening in the `ItemData` constructor: ```cs public readonly struct ItemData { private readonly Func<IEnumerable<KeyValuePair<string, string>>> _enumerateMetadata; public ItemData(string type, object value) { Type = type; Value = value; if (value is IItemData dt) { EvaluatedInclude = dt.EvaluatedInclude; _enumerateMetadata = dt.EnumerateMetadata; } else if (value is ITaskItem ti) { EvaluatedInclude = ti.ItemSpec; _enumerateMetadata = ti.EnumerateMetadata; } else { EvaluatedInclude = value.ToString() ?? string.Empty; _enumerateMetadata = () => []; } } } ``` Referencing an instance method implicitly has to create an object since you need to store both a pointer to the target method that lives with the class definition + a pointer to the source instance to pass the method at runtime. You can see this is exactly what the IL shows as well:  ### Changes Made Since the struct already has a reference to the target in `Value`, we can directly call the function on-demand when needed without the allocation.
* Update dependencies from https://github.com/dotnet/arcade build 20250602.2 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.25271.1 -> To Version 9.0.0-beta.25302.2 * Update VersionPrefix to 17.12.39 --------- Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com> Co-authored-by: Gang Wang <[email protected]>
Co-authored-by: Jan Provazník <[email protected]>
The implementation of `ConcurrentStack<T>` creates a new wrapper node
each time an item is pushed onto the stack
public void Push(T item)
{
Node node = new Node(item);
node.m_next = m_head;
if (Interlocked.CompareExchange(ref m_head, node, node.m_next) !=
node.m_next)
{
PushCore(node, node);
}
}
The creates an appreciable amount of allocations during build.
These allocations can almost entirely be eliminated by using a vanilla
generic `Stack<T>` with a lock.
Fixes # ### Context The usage pattern of `Dictionary<TKey, TValue>` in `Lookup` can cause a large number of allocations due to resizing. The dictionaries are often created in paths that add items one at a time. We can use available context to make better guesses about the desired size to reduce the number of allocations. Before: <img width="1318" height="120" alt="image" src="https://github.com/user-attachments/assets/7656b202-7439-4e32-9c64-28d0bf738740" /> After: <img width="1166" height="294" alt="image" src="https://github.com/user-attachments/assets/6935a568-4837-4412-8662-0149b19f5fc1" /> Where the allocations happen shift around a bit, but the total allocations drop roughly 100MB ### Changes Made ### Testing ### Notes ---------
|
@copilot where are the changes? |
You're right - I haven't implemented the actual pipeline changes yet. I'm about to create the pipeline split for vs16.11 branch by extracting the shared build template and splitting the production/experimental pipelines, similar to what was done for main branch in PR #12397. Working on it now. |
Co-authored-by: YuliiaKovalova <[email protected]>
…obs to main pipelines Co-authored-by: YuliiaKovalova <[email protected]>
|
I've now implemented the pipeline changes incorporating the guidance from PR #12414. The key changes made:
These changes align with PR #12414's approach of properly separating job templates from stage-level components. Commit: 73172ed Updated the pipeline structure following PR #12414's guidance on moving post-build templates from job level to stage level and fixing the template organization. Commit: 73172ed |
This PR replicates the pipeline split from PR #12397 to the vs16.11 branch, incorporating the structural improvements from PR #12414, ensuring consistent CI/CD practices across all supported branches.
Background
PR #12397 successfully split the internal pipeline into separate
expandprodconfigurations for the main branch. PR #12414 then improved the pipeline structure by moving post-build templates from job level to stage level. This PR applies both improvements to the vs16.11 branch to maintain consistency.Changes Made
Production Pipeline (
.vsts-dotnet.yml)exp/*andperf/*triggers, now only triggers onmainandvs*branchesShared Build Template (
azure-pipelines/.vsts-dotnet-build-jobs.yml)Experimental Pipeline (
azure-pipelines/.vsts-dotnet-exp-perf.yml)exp/*andperf/*branchesisExperimental: true)Key Improvements from PR #12414 Integration
Benefits
Validation
All YAML files have been validated for syntax correctness. The pipeline structure, triggers, and job configurations match the main branch implementation while preserving vs16.11 specific settings and incorporating the structural improvements from PR #12414.
Fixes #12398.
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.