-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Replace OPTIMIZE_FOR_SIZE with feature switch #111743
Replace OPTIMIZE_FOR_SIZE with feature switch #111743
Conversation
Tagging subscribers to this area: @dotnet/area-system-linq |
Oh nice: Size statisticsPull request #111743
|
I would love it if we could remove the compilation constant, have a single build, and just achieve desired size reduction in certain environments via the feature switch. |
Do you have any opinion on how to achieve the "The only problem is maintainability - how do we remember that we should only call the virtuals under a !IsSizeOptimized check." part? I was thinking about leaving the .SpeedOpt.cs files around to keep the virtuals separated at least in files. It won't help much, but it's at least something. |
Hmm, I guess this is the spot where we could use the generalized feature guards. @sbomer - did we implement that part? (I'm not able to find it right now, so my guess is no, but wanted to check.) |
Nope, only for RequiresUnreferencedCode/RequiresDynamicCode/RequiresAssemblyFiles. |
5f66281
to
eb3fe5c
Compare
/azp run runtime-nativeaot-outerloop |
Azure Pipelines successfully started running 1 pipeline(s). |
I marked this ready for review. Not all tests pass. It turns out we cannot fully replace this with a feature switch. This part is the problem:
We cannot express "this class only implements the interface if a feature switch is defined". And this is a popular interface. I'll try to measure how much that matters in a separate PR (#111794). The extra interface will only really matter for AOT. We can adjusts the test to expect this to pass, that's the least problem. @ivanpovazan is it easy enough to measure the size impact of this on iOS? This is replacing the special build of System.Linq with a common assembly that is same across platforms. There's now a feature switch to enable size savings. To enable the feature switch, one currently needs to add: <ItemGroup>
<RuntimeHostConfigurationOption Include="System.Linq.Enumerable.IsSizeOptimized"
Value="true"
Trim="true" />
</ItemGroup> The expectation is that we would introduce a property to wrap it, and default the property on iOS/Android/etc. to |
We could have an analyzer just for that library. Better would be to build out the unimplemented parts of feature guards and then use it here. We could explore having a separate build of the library that we test but don't ship and that implements the guarded functionality to throw. But probably most simply, we could rely on size regression tests to point out any place we make a mistake and then fix it, as we do with other performance-focused issues. |
Unfortunately it is tricky. Especially since we would like to measure the size impact on MAUI iOS apps and net10 builds are currently unstable. Would it be possible to apply these changes on top of net9 branch (not sure if there are any dependencies from the point we branched to net10) ? I could then use the official net9 workloads with local build of your changes to see the impact. |
Yeah, this applies pretty cleanly to 9.0 and I don't think there's prerequisites. Here's a branch: https://github.com/MichalStrehovsky/runtime/pull/new/sizeoptswitch-net9 The important bit when measuring is that we need to set the feature switch, or this will be a regression for sure. Here are the numbers for the couple test apps with native AOT using OPTIMIZE_FOR_SIZE. It is better with the ifdef, but not by much. I'd expect a similar picture for Mono AOT. Size statisticsPull request #111794
|
(Need to compare with the table in my previous comment, not the "size before". The "size before" is main branch.) |
Yeah, I guess that would work. It will require some skill to root cause but if enough people remember that some virtuals are special (and we should probably keep them special in the .SpeedOpt.cs files), it should be workable. |
/azp run runtime-nativeaot-outerloop |
Azure Pipelines successfully started running 1 pipeline(s). |
@stephentoub @dotnet/ilc-contrib this is ready for review. I tested IL-level trimming with a test app and I'm getting the same results as with native AOT (no new methods seem to be added compared to just using the ifdef, modulo the methods that implement the popular interfaces mentioned in #111743 (comment)). We can react if there's something unexpected on iDevices later. I don't expect it based on the above testing that I am equipped to do. I'm also planning to introduce a property to control this and default it to enabled for places that used the ifdef until now:
(@ivanpovazan are these the right places? Did I forget any? Anyone else we should loop in and might know?) Before I add those, I'd like to have a consensus on the property name. Does anyone have suggestions? Or any objections to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the MSBuild property, I think UseSizeOptimizedLinq
sounds good and fits our existing naming patterns.
src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
Outdated
Show resolved
Hide resolved
Thank you, here are the results for:
@MichalStrehovsky It seems the optimization only cuts-off the regression introduced, is that expected? PS I will measure Mono as a follow-up. |
Regarding: iOS/Android
Regarding: wasm/wasi
/cc: @pavelsavara @lewing |
Yep, this just replaces ifdef with a configurable feature switch. There were valid questions in #109978 (that introduces a feature switch for LINQ) why we should have both ifdefs and feature switches. So this is a preparatory change for that. |
/azp run runtime-nativeaot-outerloop |
/azp run runtime-extra-platforms |
Azure Pipelines successfully started running 1 pipeline(s). |
1 similar comment
Azure Pipelines successfully started running 1 pipeline(s). |
<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. --> | ||
<PropertyGroup> | ||
<TargetPlatformIdentifier>$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))</TargetPlatformIdentifier> | ||
<OptimizeForSize Condition="'$(TargetPlatformIdentifier)' == 'browser' or '$(TargetPlatformIdentifier)' == 'android' or '$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos'">true</OptimizeForSize> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OptimizeForSize
was set true for some targets but it's not anymore.
WasmFeatures.props
set the default for wasm workload, but default runtime pack also needs to be optimized for size - when people don't use workload in Blazor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OptimizeForSize
was set true for some targets but it's not anymore.
Yes, that is the purpose of this PR. To only have one build of this library (#111743 (comment))
WasmFeatures.props
set the default for wasm workload, but default runtime pack also needs to be optimized for size - when people don't use workload in Blazor.
Could you please point me to which .targets we need to update? Is it the Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets linked from #111743 (comment)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(To be clear, the OptimizeForSize property controls whether we build System.Linq assembly in a not-fully-compatible manner (see the tests I'm touching where it behaves differently) that has better size characteristics. This PR deletes that build and changes it to always build the compatible Linq implementation. It introduces a feature switch that allows switching to the not-fully-compatible mode. The default for the feature switch is disabled. It can be enabled where the tradeoff is worth it (I have PRs out enabling it in places, linked above). The advantage is that now if a customer runs into the incompatibility, they can just set a property and unblock themselves.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think maybe we need both
@maraf
I hope that we run ILLink during publish when wasm-workload is not installed ?
Is there better place ?
and for workload
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! Folded the BlazorWebAssembly.targets change into dotnet/sdk#46375.
WasmFeatures.props change is already part of the PR here.
* main: (31 commits) More native AOT Pri-1 test tree bring up (dotnet#111994) Fix BigInteger outerloop test (dotnet#111841) JIT: Run 3-opt once across all regions (dotnet#111989) JIT: Check for profile consistency throughout JIT backend (dotnet#111684) [JIT] Add legacy extended EVEX encoding and EVEX.ND/NF feature to x64 emitter backend (dotnet#108796) [iOS][globalization] Fix IndexOf on empty strings on iOS to return -1 (dotnet#111898) System.Speech: Use intellisense xml from dotnet-api-docs (dotnet#111983) [mono][mini] Disable inlining if we encounter class initialization failure (dotnet#111754) [main] Update dependencies from dotnet/roslyn (dotnet#111946) Update dependencies from https://github.com/dotnet/arcade build 20250129.2 (dotnet#111996) Try changing the ICustomQueryInterface implementation to always return NotHandled instead of Failed to defer back to the ComWrappers impl. (dotnet#111978) Combined dependency update (dotnet#111852) Replace OPTIMIZE_FOR_SIZE with feature switch (dotnet#111743) Fix failed assertion 'FPbased == FPbased2' (dotnet#111787) Add remark to `ConditionalSelect` (dotnet#111945) JIT: fix try region cloning when try is nested in a handler (dotnet#111975) Use IRootFunctions in Tensor.StdDev (dotnet#110641) Remove zlib dependencies from Docker containers (dotnet#111939) Avoid `Unsafe.As` for `Memory<T>` and `ReadOnlyMemory<T>` conversion (dotnet#111023) Cleanup membarrier portability (dotnet#111943) ...
When I was looking at this last time, I noticed the virtual method overrides and though it's not solvable with feature switches. But looks like the virtual methods are not actually called outside
!OPTIMIZE_FOR_SIZE
code so we might still be able to trim things correctly if it's a feature switch (we can remove unused virtual methods).The only problem is maintainability - how do we remember that we should only call the virtuals under a
!IsSizeOptimized
check. The ifdef was a nice forcing function enforced by the compiler.