3 ms·
The problem with it is that you don't know. The fundamental language construct "foreach" is one that may or may not allocate and it's hard for you as a develope
by alkonaut 2y ago
The problem with it is that you don't know. The fundamental language construct "foreach" is one that may or may not allocate and it's hard for you as a developer to be sure. Many other low level things do this or at least used to (events/boxing/params arrays, ...).
I wish there was an attribute in C# that was "[MustNotAllocate]" which files the compilation on known allocations such as these. It's otherwise very easy to accidentally introduce some tiny allocation into a hot loop, and it only manifests as a tiny pause after 20 minutes of runtime.
- neonsunset 2y agoMost often you do know whether an API allocates. It is always possible to microbenchmark it with [MemoryDiagnoser] or profile it with VS or Rider. I absolutely love Rider's dynamic program analysis that just runs alongside me running an application with F5, ideally in release, and then I can go through every single allocation site and decide what to do. Even when allocations happen, .NET is much more tolerant to allocation traffic than, for example, Go. You can absolutely live with a few allocations here and there. If all you have are small transient allocations - it means that live object count will be very low, and all such allocations will die in Gen 0. In scenarios like these, it is uncommon to see infrequent sub-500us GC pauses. Last but not least, .NET is continuously being improved - pretty much all standard library methods already allocate only what's necessary (which can mean nothing at all), and with each release everything that has room for optimization gets optimized further. .NET 9 comes with object stack allocation / escape analysis enabled by default, and .NET 10 will improve this further. Even without this, LINQ for example is well-behaved and can be used far more liberally than in the past. It might sound surprising to many here but among all GC-based platforms, .NET gives you the most tools to manage the memory and control allocations. There is a learning curve to this, but you will find yourself fighting them much more rarely in performance-critical code than in alternatives.
- ygra 2y agoWhile this would be nice for certain applications, I'm not sure it's really needed in general. Most people writing C# don't have to know about these things, simply because it doesn't matter in many applications. If you're writing performance-critical C#, you're already on a weird language subset and know you way around these issues. Plus, allocations in hot loops stand out very prominently in a profiler. That being said, .NET includes lots of performance-focused analyzers, directing you to faster and less-allocatey equivalents. There surely also is one on NuGet that could flag foreach over a class-based enumerator (or LINQ usage on a collection that can be foreach-ed allocation-free). If not, it's very easy to write and you get compiler and IDE warnings about the things you care about. At work we use C# a lot and adding custom analyzers ensuring code patterns we prefer or require has been one of the best things we did this year, as everyone on the team requires a bit less institutional knowledge and just gets warnings when they do something wrong, perhaps even with a code fix to automatically fix the issue.
- deleted 2y ago[deleted]
- jayd16 2y agoIf you know what types you're using, you do know. If you don't know what you're calling, that's a pretty high bar that I'm not sure C++ clears.
- alkonaut 2y agoIf you are calling SomeType.SomeMethod(a, b, c) then you don't know what combintions of a, b, c could allocate unless you can peek into it or try every combination of a, b and c. So it's hard to know in the general case even with profiling and testing.