[HN Gopher] Performance Improvements in .NET 7
       ___________________________________________________________________
        
       Performance Improvements in .NET 7
        
       Author : mikece
       Score  : 196 points
       Date   : 2022-08-31 13:18 UTC (9 hours ago)
        
 (HTM) web link (devblogs.microsoft.com)
 (TXT) w3m dump (devblogs.microsoft.com)
        
       | Mindless2112 wrote:
       | > _try running Console.WriteLine((object)new uint[42] is int[]);,
       | and you'll find it prints out True_
       | 
       | I knew .NET's array variance rules were kinda bad, but that's
       | really surprising.
        
       | nimish wrote:
       | Interesting that branch was chosen over CMOV in the ToString bool
       | case, it's a PGO case study: https://discourse.llvm.org/t/rfc-
       | cmov-vs-branch-optimization...
        
       | rkagerer wrote:
       | That blog site could use some performance enhancements. Anyone
       | have a link to a static copy?
        
       | mastax wrote:
       | There was a (very long) post recently talking about the Regex
       | performance improvements in .NET 7[0]. It's comprehensive and
       | well written and I found it very interesting. I love the new
       | Regex source generator not just because of the performance but
       | because you can step into the C# code and understand pretty well
       | what the regex is doing.
       | 
       | [0]: https://devblogs.microsoft.com/dotnet/regular-expression-
       | imp...
        
       | hardware2win wrote:
       | Damn
       | 
       | Stephens posts exciting as always
        
       | giancarlostoro wrote:
       | Native AOT will come with .NET 7
       | 
       | https://devblogs.microsoft.com/dotnet/performance_improvemen...
       | 
       | > Native AOT is different. It's an evolution of CoreRT, which
       | itself was an evolution of .NET Native, and it's entirely free of
       | a JIT. The binary that results from publishing a build is a
       | completely standalone executable in the target platform's
       | platform-specific file format (e.g. COFF on Windows, ELF on
       | Linux, Mach-O on macOS) with no external dependencies other than
       | ones standard to that platform (e.g. libc). And it's entirely
       | native: no IL in sight, no JIT, no nothing. All required code is
       | compiled and/or linked in to the executable, including the same
       | GC that's used with standard .NET apps and services, and a
       | minimal runtime that provides services around threading and the
       | like.
       | 
       | Of course it does have some downsides:
       | 
       | > It also brings limitations: no JIT means no dynamic loading of
       | arbitrary assemblies (e.g. Assembly.LoadFile) and no reflection
       | emit (e.g. DynamicMethod), everything compiled and linked in to
       | the app means the more functionality that's used (or might be
       | used) the larger is your deployment, etc. Even with those
       | limitations, for a certain class of application, Native AOT is an
       | incredibly exciting and welcome addition to .NET 7.
       | 
       | I wonder how things like ASP .NET will run with Native AOT in the
       | future.
        
         | lvass wrote:
         | I just can't help laughing profusely whenever I see MS doing a
         | Native something. Don't you folks remember Native HTML5?
         | 
         | https://www.youtube.com/watch?v=9mXe9nRiPHI
        
         | SideburnsOfDoom wrote:
         | > I wonder how things like ASP .NET will run with Native AOT in
         | the future.
         | 
         | Let me give an example of an ASP.NET app lifecycle: an instance
         | is launched, goes through startup code once. When it reports
         | itself healthy, it is put into the Load balancer and then
         | starts handling requests. Code in these paths is executed
         | anywhere from occasionally to 1000s of times per second. After
         | around 24 hours of this, it is shut down and restarted
         | automatically.
         | 
         | So, compiler micro-optimising startup code to take ms off of
         | it, is not interesting at all - it's only run once a day.
         | Startup can take whole seconds, it makes little difference, the
         | instance is ready when it's done. AOT in general isn't that
         | important, but automatic tiered compilation based on usage data
         | is very nice.
        
           | trevorishere wrote:
           | SharePoint, as an example, can take 1+ minutes to JIT.
        
           | ewjt wrote:
           | Serverless.
           | 
           | Running an ASP.NET app in AWS Lambda is just a few lines of
           | code. However, all of a sudden startup time becomes important
           | for both performance and cost.
           | 
           | These investments by Microsoft and others[1] allow .NET to
           | remain relevant and viable for modern use cases.
           | 
           | [1] https://github.com/awslabs/dotnet-nativeaot-labs
        
             | SideburnsOfDoom wrote:
             | Serverless is great, but if you want to go that route you
             | should be aware that it is in no way a typical hosted
             | ASP.NET app, and while you can "Run an ASP.NET app in AWS
             | Lambda" with little code, there are are better ways to
             | design a Lambda.
        
               | ewjt wrote:
               | Can you elaborate on "is in no way a typical hosted
               | ASP.NET app"?
               | 
               | I get that the machinery under the hood is different (ie.
               | Kestrel web server may not get used). However, we
               | typically don't care about those details. Our ASP.NET
               | code runs in 3 separate places (containers, servers,
               | Lambda) and the only difference between all 3 is a single
               | entry point file.
               | 
               | Do you mean because Lambda is only serving one request at
               | a time and has a more ephemeral host process lifetime?
        
               | SideburnsOfDoom wrote:
               | > Lambda is only serving one request at a time and has a
               | more ephemeral host process lifetime
               | 
               | Yes, a typical hosted ASP.NET app is neither ephemeral or
               | one request at a time. So this drives different design
               | decisions.
        
         | pjmlp wrote:
         | Finally, I always though NGEN was a bad compromise and full AOT
         | should have been supported since .NET 1.0, specially given
         | Delphi's influence.
        
         | ledgerdev wrote:
         | It definitely will eventually, probably .net8 timeframe. It
         | will be hard to support so many dynamic features and no doubt
         | will make extensive use of Source Generators.
         | https://docs.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/so...
         | 
         | Anyone have any interest in a small c# aot micro web framework
         | that cold starts up fast (~500ms, vs ~4s for non aot mvc) on
         | serverless containers like google cloud run?
        
         | geodel wrote:
         | Good.
         | 
         | Java is doing something similar with Graal Native Image thing.
         | In a way it is funny to see few years back so many people were
         | claiming that heavy CPU/memory usage of JIT based platforms
         | would be even more non-issue in Cloud because one can scale as
         | much they need on demand.
         | 
         | And now I see AOT/Slimmed Runtime/Compact packaging are
         | happening largely because of cloud deployments.
         | 
         | Seems cloud bills are making impact in places where enterprise
         | cloud architects were running amok with _everything micro
         | service_ deployments.
        
           | kevingadd wrote:
           | I do extensive profiling of managed apps and while the JIT
           | does eat a measurable amount of CPU time, it's really not
           | much. And at least on .NET, you can manually ask the runtime
           | to JIT methods, so you can go 'okay, it's startup time, I'm
           | going to spin up a pool of threads and use them to JIT all my
           | code' without blocking execution - for a game I'm working on
           | it takes around 2 seconds to warm ~4000 methods while the
           | other threads are loading textures and compiling shaders. At
           | the end of that the CPU usage isn't a problem and you're
           | mostly dealing with the memory used by your jitcode. I don't
           | know how much of a problem jitcode memory usage actually is
           | in production these days, but I can't imagine it would be
           | that bad unless you're spinning up 64 entirely separate
           | processes to handle work for some reason (in which case I
           | would ask: Why? That's not how managed runtimes are meant to
           | be used.)
           | 
           | I mostly do JIT warming to avoid having 1-5ms pauses on first
           | use of a complex method (which is something that could be
           | addressed via an interpreter + background JIT if warming
           | isn't feasible.)
           | 
           | The slimmed runtime and compact packaging stuff is definitely
           | attractive in terms of faster deploys and lower storage
           | requirements, however, because the cost of needing to deploy
           | 1gb+ worth of stuff to your machines multiple times a day is
           | still a pain. I don't actually know if AOT is an improvement
           | there though, since in some cases the IL that feeds the JIT
           | can be smaller than the AOT output (especially if the AOT
           | compiler is having to pre-generate lots of generic instances
           | that may never get used.) You also have to ship debug
           | information with AOT that the JIT could generate on demand
           | instead.
        
             | zeroc8 wrote:
             | I 've never understood the point of JITing. Just compile
             | the thing once for your target architecture and you're
             | done. No more spawning threads for doing the same thing
             | over and over again. I'm glad that languages like Go and
             | Rust are bringing back the lost simplicity of yesteryear's
             | dinosaur languages. Life can be so easy.
        
               | carlhjerpe wrote:
               | Reflection in C# is a thing, it isn't in either Go or
               | Rust. I've read some kind of compiler shenanigans can get
               | something that resembles reflection in C++.
               | 
               | I love reflection, the fact that libraries can look at
               | your types is really really cool.
               | 
               | Not saying it can't be done with a compiled language but
               | I don't see it anywhere.
               | 
               | Being able to load a shared library, search it for
               | classes implementing interfaces, instantiate them and
               | call their methods is pretty slick too.
        
               | pjmlp wrote:
               | Go has reflection, and you can make use of compile time
               | reflection on Rust via the macros infrastructure.
        
               | carlhjerpe wrote:
               | Does that cover all mentioned usecases though? They're
               | discussing source generation in this thread, not even
               | close to the same thing (for certain usecases)
        
               | pjmlp wrote:
               | Kind of, you can use the parsing package for that,
               | alongside the //go:generate infrastructure.
               | 
               | Or you can use Go reflection package and generate the
               | source code from it.
               | 
               | Of if wanting to do something like Assembly.Emit,
               | generate the machine code directly with a little help
               | from unsafe and syscall packages.
               | 
               | Depends pretty much on the actual use case.
        
               | pjmlp wrote:
               | IBM OS/400 binaries (nowadays IBM i), can be executed in
               | a completly different architecture from 1988, without any
               | changes, possibly the source code doesn't exist anymore,
               | while taking full advantage of IBM Power10 on its last
               | iteration.
               | 
               | Same applies to any other bytecode format making use of
               | dynamic compilers.
        
           | heurisko wrote:
           | > And now I see AOT/Slimmed Runtime/Compact packaging are
           | happening largely because of cloud deployments.
           | 
           | I see the main driver of AOT to be startup time for things
           | like lambdas.
           | 
           | The JVM is pretty lightweight anyway on modern hardware,
           | especially if you use modules.
        
           | pjmlp wrote:
           | Actually I think it is more competition pressure of languages
           | like Go and Rust than anything else.
           | 
           | I have been using Java and .NET languages for distributed
           | computing for the last two decades, and JIT has always been
           | good enough.
           | 
           | By the way, Google rolled back their AOT compiler introduced
           | on Android 5, and since Android 7 it uses a mix of highly
           | optimized interpreter written in Assembly, a JIT compiler
           | with PGO feedback, and when the device is idle, those PGO
           | profiles are used to only AOT compiler the application flows
           | that matter. On more recent Android versions, those PGO
           | profiles are shared across devices via the Playstore.
           | 
           | On the .NET front, I think the team has finally decided to
           | make front of the whole C++ rulez of WinDev, specially after
           | Singularity and Midori projects having failed to change their
           | mind.
        
             | [deleted]
        
             | geodel wrote:
             | Nowadays Go is working on a PGO solution, so there is that.
             | 
             | I don't disagree but kinda doubt that new upstart languages
             | with single digit market and mindshare in enterprise space
             | would force .net /JVM behemoth to do anything. Forget Go,
             | places I work would not know a single new thing beyond Java
             | 1.7 or _latest Java 1.8_. But they have stood up a dozen
             | _cloud teams_ doing every buzzword you can hear about
             | cloud.
             | 
             | So unless finance guys ask tough questions about rising
             | Amazon billing IT wouldn't care if their SpringBoot crapola
             | take 2GB of RAM or 32GB.
        
               | pjmlp wrote:
               | The new upstart languages are where the younger
               | generations are, that is why you see .NET doing all those
               | changes to make rolling an Hello World website as easy as
               | doing it in Go, with global usings, single file code,
               | simplified namespaces and naturally AOT.
        
         | davidfowl wrote:
         | We've been experimenting with NativeAOT for years with ASP.NET
         | Core (which does runtime code generation all over the place).
         | The most promising prototypes thus far are:
         | 
         | - https://github.com/davidfowl/FasterActions -
         | https://github.com/davidfowl/uController
         | 
         | They are source generated version of what ASP.NET does today
         | (in both MVC and minimal APIs). There are some ergonomic
         | challenges with source generators that we'll likely be working
         | through over the coming years so don't expect magic. Also its
         | highly unlikely that ASP.NET Core will not depend on any form
         | of reflection. Luckily, "statically described" reflection
         | generally works fine with NativeAOT.
         | 
         | Things like configuration binding, DI, Logging, MVC, JSON
         | serialization all rely on some form of reflection today and it
         | will be non-trivial to remove all of it but we can get pretty
         | far with NativeAOT if we accept some of the constraints.
         | 
         | As of right now, we're trying to make sure "motivated people"
         | can play with it, but it's not something that is supported by
         | ASP.NET Core or EF at the moment.
         | https://github.com/dotnet/aspnetcore/pulls?q=is%3Apr+nativea...
         | 
         | PS: Some of the challenges
         | https://github.com/dotnet/aspnetcore/issues/42221
        
           | jeremycarter wrote:
           | Thanks for your hard work with the .NET community David.
        
           | merb wrote:
           | Actually system text json does work with source generators
           | and di can work with source Generators aswell.
        
             | davidfowl wrote:
             | Thanks for telling me :). On a serious note though,
             | anything can work with source generators but it doesn't
             | match the style of coding that we'd like (moving everything
             | to be declarative isn't the path we want to go down for
             | certain APIs). Also source generators don't compose, so any
             | source generation that we would want to use would need to
             | take advantage of the JSON source generator (if we wanted
             | to keep things NativeAOT safe). Right now most of the APIs
             | you use in ASP.NET Core are imperative and source
             | generators cannot change the callsite so you need to resort
             | to method declarations and attributes everywhere.
             | 
             | That's not an optimization, that's a programming model
             | change.
        
         | fabian2k wrote:
         | I think typical ASP.NET applications likely won't use AOT in
         | the near future. There is quite some "magic" in ASP.NET that I
         | suspect won't work out of the box with AOT because it is
         | heavily reflection-based. And while there are solutions for
         | cases like JSON serialization, as far as I understand they
         | require writing different code right now, so it's not
         | automatic.
         | 
         | AOT is much, much more interesting for cases where startup time
         | matters, and ASP.NET usually isn't such a case. But I can
         | imagine that it will be much more useful and easier to
         | implement for smaller, focused tools that don't use that much
         | reflection-based framework code. CLI tools might be the ideal
         | case for trying this out.
        
           | ewjt wrote:
           | Serverless hosting (ie. AWS Lambda) makes ASP.NET a primary
           | use case for AOT because cold startup times cripple
           | performance and increase cost.
           | 
           | I suspect they're just not ready yet and targeting the easy
           | use cases (console apps).
        
           | romanhn wrote:
           | As someone writing a .NET-based API hosted on AWS Lambda, I
           | assure you, startup time matters a ton. I spent a bunch of
           | time getting cold start time down to a semi-reasonable
           | number, but there's still a ton of room for improvement. I
           | for one am very much looking forward to progress on AOT
           | Native.
        
             | fabian2k wrote:
             | The "typical" in my comment was meant to take care of that
             | part, I probably should have mentioned this explicitly. AWS
             | Lambda is of course a case where startup time matters a
             | lot, but it still leaves the common problem of ASP.NET that
             | it was designed around a lot of reflection. My
             | understanding is that this simply won't work with AOT out
             | of the box unless you adapt all the places where you use
             | reflection-based code. For example you'd need to use the
             | source generator versions of JSON serialization and the EF
             | Core DbContext in this case, with all limitations those
             | have. But I'm not sure my understanding here is entirely
             | correct or complete.
        
               | paavohtl wrote:
               | I don't know about ASP.NET, but the post says that
               | Reflection.Emit is not available. Reflection.Emit is a
               | namespace used for runtime code generation; I would
               | assume most other parts of reflection are available, like
               | with previous iterations of AOT support in .NET.
        
               | kevingadd wrote:
               | It is theoretically possible to have Reflection.Emit and
               | DynamicMethod for less critical cases by using an
               | interpreter - this happens in certain scenarios already.
        
               | tmitchel2 wrote:
               | I think you are correct. The DI container and JSON
               | serialisation are two bigger areas that i suspect would
               | need attention but as you say source generation solves
               | the later and I think there is non reflection DI on the
               | way if not already. I guess you would need to wait for
               | any of the third party assemblies you depend on to allow
               | for no reflection also...still a bit of a wait i think
               | for my use case.
        
             | ledgerdev wrote:
             | The good news is that .net7 aot supports console apps so
             | lamdas can already be native aot. Json serialization was a
             | concern of mine, but it was surprising easy to get working
             | with very minor changes in the call. If I were building
             | lambdas would move it asap.
        
             | tmitchel2 wrote:
             | Lol snap
        
               | fnord123 wrote:
               | No, snap startups have an even worse startup time.
        
             | SideburnsOfDoom wrote:
             | As someone writing a .NET-based API hosted on AWS Lambda,
             | your case is quite different to a "typical ASP.NET hosted
             | application", that can run for days between restarts.
        
               | ledgerdev wrote:
               | I would like to host .net based api on google cloud run
               | and azure container apps, which often will go to sleep
               | after about 10 minutes without a request, so cold start
               | time do matter quite a bit. I've done pretty extensive
               | testing and see ~4-7 second cold starts with classic non
               | aot mvc versus ~500ms with natively compiled golang/.net.
               | 
               | The reality of the cloud is that sleep & cold starts are
               | very common and in fact necessary to run efficient
               | systems. Half a second is acceptable but 4+ seconds is
               | not and so aot is very important for future apps.
        
             | phillipcarter wrote:
             | FWIW the problems with cold starts could also be solved at
             | the platform layer. App startup is what typically dominates
             | cold start times and there's creative ways to potentially
             | frontload that, serialize the state, and treat all
             | invocations as "warm".
        
           | tmitchel2 wrote:
           | Using ASP.NET with AWS Lambda or any other FAAS is a concrete
           | scenerio. Startup time for that is critical.
        
       | jackmott42 wrote:
       | For people curious how much impact the .NET performance
       | improvements over the last 5 years have on real, large scale web
       | applications: At work we have a core piece of our online ordering
       | system that has been running on .NET Framework 4.8. We run around
       | 8 web servers and handle around 4,000 orders per minute, 300
       | requests per second per server.
       | 
       | We have been working on porting everything over to .NET 6 by
       | making all the code compatible so we can build both from the same
       | code base, and we just started putting the .NET 6 version live on
       | one of the webservers for a few hours at a time to test it out.
       | 
       | CPU usage on that .NET 6 machine is 1/2 of the Framework
       | machines! Impressive improvement, and .NET 7 should be even
       | better.
        
         | pwdisswordfish0 wrote:
         | "Framework" is such a poor differentiator to contrast with
         | modern .NET (owing to the original poor naming decision in the
         | first place--it's not like ".NET" itself is amazing) that it's
         | hard, from a casual reading, to pick up on the fact that
         | "Framework" is even being used a differentiator.
         | 
         | I propose that when people want to differentiate between the
         | modern .NET Core vs the legacy closed source .NET
         | implementation, the latter is referred to as "OG" .NET (in
         | casual and business-casual contexts, at least).
        
           | wombarly wrote:
           | Please, no more name changes.
        
             | pwdisswordfish0 wrote:
             | Not a name change, just an editorial prescription for
             | discussions. The proper name is .NET Framework (just don't
             | call it that!).
        
           | branko_d wrote:
           | IMHO, they should have just named the new one .CORE and
           | called it a day.
           | 
           | It would have made it immeasurably easier to google, and
           | could have been be incorporated into other names to
           | differentiate them from old versions: ASP.CORE, ADO.CORE,
           | WinForms.CORE...
        
             | copperx wrote:
             | No, it's in their DNA to name things like that. "Windows"
             | ... nuff said.
        
           | jcpst wrote:
           | Yes, it is terrible. I think you need to be an active .NET
           | developer to communicate those subtle differences clearly.
           | 
           | But we're used to it being bad at this point. .NET Framework,
           | Microsoft Teams, Visual Studio Code...
        
           | whoisthemachine wrote:
           | A suggestion I liked was to call ".Net Framework" ".Net
           | classic" a la "ASP classic", nicely honors the original .Net
           | while properly suggesting it is not the "new" .Net.
        
           | phillipcarter wrote:
           | I've taken to calling it "Legacy .NET".
        
           | na85 wrote:
           | Microsoft is so incredibly bad at naming things it'd be
           | hilarious if it wasn't so frustrating.
        
             | whoisthemachine wrote:
             | It feels like it _must_ be a company policy. When needing
             | to compete with other major tools, choose a name so generic
             | and so easily confused with other things that people who
             | have no knowledge of the product will assume they need it.
             | I can imagine some IT conversations 20 years ago along the
             | lines of  ".Net? I must need that for internet access!",
             | just like I'm sure there are discussions today where
             | managers assume they need Azure DevOps to get this new-
             | fangled DevOps thing.
        
       | XzAeRosho wrote:
       | I don't follow the .NET ecosystem closely enough, but it feels
       | that releasing a mayor version every year feels like kind of
       | unnecessary? Or if they intend to keep doing this, they should
       | release LTS versions of some kind.
       | 
       | It feels kind of scary committing to .NET 7 right now, when in a
       | year, it will be replaced by yet another major version promising
       | ever increasing performance gains... Or is the difference between
       | major versions not that big?
        
         | bob1029 wrote:
         | They do have a tick-tock-style LTS approach. Our strategy is to
         | stick with the LTS versions, since we sell software to banks.
         | 
         | The path we are on:
         | 
         | Framework 4.x => .NET Core 2.x => .NET Core 3.1 => .NET 6 (we
         | are here today) => .NET 8
         | 
         | The migration from .NET Core 3.1 to .NET 6 was a total non-
         | event. It was substantially harder for us to go from 2.x to
         | 3.1.
         | 
         | Based upon the current proposals and available documentation,
         | we anticipate our migration from .NET6 => .NET8 will occur with
         | absolutely zero ceremony sometime around Q2 2024.
        
         | yodon wrote:
         | Even numbered versions are LTS
        
         | SideburnsOfDoom wrote:
         | > they should release LTS versions of some kind.
         | 
         | Uh? .NET 6 is the current LTS version. 8 will be the next.
         | 
         | https://dotnet.microsoft.com/en-us/platform/support/policy
         | 
         | I don't comment on Java or Python platform versioning policies
         | because, like you with .NET, "I don't follow the ecosystem
         | closely enough" so I don't have anything meaningful to say
         | about it.
         | 
         | > It feels kind of scary committing to .NET 7 right now .. Or
         | is the difference between major versions not that big?
         | 
         | Rolling forwards, the changes are very minor indeed. And there
         | are utilities to automate parts of the update. e.g.
         | https://dotnet.microsoft.com/en-us/platform/upgrade-assistan...
         | 
         | There are of course new things that you will find missing if
         | you're rolling backwards.
        
           | TillE wrote:
           | One of the weirder HN tropes is being upset that new software
           | continues to be released, or that languages get updated with
           | new features.
           | 
           | I've so rarely seen truly major breaking changes that require
           | substantial effort in updating your own code. Like, the
           | Python 2->3 transition is notable because it's unusual.
        
             | SideburnsOfDoom wrote:
             | What I'm finding the weird trope is the pattern of: "I
             | don't know anything about X, anyway why doesn't X do (thing
             | that X has been doing for ages)"
             | 
             | ignorance is no problem, I am ignorant of the details of
             | most popular programming languages, and I know that. it's
             | another thing to show awareness of ignorance, and then lose
             | that awareness before the end of the paragraph.
             | 
             | Whatever happened to "Whereof one cannot speak, thereof one
             | must be silent."
        
         | atraac wrote:
         | > It feels kind of scary committing to .NET 7 right now, when
         | in a year, it will be replaced by yet another major version
         | promising ever increasing performance gains... Or is the
         | difference between major versions not that big?
         | 
         | Switching between major versions these days is incredibly easy
         | so it's not like you pick a version for life. They're also not
         | changing THAT much.
        
         | ochronus wrote:
         | They have an excellent track record of not breaking
         | compatibility, most updates are drop-in & forget (unless you
         | want to utilize new lang/runtime features)
        
           | smcl wrote:
           | I recall a few bumpy bits in the various upgrades from 1.0 to
           | 2.2 - they reworked quite a few areas such that updating the
           | .NET Core version required changing your code. They provided
           | good documentation and migration guides, but they did
           | definitely break things along the way.
        
             | ThunderSizzle wrote:
             | I recall most developers didn't jump from 472 to core 1
             | since this was thr expectation.
             | 
             | I barely started touching core until core 3, and really
             | started checking out .net 5.
        
               | smcl wrote:
               | To be honest we didn't migrate .NET Framework apps - new
               | build was done in .NET Core and thankfully there were a
               | few smaller projects in the pipeline to allow us to
               | fiddle with a bit through the transition 'til it was a
               | bit more stable.
        
         | quaffapint wrote:
         | .NET Core 3.1 and .NET 6 are LTS (though 3.1 ends this year).
         | Also in general things don't break that much. There are cases
         | like nullable checks whose default changes and so if you go
         | with that default you will have to update, but it's tweaks more
         | than overhauls.
        
         | kryptiskt wrote:
         | Every other release is an LTS release, with three years of
         | support. .NET 6 was one so this won't be.
         | 
         | They are pretty good about backwards compatibility, upgrading
         | .NET Core and .NET hasn't been a big deal so far.
        
           | mikece wrote:
           | I have an issue with calling three years "long term" support.
           | Most companies won't update to the new LTS for 9 to 12 months
           | in order for any issues to be ironed out, so that means you
           | get only 24 to 27 months on the LTS. Between CVEs and
           | upgrading to dotnet6 before the end of life on dotnet3.1 most
           | of my coworkers and I have gotten almost no new coding done
           | on our applications this summer (and this was after a three
           | month blitz to get all of our code up to dotnet3.1 ahead of
           | the dotnet2.1 end of life less than a year ago). As long as
           | the LTS doesn't introduce a lot of breaking changes then it's
           | not that big of a deal but when you have to refactor and
           | rewrite non-trivial chunks of code this gets to be really
           | wasteful.
        
             | guhidalg wrote:
             | What did you have to rewrite when upgrading from .NET 3.1
             | to 6? If all you had to touch was ASP.NET startup changes,
             | that's hardly 1 day's worth of work.
        
         | danparsonson wrote:
         | I have to agree that it's excessive and I don't understand why
         | they're doing it. Major versions imply breaking changes, which
         | contradicts the assertions of other people here replying that
         | the differences between each version are minor; in that case
         | why not minor version bumps? What's the benefit? The only thing
         | I can think of is that it forces upgrades to new versions of
         | Visual Studio.
         | 
         | One immediate problem it creates is with recruitment - between
         | 2019 and 2021 we went from .NET Core 3.0, through 3.1, .NET 5,
         | and now .NET 6, and suddenly in the space of one job I went
         | from up-to-date to 'legacy CV'.
        
           | ewjt wrote:
           | The annual cadence simplifies our planning-- we can preview
           | features a year ahead and prepare for the LTS release. It
           | also shortens the feedback cycle for Microsoft which has to
           | be useful.
           | 
           | From a recruiting standpoint, it allows me to start
           | conversations. I don't take points off if candidates haven't
           | worked on the latest version, but one of my go-to questions
           | is "What features in .NET X are you looking forward to
           | using?".
           | 
           | In an industry of constant change, it's a negative signal if
           | you don't know _anything_ about the current version of the
           | platforms you use. Many, many candidates don't.
        
           | ahtihn wrote:
           | Why would you list the .NET version on your CV? Why would
           | anyone hiring care about whether you have experience with
           | .NET 6 vs .NET 3
           | 
           | Do you also list all the library versions you've worked with?
           | 
           | This is pretty absurd.
        
             | danparsonson wrote:
             | The .NET framework has changed a great deal between
             | versions 1 and 6, so I don't agree that it's sufficient
             | just to say that I am experienced with .NET and leave it at
             | that, nor is it equivalent to listing library versions.
             | Besides I'm a contractor; potential customers are usually
             | looking for someone who can hit the ground running, not
             | spend a month skilling up.
        
               | nightski wrote:
               | If a recruiter denies you because you have .NET 6 on your
               | resume instead of .NET 7 then that probably isn't a very
               | lucrative job in the first place. You don't need a month
               | of skill up to go from 6 to 7, maybe like 10 minutes.
               | 
               | Personally I'd just say .NET in general and leave the
               | specifics to the interview.
        
             | ewjt wrote:
             | It's a minor signal, but it's still a signal.
             | 
             | If someone only lists "Angular", it could be Angular 4 from
             | 2016 or something more relevant.
             | 
             | Same thing with .NET-- the version can imply Windows-
             | related experience (.NET Framework) vs. something more
             | cloud native (.NET 6).
        
           | Salgat wrote:
           | Major versions do include breaking changes in some libraries.
           | You usually keep all Microsoft libraries aligned on the same
           | major version to ensure compatibility, and it allows
           | Microsoft to introduce these breaking changes on a regular
           | basis without causing any confusion. If you're on .NET 6,
           | most of your Microsoft nugets are on 6.xx.xx.
        
             | ThunderSizzle wrote:
             | It gets really weird when you have one dependency including
             | .NET 6 version, and another including .NET 5, since they
             | sometimes rework nuget packages with how many
             | Microsoft.Extensions they have now.
        
         | cyral wrote:
         | The updates from .NET Core 2 to .NET Core 6 have taken me maybe
         | an hour of combined work across many years. Each major version
         | has barely any breaking changes if any.
        
           | Wohlf wrote:
           | Yeah it's pretty much just new features, and you can stick to
           | the LTS releases.
        
         | fabian2k wrote:
         | The most recent upgrades were pretty easy, it looks like the
         | more disruptive changes are mostly done now.
        
         | phillipcarter wrote:
         | > Or if they intend to keep doing this, they should release LTS
         | versions of some kind.
         | 
         | That's exactly what they do: https://dotnet.microsoft.com/en-
         | us/platform/support/policy/d...
        
       | esprehn wrote:
       | Unfortunately looks like still no compact strings like Java or
       | JS: https://github.com/dotnet/runtime/issues/6612
       | 
       | You can work around it, but it's nice in languages where common
       | text doesn't take double the memory (because of utf8 or compact
       | strings).
        
         | svick wrote:
         | UTF-8 string literals will be part of C# 11/.Net 7, which could
         | help. But they're still more awkward to use than the UTF-16
         | string.
        
         | gavinray wrote:
         | Is this what you're talking about?                 > "Arguably
         | the biggest improvement around UTF8 in .NET 7 is the new C# 11
         | support for UTF8 literals."           > "UTF8 literals enables
         | the compiler to perform the UTF8 encoding into bytes at
         | compile-time. Rather than writing a normal string, e.g.
         | "hello", a developer simply appends the new u8 suffix onto the
         | string literal, e.g. "hello"u8. At that point, this is no
         | longer a string. Rather, the natural type of this expression is
         | a ReadOnlySpan<byte>. If you write:"            > public static
         | ReadOnlySpan<byte> Text => "hello"u8;            > public
         | static ReadOnlySpan<byte> Text =>         new
         | ReadOnlySpan<byte>(new byte[] { (byte)'h', (byte)'e',
         | (byte)'l', (byte)'l', (byte)'o', (byte)'\0' }, 0, 5);
        
           | esprehn wrote:
           | No, not unless you can pass a ReadOnlySpan into every API
           | that expects a String. The change you referenced let's folks
           | work around the fact that String is UTF16. It doesn't
           | transparently handle ASCII with one byte like other
           | languages.
           | 
           | See the GitHub issue or https://openjdk.org/jeps/254
           | 
           | The .NET 7 change doesn't retain backwards compatibility
        
       | [deleted]
        
       | spaetzleesser wrote:
       | On the one hand it's cool that they are improving but how do
       | people keep up with all these additions? I find this really hard.
       | 
       | Seems a lot of the changes are new stuff which you have to
       | evaluate and see how they could actually be used productively.
       | For example a while ago I tried the new nullable stuff and while
       | in theory it looks straightforward it turned out to be very
       | difficult to use nullable with existing APIs and code in a
       | productive way. The same applies to a lot of the other new
       | features.
       | 
       | I feel .NET Core after a good start is falling into the typical
       | Microsoft trap of constantly cranking out new stuff to do the
       | same thing and leaving it to developers to keep up. That's how we
       | ended up with several .NET desktop UI frameworks that are more or
       | less in maintenance mode without a real upgrade path to the
       | currently fashionable framework (which will most likely be
       | abandoned soon too).
        
         | MarkSweep wrote:
         | A lot of these things you will get the benefit from upgrading.
         | Also see the last section of the post about analyzers. You can
         | enable these and Visual Studio will identify and potentially
         | automatic improve performance by using different APIs.
         | 
         | There are a performance ton knobs you can turn both at develop
         | time and deployment time. There are 4 ways to run Regexes in
         | .NET 7! (Interpreted, compiled, the new non-backtracking
         | interpreter, the new compile time source generator). I do agree
         | with the assessment that it's hard to keep up with.
        
         | bestinterest wrote:
         | I very much like the ethos of Golang for this reason. Still not
         | had a reason to use it but like the idea of mastering the
         | fundamentals in a weekend. Even if I lose the flexibility of
         | LINQ or Java streams.
         | 
         | It feels like a scale of language conservativeness Go all the
         | way at the top, Java somewhat in the middle (a little above)
         | and C# at the bottom. It is going the route with lots of
         | features and complexity, which can be a great thing but not for
         | grug devs https://grugbrain.dev like me.
         | 
         | But saying all that Blazor looks really good for WebDev, I just
         | worry it gets abandoned. It feels everything does in the C#
         | space.
         | 
         | C# also made a big mistake imo by going with async/await
         | instead of lightweight threads which will add a ton of
         | complexity in the future for if they decide to go the
         | greenthread route like Goroutines/Project Loom.
        
           | whoisthemachine wrote:
           | > C# also made a big mistake imo by going with async/await
           | instead of lightweight threads which will add a ton of
           | complexity in the future for if they decide to go the
           | greenthread route like Goroutines/Project Loom.
           | 
           | Could you expand on this? Async/await is just syntax magic
           | for Task continuations (in other words, Promises [0]), which
           | have very little to do with the underlying threading model.
           | This statement is equivalent to saying "Completable Futures
           | add a ton of complexity to Project Loom."
           | 
           | [0] https://en.wikipedia.org/wiki/Futures_and_promises#List_o
           | f_i...
        
             | bestinterest wrote:
             | I think this post has the best information on it as I am
             | also learning here
             | https://journal.stuffwithstuff.com/2015/02/01/what-color-
             | is-...
             | 
             | It is not just `magic` syntax imo, it is viral to your
             | codebase. The blog post does way more justice than I can
             | explain.
        
               | whoisthemachine wrote:
               | Yes I understand the function coloring "problem" (oh no
               | functions need to specify in their signature whether they
               | return results immediately or eventually). Regardless, I
               | still don't understand how this prevents green threads a
               | la Project Loom, if you have a function that returns a
               | `CompletableFuture` in Java, it also needs to change its
               | signature.
        
               | metaltyphoon wrote:
               | For what it's worth, there is experimentation of green
               | thread in C# happening.
        
             | blackoil wrote:
             | IIRC, the statement was from some Java blog about Loom.
             | Idea is that with lightweight threads you can make
             | everything sync and still be performant. While C# has gone
             | ahead with making everything async
        
               | whoisthemachine wrote:
               | I understand the difference in approaches. However, the
               | parent stated that this decision makes green threads
               | harder in C#, which is what I don't understand.
        
           | keltex wrote:
           | > But saying all that Blazor looks really good for WebDev, I
           | just worry it gets abandoned. It feels everything does in the
           | C# space.
           | 
           | I've been maintaining a asp.net (WebForms!) framework
           | codebase for almost 18 years now. Running on the latest
           | Windows Server and Visual Studio 2022.
        
         | rr888 wrote:
         | I think this is pretty common with a lot of legacy code bases
         | in popular languages. Lots of big systems on Java 8, Python 2,
         | we wont talk about JS. :)
        
         | francisofascii wrote:
         | Fortunately the upgrade path has not been too bad once you get
         | to .NET Core. Usually one time changes to your Program.cs and
         | Startup.cs files. The Top-Level statements addition is pretty
         | jarring at first, but a cool addition. I did run into a
         | breaking change from .NET 5 to 6 with an Encryption library not
         | working right. But most everything else just works when
         | upgrading. If it was just .NET additions life would be fine. It
         | is the front-end churn that causes real stress. AngularJS,
         | TypeScript, Angular, React, Blazor, WebPack, Vite, etc.
        
         | hardware2win wrote:
         | >On the one hand it's cool that they are improving but how do
         | people keep up with all these additions? I find this really
         | hard.
         | 
         | Very often they improve things in a way that you'll get an
         | improvement regardless of whether you are aware
         | 
         | E.g linq, collections, regexes, etc.
        
         | bob1029 wrote:
         | I don't try to keep up with absolutely everything. I've been to
         | this particular buffet for a really long time and have
         | developed tastes for certain things.
         | 
         | For instance, I recognize AOT is elegant in theory, but I also
         | notice it's a difficult thing to get right and has certain
         | tradeoffs. Especially, for a complex application that may need
         | to use legacy APIs. As a consequence, I have mostly avoided
         | this in favor of focusing on other areas that do seem to
         | deliver additional value for our product (and my side-
         | projects). Examples of these being intrinsics (SIMD), UTF8
         | text/serializer, file I/O and more advanced GC techniques.
         | 
         | Ultimately, it's a matter of experience to know when you should
         | and should not chase a particular rabbit. My general policy
         | right now is to observe the new shiny while it's in the odd-
         | version, and then wait to see how it shakes down by the time
         | the LTS comes around. You almost never want to try to grab a
         | Microsoft-branded rabbit upon the very first sighting.
        
         | vsareto wrote:
         | Bookmark https://devblogs.microsoft.com/dotnet/ and read
         | occasionally. https://docs.microsoft.com/en-
         | us/dotnet/csharp/whats-new/csh... is good for a quick overview
         | of language features depending on what version you're at.
         | 
         | Most .NET devs only need to be peripherally aware of these
         | changes, as most businesses using .NET will move slower than
         | Core's new pacing. It's even less important for lower seniority
         | devs as you typically need project changes to utilize new
         | features, which is a call a senior would make, which involves
         | approval/testing/deployment, so a slow process which gives you
         | time to read up on the new features as they're needed.
         | 
         | You're better at your "craft" the more tools you know about and
         | how to apply them, but if your day job prevents you from
         | following the new stuff, I wouldn't worry too much about it.
         | 
         | >I feel .NET Core after a good start is falling into the
         | typical Microsoft trap of constantly cranking out new stuff to
         | do the same thing and leaving it to developers to keep up.
         | 
         | Yeah, the pacing has increasing dramatically from .NET
         | Framework days, but that's probably a good thing. I would just
         | stick to learning about what you do in your day job. .NET has a
         | huge ecosystem compared to other languages, so it's going to be
         | very hard to keep up with everything MAUI is doing if you're
         | doing regular ASP.NET core APIs.
        
         | pjmlp wrote:
         | We don't really keep up.
         | 
         | I read with interest all these articles, and then go back
         | coding our LOB stuff in .NET Framework 4.7.2 and similar.
         | 
         | On the Java side, I am finally confident that all our servers
         | have at least Java 11 on them.
         | 
         | Only on private stuff do I happen to enjoy latest versions.
        
         | kevingadd wrote:
         | You really don't need to use any new features to get the
         | benefit of improvements in many cases, because the standard
         | library is updated to use them. So things like string and array
         | operations just get faster.
         | 
         | I've started using 'ref readonly' and stuff like that in bits
         | and pieces of my application code, but the rest of it is
         | basically written in a .NET 4.0 style and it's fine, there's no
         | obligation to use the new stuff unless you want to. I've
         | started using some of the newer syntax sugar to make things
         | more concise, like local functions, value tuples, and short
         | property definitions - but again that's totally optional.
        
         | jabart wrote:
         | Nullable's have been around for at least a decade and before
         | .Net core existed? Are you thinking of the NotNull attribute
         | argument checking instead(Likely the wrong term here)? Any
         | change in a data type to an existing API or code will always be
         | tough and a lot of work. Maybe an in32 to a in64 being the
         | exception.
         | 
         | Library developers are looking at these and following them to
         | make enhancements. Developers working on business apps get the
         | automatic performance improvements of these changes and the
         | library dev changes to improve performance and allocations. I
         | personally quickly go over these so when I get into a
         | performance issue I have a little bit to know where to go find
         | more information.
        
           | nightski wrote:
           | Pretty sure they mean Nullable reference types which are much
           | newer (although they have been around for a while at this
           | point).
        
         | ewjt wrote:
         | Can you name other development platforms of similar scale/depth
         | that don't have this issue?
         | 
         | Compared to the whirring treadmill of frontend web development,
         | I find .NET to be pretty easy to keep up with. Good IDEs
         | (ReSharper or Rider) really help with the new language features
         | (which are opt-in).
         | 
         | Outside of that, the ecosystem is large enough that there are
         | quality blogs/articles/podcasts to stay current without sinking
         | a ton of time into doing so.
        
           | asdajksah2123 wrote:
           | Front end development may have a lot of frameworks, etc., but
           | so much less magic than .Net.
           | 
           | What makes .Net so much harder (especially ASP.Net) are the
           | new features but also all the magic that makes
           | discoverability so hard.
           | 
           | Front end frameworks tend to follow similar patterns, and
           | where they differ, they usually advertise the difference
           | between the standards heavily.
           | 
           | But also, since they are genuinely different frameworks, it's
           | easier to search the differences etc. So, if I open a Vue
           | application codebase, I know almost immediately that it's a
           | Vue application and not a React one. I can google vue and
           | figure out (most likely from their getting started page)
           | exactly all I need to know to get started.
           | 
           | If I enter an ASP.Net codebase, on the other hand, I have no
           | idea whether I'm looking at ASP.Net Pages, .Net WebAPI, .Net
           | WebServices, or more often than not, some combination of all
           | of them. Throw in some Dependency Injection, with a
           | combination of IOC containers used in the same project, and
           | it's a massive lift to know what even to Google for.
        
             | metaltyphoon wrote:
             | I hear this word all the time "magic". Can you describe
             | what it means with concrete example for other stacks?
        
               | ewjt wrote:
               | Magic means "hidden complexity", AKA "abstraction". The
               | negative connotation is that sometimes you can't figure
               | out why your software is behaving a certain way and what
               | you need to do about it.
               | 
               | The positive connotation is that you write less code with
               | less cognitive overhead.
               | 
               | Generally I prefer ASP.NET over things like Spring and
               | Ruby on Rails because it has less magic, despite being
               | clearly inspired by both of those.
               | 
               | Here's a concrete ASP.NET example: You can put an
               | [ApiController] attribute on your controllers and it can
               | change the structure of error responses, among other
               | things. [1]
               | 
               | I don't agree with some of the other parent points. For
               | example, the comments on "multiple dependency injection
               | container" --- ASP.NET is pretty prescriptive on DI
               | patterns. That sounds like someone made a decision to add
               | complexity, which is on them.
               | 
               | [1] https://stackoverflow.com/a/66546105
        
               | metaltyphoon wrote:
               | So "non magic" means to write everything then? Because
               | the example you showed you have to understand a language
               | feature, attributes. Then see the source code, which is
               | open, and how it's being used.
               | 
               | So, are Rust macros, or C macros, C++ templating magic or
               | it's just means "I don't want to know how this works
               | therefore it's magic"?
        
               | cerved wrote:
               | Http request handling in asp.net mvc. The uri, type and
               | parameter serialization is a magic mess.
               | 
               | How do you PUT { "foo" : { "bar" : "baz" }} to
               | ping/pong/yolo?
               | 
               | You create a controller class, PingController, you create
               | a method PutPong, then you give up and proceed to cry
               | blood
        
               | aliswe wrote:
               | something like
               | 
               | app.MapPut("ping/pong/yolo", ctx => ...);
               | 
               | and in there you read the Body as string ... it should be
               | a one liner?
        
               | guhidalg wrote:
               | It seems you had a bad time learning ASP.NET, but in my
               | experience what you're asking for is not difficult at
               | all.
               | 
               | You have to: 1. [Optional] Define a DTO that deserializes
               | { "foo" : { "bar" : "baz" }} to an object 2. Write a
               | class that subclasses Controller 3. Write a method, call
               | it whatever you want, that accepts a JObject or one of
               | your DTO types. 4. Add the attribute
               | [HttpPut("ping/pong/yolo")] to tell the framework this
               | method should be bound to PUTs for that path. 5. Write
               | your business logic.
               | 
               | Is that blood-crying inducing? No.
        
       | fabian2k wrote:
       | https://devblogs.microsoft.com/dotnet/performance_improvemen...
       | 
       | This is a really interesting, or rather terrifying part of that
       | blog post. I thought I had a reasonably good knowledge about
       | typical performance mistakes in .NET, but I never heard about
       | this particular one.
       | 
       | If I understand it right, initializing a fresh
       | JsonSerializerOptions object before each call to
       | Serialize/Deserialize in System.Text.Json is incredibly
       | expensive. It essentially disables the cache and means the
       | serializer analyzes this case fully every time. And this is more
       | on the order of 100x as expensive than using the cached version,
       | so not a trivial amount.
        
         | AtNightWeCode wrote:
         | Yeah, it should be in a static property/field or a singleton.
         | Seen measurable improvements based on fixing this.
         | System.Text.Json really has many unnecessary pitfalls. Maybe
         | they should have fixed the memory problems with Newtonsoft
         | instead. A much more practical lib.
         | 
         | Edit: With that said, in most apps JSON speed is very not
         | important. Never seen it consumed more than couple of percent
         | of execution time for APIs since .NET 1.1.
        
           | jayd16 wrote:
           | Newtonsoft isn't AOT friendly though. That's why they went
           | with both.
        
             | AtNightWeCode wrote:
             | AOT? I really don't understand why they made the transition
             | to System.Text.Json impossible for most projects.
        
         | kevingadd wrote:
         | A polite way to describe most serialization tooling in the .NET
         | ecosystem would be "bad". It's unfortunate, but it's good to
         | see improvements happening. I still try to avoid JSON in
         | general on .NET since I've had so many bad experiences with the
         | various JSON libraries out there.
         | 
         | Sadly if you want to ship well-performing software in .NET it's
         | still mandatory to run it under a profiler on a regular basis,
         | both to spot frequent unnecessary GC allocations and to spot
         | CPU bottlenecks. Either one would probably catch this
         | particular problem, thankfully!
         | 
         | I use the built in VS profilers sometimes, and other times I
         | use Superluminal's excellent CPU profiling.
        
           | aliswe wrote:
           | Streaming serialization with System.Text.Json doe.
        
       | krona wrote:
       | I wish benchmarks with sample means in the tens of nanoseconds
       | weren't reported and compared by the arithmetic mean. These are
       | not normal distributions and a 10% improvement could just mean
       | you improved the 99% percentile, which is quite typical in my
       | experience given how skewed the distributions tend to be.
        
         | uwuemu wrote:
         | ... that awkward moment when video game tech reviewers like
         | DigitalFoundry provide better benchmarks of video game
         | performance than a tiny company like Microsoft does for its
         | biggest inhouse programming ecosystem.
        
           | kevingadd wrote:
           | Video game performance is generally measured in milliseconds,
           | not nanoseconds. The comparison would be if you were
           | benchmarking things like physics sim updates on a per-object
           | basis, or rendering on a per-triangle basis. Those would also
           | be in nanoseconds.
           | 
           | As a game dev you're typically operating in terms of things
           | like 'rendering our UI is taking 0.5ms, can we get it lower
           | so we have more time for the terrain?'
        
         | svick wrote:
         | Many of the benchmarks are using BenchmarkDotNet, so consider
         | reporting this to them:
         | https://github.com/dotnet/BenchmarkDotNet/.
        
           | hermitdev wrote:
           | Looks like the author is explicitly excluding "Error",
           | "StdDev", "Median", "RatioSD" from the ouput. It's in the
           | setup[0]. I'm guessing the author omitted them for brevity.
           | 
           | [0] https://devblogs.microsoft.com/dotnet/performance_improve
           | men...
           | 
           | edit: meant "excluding", not "including"
        
       | devmunchies wrote:
       | > A huge amount of effort in .NET 7 went into making code gen for
       | Arm64 as good or better than its x64 counterpart
       | 
       | Awesome. I was using an LSP server for F# (in Sublime Text) on an
       | M2 Mac and it was always running at 500%+ CPU. I had to turn off
       | the LSP server. Hopefully this version fixes it.
        
         | chusk3 wrote:
         | Assuming you're talking about FsAutoComplete and this was
         | recently, that's nothing to do with the .NET Runtime and
         | entirely a coding mistake that I made that we've released a fix
         | for :)
        
           | noveltyaccount wrote:
           | This is the most Hacker News comment I have ever seen, lol.
           | The actual guy behind the bug here replying to comments with
           | a mea culpa :D
        
           | devmunchies wrote:
           | Oh! HAHA it was FsAutoComplete. right on. Thank you!
        
       ___________________________________________________________________
       (page generated 2022-08-31 23:02 UTC)