Skip to content
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

Proposed .NET 7 Breaking Changes #7131

Open
richlander opened this issue Jan 21, 2022 · 51 comments
Open

Proposed .NET 7 Breaking Changes #7131

richlander opened this issue Jan 21, 2022 · 51 comments

Comments

@richlander
Copy link
Member

richlander commented Jan 21, 2022

The following list defines breaking changes that are proposed for .NET 7.

Implemented:

Planned for .NET 7:

Unclear plan:

Moved to .NET 8:

No longer honor multi-level-lookup (MLL) for .NET 7+ apps, runtimes and SDKs

Proposal: https://github.com/dotnet/designs/blob/main/accepted/2022/disable-multi-level-lookup-by-default.md
Change: dotnet/runtime#67022
Notice: dotnet/docs#28836

Multi-level-lookup (MLL) -- specified with DOTNET_MULTILEVEL_LOOKUP -- is relevant when an alternate location is specified to look for SDKs and/or runtimes, via DOTNET_ROOT. When enabled, MLL expands the search space for runtimes and SDKs to include the global .NET installation location in addition to the DOTNET_ROOT location. This can result in a runtime or SDK being selected from the global installation location rather than the intended private DOTNET_ROOT location, whether that is desired or not.

Motivation for break:

  • MLL results in lots of confusion, both for .NET developers and even for folks working on the .NET platform.
  • MLL behavior differs by OS: enabled by default on Windows, not available on any other OS.
  • No known scenario where this feature is required.

Stop adding 32-bit .NET to the PATH for .NET 7+ runtimes and SDKs (on x64 machine)

Proposal: dotnet/sdk#22030
Change: dotnet/runtime#69902
Notice: dotnet/runtime#70039

Note: This change was made to .NET Core 3.1, .NET 6, and .NET 7.

When you install .NET, the installer adds the install location to the PATH environment variable. This behavior is sound and enables the OS to find dotnet when you use it. We build multiple .NET for multiple architectures (x86, x64, ...). In some cases, an OS supports multiple architectures and then we need a plan for how multiple .NET locations in the PATH works. In short, it doesn't work well. Going forward, we should only ever add the OS native-architecture .NET install location to the PATH. We already started doing that on Apple M1 (Arm64) machines (we only add the Arm64 .NET to the PATH, not the x64 version). We need to repeat this pattern with Windows now, with the 32-bit .NET build.

Motivation for break:

  • Adding multiple builds of .NET to the PATH is incoherent, results in bad UX, and is difficult to explain.
  • We have already started down this direction with x64 on Arm64. It makes sense to make the product consistent, by repeating the pattern with x86 on x64 and x86 on Arm64.

dotnet build/publish uses the implicit SDK RID for RID-specific apps by default

Proposal: dotnet/sdk#23539
Change: dotnet/sdk#22314
Notice: not actually breaking

Today, you must specify a RID when you specify "--self-contained". That's not a useful requirement, particularly if you want to app to be able to run in a given environment (like CI) and don't know what that is ahead of time. Instead, the implicit SDK RID should be used in any scenario where a RID is needed but one isn't provided. The most obvious example of that is to produce a self-contained app.

FYI: This is arguably not a breaking change.

Motivation for break:

  • The CLI should have good defaults where correct behavior can be provided in absence of the user provided a specific value.
  • RID targeting is confusing. We should make it simpler.

dotnet publish/pack produce release assets by default

Proposal: dotnet/sdk#23551

Our basic guidance to developers since .NET Core 1.0 has been "use build for development and publish for prod". Given that, it would make a lot more sense if publish defaulted to a release build. Debug builds run observably slow (sometimes you can see this with just your eyes; no stopwatch required). It is near certain that plenty of .NET apps are deployed as debug due to the current defaults. The CLI would be much better if it offered more differentiated options. There should probably be a more broad re-assessment of build and publish but I'm not digging into that here. I'm proposing a much more simpler change (that doesn't preclude broader changes later; in fact, this change would encourage them).

Motivation for break:

  • release assets have better performance (sometimes by a large margin).
  • Some CLI commands bias strongly to prod, which provides us with a good opportunity to provide a differentiated prod experience. We don't want developers deploying debug assets into prod unwittingly. Differentiated experiences can help that.

dotnet build/publish produces RID-specific apps by default

Proposal: dotnet/sdk#23540

The .NET SDK has produced portable apps since .NET Core 1.0. That may or may not have made sense, but it no longer does now. Portable apps are bigger, slower to startup, and less reliable in some scenarios. In addition, portable apps are not fully coherent since they have a RID-specific executable but portable assets. That means that you can use the executable for one RID environment and cannot for any other. It's an odd design choice. A perfect example is containers. RID-specific apps are always better for containers. Another example is client apps. Client apps require an executable, such that they should always be RID-specific.

Motivation for break:

  • Portable apps are good for a small subset of scenarios.
  • RID-specific apps are the only good option for a variety of popular scenarios.

dotnet build/publish does not produce an exe/apphost for portable apps by default

Proposal: dotnet/sdk#23545

Portable apps are intended to run in multiple environments. By definition, an executable is RID-specific and therefore only compatible with one of the environments in which a portable app can run. It's possible that developers are happy with this asymmetry, but should opt into that experience.

Motivation for break:

  • The apphost makes portable apps incoherent.
  • It is easier to explain how to use portable apps in absence of an executable apphost.

Precompile with AVX2 (x64) or NEON (Arm64) instructions for better startup performance

Proposal: dotnet/designs#173

Vector (SIMD) instructions are now one of the key performance pillars of the .NET platform. Today, pre-compiled Ready-to-Run (R2R) code targets the SSE2 instruction set on x64. We require the JIT to tier the platform to tier 1 in order to take advantage of larger/newer vector instructions (like AVX2). That model penalizes startup on modern hardware and also places a hard dependency on tiered compilation for good performance. Ideally, R2R code was already very good, and tiered compilation was reserved for only the highest value methods. We can achieve that by compiling R2R code with AVX2 (x64) and NEON (Arm64) by default. We would not change the Windows 32-bit or Arm 32-bit builds.

Note: After this change, machines with AVX2 will have better performance, while machines w/o it will end up with worse performance since some R2R methods will be rejected and require jitting where they previously did not.

Motivation for break:

  • Highest performance, including at startup.
  • Align product to modern hardware.

For additional context, SSE2-compatible hardware was first released in 2000, and the same for AVX2 in 2013.

@richlander
Copy link
Member Author

richlander commented Jan 21, 2022

@tannergooding
Copy link
Member

tannergooding commented Jan 21, 2022

Overall LGTM.

One nit is that "Require AVX2 (x64) or NEON (Arm64) hardware for best performance" is a confusing statement. We aren't requiring AVX2 and we will continue working on hardware without. Instead we are assuming that the hardware will have AVX2 support by default and will prep our assemblies using crossgen that assumes AVX2 is available. This will provide faster startup and steady state throughput on modern hardware but will force older hardware to reject the impacted R2R method entries and fallback to jitting them instead.

For ARM64, Neon (otherwise known as AdvSimd) is already considered baseline and there should be no "break" here.

Also worth noting that the below is inaccurate:

For additional context, SSE2-compatible hardware was first released in 2004, and the same for AVX2 in 2013.

For reference, SSE2 was introduced in 2000 alongside the Pentium 4. It's been required on all x64 CPUs which were first introduced in 2003. AVX2 was introduced in 2013 and has been available on most CPUs since then. There are some low-end/budget CPUs (like older Intel Atoms) that have shipped since then without AVX2 support, but they are believe to be a minority overall.

@richlander
Copy link
Member Author

I added a note to clarify and updated the dates. Helps?

@tannergooding
Copy link
Member

Yes. I think the wording on the title is the most confusing part, however, and the concern is that its going to show up in some "clickbait" blog post somewhere.

I think changing:

Require AVX2 (x64) or NEON (Arm64) hardware for best performance

to

Precompile with AVX2 (x64) or NEON (Arm64) support for better startup performance

might be better. Thoughts?

@richlander
Copy link
Member Author

Done.

@marcpopMSFT
Copy link
Member

Thanks for putting this together. I'm aware of and supportive of most of these. For the SDK implicit RID, I assume that the proposal once written will include the locations we want to enable the default?

I'd need to see the proposal for portable to weigh in on that one as I don't know enough about what our customer expectations are and whether this would cause more confusion than it would save.

@richlander
Copy link
Member Author

Yup. Will do what you've asked.

@joeloff
Copy link
Member

joeloff commented Jan 21, 2022

Thank you for the PATH writeup @richlander

@samsp-msft
Copy link
Member

Another wording one:

dotnet build/publish does not produce an exe/apphost for portable apps by default

how about:

dotnet build/publish for portable apps, by default no longer produces an exe/apphost

@richlander
Copy link
Member Author

Seems like passive construction, right?

@richlander
Copy link
Member Author

richlander commented Jan 22, 2022

@marcpopMSFT -- all of the proposals now have lengthy write-ups. Tell me if that works.

I just added another one on release mode.

@richlander richlander changed the title .NET 7 Breaking Changes Proposed .NET 7 Breaking Changes Jan 23, 2022
@DamianEdwards
Copy link
Member

Some of these changes will potentially impact ASP.NET Core scenarios more than console/client scenarios. Indeed some of the existing default behaviors are there because the first version of .NET Core only had ASP.NET Core as a primary workload. Can we please ensure we explicitly loop in folks from the ASP.NET Core & VS web tooling sides to weigh in on all these proposals.

@Tratcher @vijayrkn @sayedihashimi @davidfowl

@richlander
Copy link
Member Author

Yes. publish is the one that was mostly there for ASP.NET Core.

I'm not a fan of the current build/publish dichotomy. Ideally, we'd make this proposed change (release for publish by default), and use that as a way to (re-)start the bigger conversation while not blocking on delivering value (even if the final changes makes this change moot).

@davidfowl
Copy link
Member

@richlander The problem is that the proposal doesn't say much about how ASP.NET Core uses publish and what the alternative is. The details are a bit anemic on those scenarios at the moment...

@richlander
Copy link
Member Author

richlander commented Jan 23, 2022

I may be missing something, but we're just talking about whether the developer has to do one of the following to opt-out of default behavior (current vs proposed behavior):

  • dotnet publish -c Release
  • dotnet publish -c Debug

Is it more complicated that that?

I didn't mention ASP.NET Core since I think of it as the primary use case for publish. As a result, I was primarily thinking about ASP.NET Core.

I also wrote up some more insight on my RID-specific-thinking. dotnet/sdk#23540 (comment)

@MarcoRossignoli
Copy link
Member

cc: @nohwnd

@davidfowl
Copy link
Member

@richlander ie recommend sitting with the asp.net stakeholders so you can make sure the proposal isn’t missing some important pieces (I think it is).

@Perksey
Copy link
Member

Perksey commented Jan 23, 2022

The no exe/apphost by default is an interesting change, given iirc .NET Core 1.0 also didn’t do this by default but the exe/apphost was added later (presumably for a reason).

Is the reasoning for originally adding the exe/apphost by default documented somewhere for reference?

@richlander
Copy link
Member Author

We wanted exe/apphost for fx-dependent apps from the start but didn't prioritize that. We did that in .NET Core 3.0. That's the same time we added Windows client apps, which require an exe. That explains the timing. It would have been strange to only do that for client apps.

@agocke
Copy link
Member

agocke commented Jan 23, 2022

@Perksey Note that the end result of Rich's proposal is still to almost always have an apphost. If apps are RID-specific by default, and RID-specific apps have an apphost by default, most everything has an apphost. It's only portable apps, which will be moved behind an "any" RID, that won't have one.

@richlander
Copy link
Member Author

Yup. These topics were have largely not been re-assed since they were first designed. I've been working on improving this area for multiple releases (largely failing). Here's a proposal I wrote in 2019 (that didn't go anywhere) to provide some more insight. https://gist.github.com/richlander/2c8614825f9a289109ce4fd7e9ceceeb

@normj
Copy link

normj commented Jan 24, 2022

The change to RID specific by default makes me the most nervous. I'll need to think on it more but by gut reaction is not in favor.

We have many developers that build on Windows and deploy to Linux (non-container) environments. Right now they don't have to think anything about this because .NET is portable by default. This is commonly developers less experienced for Linux but want to use our managed service for Linux hosting to reduce cost. With this change we would have to train the potentially less advanced developers how to use non-default settings to get their application deployed correctly. Seems like we are raising the complexity bar for .NET instead of trying to make lives easier.

Would this breaking change happen if they are using .NET 7 SDK or only targeting .NET 7 and above. Meaning if I have the .NET 7 SDK but still targeting .NET 6 would I be affected?

I assume for .NET CLI tools we would have to force the non-default setting to make the tool portable?

Also for defaulting the RID value what is the SDK's behavior if it can't determine the RID? Will the build fail, default to some generic RID or fallback to portable?

Since right now there is a --runtime switch I would assume this gets morphed into how to change the default computed RID. Would a new switch be added to indicate RIDless or have a special value for --runtime?

@richlander
Copy link
Member Author

richlander commented Jan 24, 2022

This is similar to @DamianEdwards concern. One option is that ASP.NET Core templates to set a property for portable apps. We'd also make it easy to reverse the setting in containers.

You could also ask why make ASP.NET Core apps deal with this, and instead get client apps to opt into RID-specific behavior, since they seem to be the one that want it most.

This all comes down to philosophy. We either make the the platform native-like (like Go) or more dynamic-like (like Node.js) at its core. I'm clearly in the native-like camp. I think dynamic-like as base behavior is a losing proposition for .NET. For example, .NET doesn't even have a good REPL. .NET has so much to offer by having so much capability, but today the CLI/build defaults are a strange mix w/o a clear personality or motivation. That's the part I'm wanting to fix, by making the defaults very clearly native-like and making it easy to opt-in to the dynamic-like behavior. That should make everyone happier.

Would this breaking change happen if they are using .NET 7 SDK or only targeting .NET 7 and above. Meaning if I have the .NET 7 SDK but still targeting .NET 6 would I be affected?

Good q. I didn't state that, but I'm thinking we'd tie this to TFM, so .NET 7 SDK targeting .NET 6 TFM would NOT see a change in behavior.

I assume for .NET CLI tools

Good call. Yes, tools should remain portable. There is an excellent reason for that behavior.

Also for defaulting the RID value what is the SDK's behavior if it can't determine the RID?

This never happens. The SDK always knows it's RID. If it doesn't, that's a more general product failure and many other things won't work.

rich@MacBook-Air ~ % dotnet --info | grep RID
 RID:         osx-x64

Would a new switch be added to indicate RIDless or have a special value for --runtime

The any RID already means this. We could potentially add a CLI switch of --portable to be a convenience on that.

@brianrob
Copy link
Member

@richlander, great to see the MLL changes listed here. Thanks!

@adamsitnik
Copy link
Member

@richlander We have removed the .NET 5 FileStream compatibility mode. @danmoseley suggested to me that we should list it as a breaking change as well. I've added a description that you could reuse here: dotnet/runtime#55196 (comment)

@Perksey
Copy link
Member

Perksey commented Jan 24, 2022

@richlander @agocke Makes sense, thanks for that. :)

@richlander
Copy link
Member Author

We had a first sync on these today. I'll share plans when we get a bit further.

I asked if anyone wanted to defend multi-level-lookup. No one did. It feels so sad and abandoned. So sad.

@dazinator
Copy link

dazinator commented Jan 25, 2022

Precompile with AVX2 (x64) or NEON (Arm64) instructions for better startup performance

  • How much of an improvemrnt are we talking about?
  • How much of a detraction if AVX2 isn't available?

SSE2-compatible hardware was first released in 2000, and the same for AVX2 in 2013.

  • is there any justification to keep SSE2 as an allowed target and make AVX2 a new default. So if need be, a developer has the power? Just thinking that for the desktop consumer market where home users may be running Winforms / WPF & in future - MAUI apps on decades old pc's perhaps removal of the SSE2 support would introduce perf losses for those users and not gains? I am guessing this is not the primary use case for this feature though, and it is more for enabling cloud infra that runs millions of app startups per day to use hardware more effectively in data centres?

@richlander
Copy link
Member Author

We're still working on the numbers (improvements and regressions). You are right that this feature is motivated by cloud infra. One options is to make this change for Windows Arm64, Linux Arm64/x64 and macOS Arm64 only, and leave Windows x86/x64, and macOS x64 as-is. That would skirt most of the risk, although it would be unfortunate if our Windows x64 cloud build was unimproved. All topics to work through when we have better numbers.

@tannergooding
Copy link
Member

tannergooding commented Jan 25, 2022

is there any justification to keep SSE2 as an allowed target and make AVX2 a new default. So if need be, a developer has the power? Just thinking that for the desktop consumer market where home users may be running Winforms / WPF & in future - MAUI apps on decades old pc's perhaps removal of the SSE2 support would introduce perf losses for those users and not gains? I am guessing this is not the primary use case for this feature though, and it is more for enabling cloud infra that runs millions of app startups per day to use hardware more effectively in data centres?

To be clear, SSE2 support isn't being removed, it will just not be the target for the pre-jitted versions of the BCL libraries

Effectively all of the binaries we ship in the BCL are "pre-jitted" for a given baseline. This means on a typical application startup, these methods do not have to be jitted they can just be used "as-is". If they are executed more than ~30 times then Tiered Compilation will kick in and they will be rejitted for "your current hardware", improving overall application throughput because they are known to be "hot" (or frequently called methods).

Our previous baseline was SSE2 and this meant that startup was decently fast on all computers, but it also means that many programs needed rejit for "core" methods in Span, Array, and string to ensure that they are "optimally accelerated" for their hardware.

We are changing the new baseline to AVX2, which means that for essentially any computer released in the last decade will already get code that is "optimally accelerated" (this also puts this roughly "on parity" with what ARM64 hardware provides out of the box). If you happen to be on hardware without AVX2 support then your application startup will take longer because the pre-jitted code will be rejected and the method will have to be jitted before execution can start.

This should only impact startup and only for methods that actually use AVX/AVX2 instructions. If the method is "simple" and doesn't utilize any AVX/AVX2 instructions (this is primarily floating-point and SIMD instructions such as from System.Numerics.Vector2/3/4, System.Numerics.Vector<T>, or System.Runtime.Intrinsics.*; but also extends to some stack zeroing logic for methods with many locals) then there will be no need for the method to be rejitted as it isn't using any instructions that would prevent it from running on a given user's hardware.

@joeloff
Copy link
Member

joeloff commented Jan 25, 2022

@richlander for the PATH issue, because it's set through the host the fix in .NET 7 may not be sufficient unless we have the same change in 3.1, 5.0, 6.0.

Or we should just be very clear that if you only have .NET 7, you'll be fine, but once you mix previous versions, the issue can still surface.

@normj
Copy link

normj commented Jan 25, 2022

@richlander That is interesting debate to put .NET in the native Go/Rust trajectory by default. Would that make AOT being mainstream an eventual goal for .NET. If the goal that is to make .NET a native platform should R2R also be done by default in .NET 7?

@jssmotta
Copy link

I'd like an x64 to perform well as the x86 runtime does.
Even in Windows 11, the x86 runtime is faster than the x64.
This would not be true, but sadly is, even in WinForms ou ASP.NET Core apps, the x86 is twice faster, and I'll note complain about the difference of memory, that it's his x64 nature, but processing. Even in top hardware or not the x86 is better.

@danmoseley
Copy link
Member

Even in Windows 11, the x86 runtime is faster than the x64.

@jssmotta what version of .NET are you using? @davidfowl is this expected for ASP.NET?

@richlander
Copy link
Member Author

That is interesting debate to put .NET in the native Go/Rust trajectory by default. Would that make AOT being mainstream an eventual goal for .NET.

Yes and no. My motivation isn't oriented around AOT. Instead, I see that the defaults are not really a coherent personality. To some degree, I don't really care whether the defaults are node-like or go-like, as long as they strongly point in one direction and it is easy to switch. If I look at the broad set of investments we've made (and expect to make) and what we are asked for, they are much more native-learning than dynamic-leaning. Also, I think .NET does a better job providing value and aligning with experiences with folks who prefer native-leaning platforms. The beauty of .NET is that you can get a very nice range of experiences, depending (primarily) on your build configuration.

I see the fact that my proposal aligns with AOT as a nice bonus.

Our UX needs/challenges and yours are very similar. I'm going to take a few days to look at some more ASP.NET UX to see if I can find a good option.

@jssmotta
Copy link

.NET 6
But this behavior is the same since .NET Framework 4.x!
Let me tell you, I too deployed an app with ASP.NET AJAX using C# 10 (advocati.net - ask me a user and pwd if you like to try), and it makes a huge difference, about 500% faster. When running in the x86 bin was ok, but when I deployed the same code in x64 bin the site goes down. I conduct this test directly in production, with kind support from smarterasp.net, to be sure.
P.S.: When migrating to c# 10 I refactored all c# code using ReSharper, upgrading what was possible to c# 10 on the limit of c#10 on .NET Framework 4.8.
On WinForms (.NET 6) the x64 is slower than the x86, twice slower at least, on the same scenario and with the same source code.

@samsp-msft
Copy link
Member

The change to RID specific by default makes me the most nervous. I'll need to think on it more but by gut reaction is not in favor.

We have many developers that build on Windows and deploy to Linux (non-container) environments. Right now they don't have to think anything about this because .NET is portable by default. This is commonly developers less experienced for Linux but want to use our managed service for Linux hosting to reduce cost. With this change we would have to train the potentially less advanced developers how to use non-default settings to get their application deployed correctly. Seems like we are raising the complexity bar for .NET instead of trying to make lives easier.

How are these users deploying to Linux? Is it manually or via a publish action in Visual Studio?

I am assuming that when building with an implicit RID, we'll have the same output directory structure as for explicit RID, so you get something like bin/debug/net7.0/win10-x64, so it would pretty clear what the app has been built for.

@jssmotta
Copy link

bin/debug/net7.0/win10-x64

Hello, I make the correct build for each scenario, as you ask for 'bin/debug/net6.0/win10-x64' and x86. And my apps do not target Linux and I even know how to make such tests. So I bet this could be an issue in Microsoft OS or intrinsic of the architecture of .NET since .NET 4.x to .NET 5, .NET 6. I did not say before, but .NET 5 had this same slow behavior on the x64 bin.

@richlander
Copy link
Member Author

richlander commented Jan 26, 2022

We have many developers that build on Windows and deploy to Linux (non-container) environments. Right now they don't have to think anything about this because .NET is portable by default.

This is the one scenario that would be made worse by the proposal. One option we're playing with is leaving ASP.NET Core as portable by default and make it easy to switch. The other idea is to make all app-types RID-specific by default and make the ASP.NET Core publish profile experience portable by default.

Do your apps use publish profiles? Or just dotnet publish? @jssmotta

@normj
Copy link

normj commented Jan 26, 2022

The change to RID specific by default makes me the most nervous. I'll need to think on it more but by gut reaction is not in favor.
We have many developers that build on Windows and deploy to Linux (non-container) environments. Right now they don't have to think anything about this because .NET is portable by default. This is commonly developers less experienced for Linux but want to use our managed service for Linux hosting to reduce cost. With this change we would have to train the potentially less advanced developers how to use non-default settings to get their application deployed correctly. Seems like we are raising the complexity bar for .NET instead of trying to make lives easier.

How are these users deploying to Linux? Is it manually or via a publish action in Visual Studio?

I am assuming that when building with an implicit RID, we'll have the same output directory structure as for explicit RID, so you get something like bin/debug/net7.0/win10-x64, so it would pretty clear what the app has been built for.

@samsp-msft I know our AWS tooling for VS right now does a portable publish that can be used for deploying to either Windows or Linux AWS Elastic Beanstalk environments (WebApp hosting without containers). There is no reason we can't update that code to be RID aware for the target deployment.

Our tooling for Lambda (Serverless functions) has always created deployment bundles using the RID for all of the reasons stated here, we need to get the startup time to be as fast as possible.

@normj
Copy link

normj commented Jan 26, 2022

This is the one scenario that would be made worse by the proposal. One option we're playing with is leaving ASP.NET Core as portable by default and make it easy to switch. The other idea is to make all app-types RID-specific by default and make the ASP.NET Core publish profile experience portable by default.

@richlander That will be interesting challenge to explain why generic console is RID specific but ASP.NET Core is portable. Aren't a lot of the scenarios you are talking about for the new client side targets like Maui which already have platform specific TFW. Would that work to be RID specific if the TFW is platform specific. The exception I can think to that is Blazor WASM but I assume you could do something special for that since it targets its own specific SDK (Microsoft.NET.Sdk.BlazorWebAssembly).

To be clear I'm not hugely opposed to the change. I just worry because to me this is a pretty big redefinition of .NET being a portable VM platform that optionally has some performance knobs to tweak things with tradeoffs. And with all of the variant Linux RIDs I can see this potentially causing havoc with Linux build system of one variant (Ubuntu) being deployed to another Linux variant (Alpine). This could cause subtle issues with which third party native dependencies get bundled up that could be a pain to track down.

@richlander
Copy link
Member Author

I just worry because to me this is a pretty big redefinition of .NET being a portable VM platform

That's where we started since we only had web and console. Also, that was before containers became big, where portability is (for the most part) adds unnecessary cost. So, yes, that's what I'm pushing to change. The platform won't change. I just consider portability to be a more advanced scenario that people should opt into.

that optionally has some performance knobs to tweak things with tradeoffs.

This may be a stretch, but this isn't really "knobs with tradeoffs". For all client scenarios, portable only offers cost w/no benefits. It's these "non-specific web deploys" that we still need to reason about.

@richlander
Copy link
Member Author

Ah, I couldn't find this doc earlier (and convinced myself I hadn't written it). It provides more insight into my thinking. https://github.com/dotnet/designs/blob/main/accepted/2021/architecture-targeting.md

@dazinator
Copy link

dazinator commented Jan 27, 2022

@richlander
A quote from your doc linked above:

portable self-contained app") because it isn't technically possible.

I humbly accept your challenge.
Give me 5 years worth of investment. I'll give you a rid called all that outputs a portable file in the form of a zip. The zip can be extracted to any platform. Inside is the application built for each rid. Click on the right executable for your platform.

  • portable
  • self contained
  • desirable

Perhaps technically possible.. possibly not technically desirable.

Fun challenge aside, I assume that the default UX for library projects would still be that a build outputs a single binary targeting any as the rid and a dotnet pack would result in this any binary being distributed? Build time costs electricity - i'd like to pass that cost to the consumer. This is a case where portability is the default paradigm and rids are the logical leap? So I think you are highlighting the inverse is true for executable projects only.

@richlander
Copy link
Member Author

I assume that the default UX for library projects would still be that a build outputs a single binary targeting any as the rid and a dotnet pack would result in this any binary being distributed?

Yes.

@dazinator
Copy link

dazinator commented Jan 27, 2022

For all client scenarios, portable only offers cost w/no benefits

Does client include console apps? A big benefit for portable console apps is ease of distribution. This often trumps performance optimisations for variety of reasons. If I want my console app to run on any of our Azure DevOps agents for example, I can publish a single artifact with the portable binaries and every agent can download that same artifact and use it in an identical way via dotnet [path to dll] There is no fragmented logic I have to add or maintain to make each agent resolve the correct executable based on the platform. This means less fragmented documentation. Less chance of error when the wrong exe is used. Quicker builds and smaller packages.

@richlander
Copy link
Member Author

I meant desktop/mobile client.

You are right on console. I also use that experience. The only conversation on the table is whether that will be the default vs opt-in. It definitely won't be taken away. In any case, I still have to write-up a plan that works through all the workflows. I'm writing a different spec at the moment, but will do that next.

@OronDF343
Copy link

OronDF343 commented Feb 20, 2022

dotnet build/publish produces RID-specific apps by default

I am also concerned about this proposed change. Here are my thoughts on it:

  • In the CLI, I think that the current defaults should not be changed, as stated by others above. Maybe, if the behavior was not explicitly specified in the arguments, an informational message could be shown to let the developer know about RID-specific targeting.
  • In VS, I think that the problem could be solved with better UX for the publish profile creation wizard.
    Currently, after choosing a publish target, the wizard closes and defaults are set automatically. Instead:
    • A new final step could be added to the wizard, which will allow the developer to change the publish settings. This step should only include the following relevant settings: Configuration, Target Framework, Deployment Mode, Target Runtime, as well as everything in the File Publish Options category.
    • This could even be split into two steps in the wizard, where the first step will be to choose the Deployment Mode and RID (default is auto), and the next step will contain all of the aforementioned settings.

EDIT: I'd like to add that for those developing in a Windows environment and publishing to Docker (Linux), the change in behavior would definitely affect anyone following this tutorial.

@EgorBo
Copy link
Member

EgorBo commented Apr 10, 2022

@richlander

Precompile with AVX2 (x64) or NEON (Arm64) instructions for better startup performance

It should make perfect sense to also enable more advanced ARM64 instructions by default for RID=osx-arm64, because we already know that M1 is a baseline, e.g. we should enable lse by default - it means all Interlocked C# APIs will be faster and smaller:

int Test(ref int a, int b, int c) => 
    Interlocked.CompareExchange(ref a, b, c);

Currently we prejit (r2r) it like this:

G_M59805_IG02:              ;; offset=0008H
        885FFC64          ldaxr   w4, [x3]
        6B02009F          cmp     w4, w2
        54000061          bne     G_M59805_IG03
        8800FC61          stlxr   w0, w1, [x3]
        35FFFF80          cbnz    w0, G_M59805_IG02
						;; size=20 bbWeight=1    PerfScore 8.50
G_M59805_IG03:              ;; offset=001CH
        D5033BBF          dmb     ish
        2A0403E0          mov     w0, w4

if prejitted with crossgen2 --instruction-set:lse (ARM64 8.1):

G_M65452_IG01:              ;; offset=0000H
        2A0303E0          mov     w0, w3
        88E0FC22          casal   w0, w2, [x1]

Apple already do the same with its clang - if you compile a simple app without specifying any arch it will be using Apple-M1 as a baseline (e.g. it uses ARM64 8.3 ldapr, etc)

image

@stunney
Copy link

stunney commented Feb 28, 2023

donet pack --output in 6.0.300+ BREAKS with --output not supported. This breaks a LOT of functionality in GitHub Actions. Why was this broken?

@baronfel
Copy link
Member

@stunney are you meaning the breaking change described here? If so this was in 7.0.200 so I'm confused why you're seeing it in 6.0.300+. This was a regression and should be fixed in 7.0.201, which I was able to use in GitHub Actions. If you have a pointer to an erroring project or CI pipeline we could investigate further?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests