HNHacker News
TopNewBestAskShowJobs

tannergooding

14 karma · joined February 14, 2023

submissionscomments
tannergooding··on The full API diff between .NET 7 and .NET 8
For the areas I help maintain, some of the new features include...

Better support for `Int128`, `UInt128`, `IntPtr`, and `UIntPtr` throughout the codebase, particularly in `BinaryPrimitives` for endianness handling.

`IUtf8SpanFormattable` and `IUtf8SpanParsable`, allowing types to be directly parsed/formatted as UTF8

Standard APIs for `Lerp` (Linear Interpolation), `DegreesToRadians`, and `RadiansToDegrees`

Removing the `where T : struct` constraint from the `Vector` types so they can better be used in generic code paths (this doesn't expand the set of supported `T`, it just means you can use the type if the `IsSupported` check returns `true`)

Support for SIMD on Wasm.

Support for AVX-512 on x86/x64

New throw helpers on `ArgumentOutOfRangeException` (`ThrowIfZero`, `ThrowIfNegative`, `ThrowIfEqual`, `ThrowIfGreaterThan`, etc)

New APIs on `Span`/`ReadOnlySpan` for searching/finding data (`ContainsAny`, `ContainsAnyExcept`, `CointainsAnyInRange`, `Count`, `IndexOfAny`, `IndexOfAnyExcept`, `LastIndexOf`, `Split`, etc)

New APIs on `Random` in the form of `GetItems` and `Shuffle`

and so much more in areas maintained by others on the team as well (collections, json, WinForms, ASP.NET, etc)

tannergooding··on What is .NET, and why should you choose it?
Spans are certainly a bit unfortunate, and are something I wish were backed by `nint` instead of `int`. But those came to the language later and it wasn't covered as part of the initial design.

That being said, it is largely in the same boat where if you're working with more than 2GB of memory in a single allocation, you're not necessarily going to have a good time. Even if your phone has 12GB of memory, you in practice get a much smaller portion of that available for actual usage and you don't want to cap out the available RAM either since that will cause thrashing and other issues.

75-80% of available physical RAM is about what you want to cap out at, and that's if you're the only app really using any resources.

MemoryMapped files notably support 64-bit values today and have since they were introduced. As have streams in general.

Streaming tensors can make sense and is necessary with extremely large models. The CPU cannot handle all data at once and the cost of loading everything from disk is often expensive, so you'll want to balance loading in data you need in the short term with data that isn't needed anymore or will only be needed in the long term.

Streaming or otherwise chunking your data can likewise help with parallelization and while there are some domains where chunking isn't possible, streaming always is. It just comes down to data size to determine if its beneficial or not.

tannergooding··on What is .NET, and why should you choose it?
Could you elaborate? Do you specifically mean support for 64-bit arrays or something?

.NET in general has great support for 64-bit platforms and while it wasn't the default target for .NET Framework, it has been the default target for .NET [Core] for years.

We notably don't support single-dimensional arrays that have a 64-bit length or individual objects in general being that large. However, that also isn't necessarily a bad thing as there is a large difference between your overall application supporting more than 32-bits of memory (decently common and generally good) and individual allocations using more than 32-bits of memory (not so common and generally not good to have).

If you're creating individual allocations that are larger than 4GB, you're likely not correctly accounting for devices that only have 4-8GB of memory, are more prone to cause memory thrashing, and may end up negatively impacting other parts of the system since such allocations place special restrictions on how the GC can/should interact with the data or in the case of data that is or contains reference types it can add large numbers of additional references for the GC to track.

For such scenarios, it's often better to manage your memory differently, to support streaming/buffering where possible, and if it is the rare scenario that actually necessitates such large allocations to use things like NativeMemory to bypass the GC alongside Span or the SequenceReader/Writer APIs to work with smaller chunks of data. This also allows the data to be thought about differently since the algorithm you'd use to work with small to medium sized data is not necessarily the same algorithm you'd want to use when working with large to huge data. The time cost required to process multiple GB of memory is significantly different after all, as is the cache locality and other aspects.

tannergooding··on What is .NET, and why should you choose it?
> But at that point you aren't really doing C# so much

I wouldn't really agree on this point.

Unsafe blocks have been a feature since C# 1.0 They may not be the default way of writing C#, but they've always been a natural and integral part of C# and have extensive use internally to ensure that interop can work and extra performance is available where possible.

SIMD acceleration has likewise been a part of the BCL (standard library) for nearly 10 years now and is just as integrated behind the scenes. It being part of the formal BCL makes it even more "standard" than the equivalent in C/C++ where such functionality is relegated to compiler specific headers/extensions.

SIMD code itself is also very idiomatic C#. There is nothing really different about utilizing the APIs, the only difference is in how you think about handling your data. Needing to think differently about how data is handled for some contexts is applicable to many domains in C# (and programming in general). It's no different than making code work with async or multi-threading ;)

Simply put, all these features are still C# and I don't think its necessarily good to say that using them means you're not really writing C# anymore. I view it as a disservice to the language, its extensibility, and power. It also leaves a connotation that you might be better off writing C/C++ instead, which is often not the case.

tannergooding··on What is .NET, and why should you choose it?
For `Vector4.Transform(Vector4, Matrix4x4)` we had `5.22ns` to `2.46ns` (~53% faster)

For `Matrix4x4.Multiply(Matrix4x4, Matrix4x4)` its a bit less at `10.04ns` to `8.38ns` (~17% faster)

The main improvements tracked by: * https://github.com/dotnet/perf-autofiling-issues/issues/1144... * https://github.com/dotnet/perf-autofiling-issues/issues/1148... * https://github.com/dotnet/perf-autofiling-issues/issues/1146...

There are some more cases to be improved. `Matrix4x4.Multiply` in particular I need to update to use a proper SIMD implementation rather than its current scalar implementation.

If you have any other core scenarios where it's important, let me know and I can try to focus on them sooner, rather than later ;)

tannergooding··on What is .NET, and why should you choose it?
I got a rewrite for Matrix3x2, Matrix4x4, Quaternion, and Plane in for .NET 8 as well.

It should provide up to 48x perf improvements as compared to .NET 7