Performance Tuning for .NET Core
reubenbond.github.io
reubenbond.github.io
They don't ship Crossgen with the Linux packages, and you have to manually generate the .NET runtime symbols.
I've gotten things like FlameGraphs working using BCC profile[2], but it took quite a bit of work.
[1]: https://raw.githubusercontent.com/dotnet/corefx-tools/master... [2]: https://github.com/iovisor/bcc/blob/master/tools/profile.py
For generating symbols, (I misspoke a bit, I mean downloading them) I'm talking about the native CLR runtime symbols. According to the docs if you want those, you need to use dotnet-symbol and manually download the symbols for the CLR alongside the CLR .so files.
I think making the tools cross-platform after such a long time is not that easy/fast.
void GetValue (string key, out SomeBigType result) {
if (_cache.TryGetValue(key, out result))
return;
result = new SomeBigType(key, ...);
_cache[key] = result;
}
In most scenarios this function might not get inlined, because the cache miss path makes the function bigger. If you use the aggressive inlining attribute you might be able to convince the JIT to inline it, but once the function gets bigger it doesn't inline anymore.However, if you pull the cache miss out:
void GetValue (string key, out SomeBigType result) {
if (_cache.TryGetValue(key, out result))
return;
GetValue_Slow(key, out result);
}
void GetValue_Slow (string key, out SomeBigType result) {
result = new SomeBigType(key, ...);
_cache[key] = result;
}
You will find that in most cases, GetValue is inlined and only GetValue_Slow produces a function call. This is especially true in release builds and you can observe it in the built-in Visual Studio profiler or by looking at method disassembly.(Keep in mind that many debuggers - including VS's - will disable JIT optimization if you start an application under the debugger or attach to it. You can disable this.)
This tip applies to both desktop .NET Framework and .NET Core, in my testing (netcore is generally better at inlining, though!) If you're writing any performance-sensitive paths in a library I highly recommend doing this. It can make the code easier to read in some cases anyway.
Also, I noticed there haven't been any commits for a while - is this more or less considered 'complete'?
Performance deltas should be similar on .NET Core.
Focused on reducing the allocations which makes linq heavy
Given the immediate next bullet point in the article is about foreach operations over List<T>, that would be my top expectation why they were seeing so much allocation in Linq. ToList() allocates a lot, and to many projects I've seen reify everything with ToList() way too often in Linq usage. I've argued before that List<T> is very rarely the appropriate data structure for a lot of Linq work, and personally consider ToList() harmful. A successful strategy I've used to cleaning up Linq performance in projects is to simply start by remove all ToList() calls entirely and work to move API signatures to use smarter, more appropriate data types than List<T> and IList<T> everywhere.
It sounds like you know this already, but I like to think I just saved someone from introducing a runtime error after following your advice without understanding the consequences.
Also with Linq against IQueryable sources, so often ToList/ToArray is used where AsEnumerable would be better. Understanding those monad boundaries is hard, and AsEnumerable doesn't always sound self-explanatory to people when they need to cross those boundaries. (I've thought before that some simple IDE highlighting of which bits of Linq are against IQueryable and which against IEnumerable might help some people think better in Linq.)
I find it makes most (but not all) code more readable, particularly given that we have great IDEs/tooling in the .NET world.
Just depends on the kind of thing you are working on. For many people you could use dynamic all over, no problem. For many people that would be a disaster. (super high throughput server, things that need super low latency, games, rendering, libraries that could be used in any of those domains)
It is just ‘faster’ for people to dump JSON into dynamic and write against that than it is to properly create structured classes. In the end ofcourse the unittests validate the JSON, the code is slow and harder to use and refactor; you would have saved time and stress actually just writing classes with proper typing.
I'm a big fan of LINQ and I use it in my own code, just not in the high-perf bits. LINQ is great for writing code which has "obviously no deficiencies" in terms of correctness. Without it your code may end up having "no obvious deficiencies".
I also like LinqFaster, LinqAF and these other libraries/tools which can make LINQ usable in more domains.
For lightweight threads I recommend https://github.com/Hopac which is an implementation of SML's John Reppy's Concurrent ML on .NET.
I wrote a parser for a "formalized" URI (it looked somewhat like OData). This parser was being invoked millions of times and was adding minutes to an operation - it dominated the profile at something like 30% CPU time. It started off something like this:
int state = State_Start;
for (var i = 0; i < str.Length; i++)
{
var c = str[i];
switch (state)
{
case State_Start:
/* Handle c for this state. */
/* Update state if a new state is reached. */
}
}
Hardly rocket science, a clear-as-day miniature state machine. VTune was screaming about the switch, so I changed it to this: for (var i = 0; i < str.Length; i++)
{
for (; i < str.Length; i++)
{
var c = str[i];
/* Handle c for this state. */
/* Break if a new state is reached. */
}
for (; i < str.Length; i++)
{
var c = str[i];
/* Handle c for this state. */
/* Break if a new state is reached. */
}
}
The new profile put the function at < 0.1% of CPU time. This is something that the "premature optimization crowd" (who tend to partially quote Knuth concerning optimization) get wrong: death by a thousand cuts. A single branch in the source (it ends up being more in machine code) was costing 30% performance.But this wasn't premature optimisation.
- you found performance was actually a problem in practice
- you used a tool to profile the application for a real workload
- you isolated something to optimise
- you came up with a way to optimise based on the data and your tools
- you tested that the optimisation worked
That isn't premature optimisation. Premature optimisation would have been writing this in the first place without checking anything first.
While this may no include you, a great many forums and advise areas on the internet, and elsewhere, would discourage you from even worrying about a detail like this. Asking questions about this will just get you a knuth quote. I sometimes wonder if the reason so much software today is inexplicably slow is because for 30 years so many young kids curious about how computers and compilers work were told not to worry about it.
I think it is useful to build up some intuition about these things, so your default implementation can be the fast one when it isn't especially messy to do so. People don't always get the opportunity to come back and change things later, and some design choices don't just affect a single function that you can easily fix later in the development processes.
So when you do have such an issue, and you want to make it clear that it's not a premature optimization, you should mention that it's a hot path as determined by profiling (or whatever other technique). Then you won't get the Knuth quote.
The person asking may be prematurely optimizing, but can you make that assertion for every single person arriving at the same question/post from a search engine? If premature optimization is a bad thing, then prematurely calling out premature optimization is doubly-so.
However, as the sibling comment says, I'm most commonly annoyed by this when I arrive at an answer via search and have to wade through the whole interrogation process about what the OP was really trying to accomplish.
It's like, look... you're not being brought on as a consultant, where whole-system analysis and root-causing issues would be part of your professional responsibilities. Sometimes a person just wants to ask an obscure question about software to see if maybe someone else knows the answer. That person shouldn't have to provide a complete backstory and concept of operations for whatever it is they happen to be working on.
They're also not entitled to an answer, so if you don't know, for the love of god, just move along and do something else with your day. I think this kind of behavior wastes a lot of time for everybody involved.
The vast majority of "premature" optimisation happens before the code has been profiled to see which part is slow. In some cases, before the code has _ever been run_. i.e. it is speculative design.
In contrast, you're talking about a function using 30% of the CPU time in a profile, so that's clearly not you.
The longer and less common Knuth quote is below, and it's much more nuanced than the headline. Note that it specifically talks about code optimisation by identifying "critical code" with "measurement tools". That sounds like what you did. Does it not?
> "There is no doubt that the grail of efficiency leads to abuse. Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.
> Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified. It is often a mistake to make a priori judgements about what parts of a program are really critical, since the universal experience of programmers who have been using measurement tools has been that their intuitive guesses fail."
- Structured Programming with go to Statements, Donald Knuth, ACM Computing Surveys, Dec. 1974
Please, no! This shouldn't be the default - it's a constant bugbear of mine where I want to extend a class from a library, and I can't because it's been sealed for no good reason.
http://developer-interview.com/p/oop-ood/what-are-advantages...
Without inheritance (if the class is sealed) I often end up having to re-write the entire component or simply accept my fate. So that is a lose-lose situation.
Create a new class
Create a private variable:
private Foo _foo
Click on _foo
Resharper menu -> Generate Code -> create Delegating members.
Secondly this code generation solution is just re-implementing inheritance again poorly and with the above mentioned limitation. I fail to see how code generating a proxy is any way better than (or significantly different from) inheritance.
> There is principle, which sounds like "favor object composition over class inheritance".
This is begging the question, isn't it? Why favor object composition over inheritance? The qualification here that's missing is "for code re-use". If your goal is simply to re-use code then you should favor composition. But if you're actually modeling an is-a relationship or trying to modify the behavior of a base class then that's not just for code re-use.
This subtly seems to have been lost. It's much easier to religiously ban all inheritance than to simply use it appropriately.
> In most cases "HAS-A" relationship is more semantically correct than "IS-A" relationship between classes.
In most, but not all cases. You will find that in most cases of inheritance in frameworks, for example, follow the is-a relationship perfectly. And you'll find most of time composition is used for has-a relationships. It's an error to mix this up but that's not the fault of inheritance.
For example, in my own software I inherit from the database context class to provide a host of features beyond what the framework provides. My context is a database context.
> Composition is more flexible than inheritance.
If you need that flexibility, great. But I don't see why having more flexibility is automatically good for the correctness of the program. If X is always implemented by Y, that's much easier to reason about.
> Inheritance breaks encapsulation.
I don't see how that's true. Your base class exposes what it wants to potential child classes so encapsulation is preserved. There are some languages that provide no limits (no protected/private/virtual/etc) but then those don't provide any encapsulation in any other situation either.
> A design based on object composition usually will have less classes.
Says who? What's the research behind this? I assume most projects have a mix of inheritance and composition as needed.
> It is possible to implement "multiple inheritance" in languages which do not support it by composing multiple objects into one.
That's not multiple inheritance -- it's just composing multiple objects using some kind of proxy. There is a difference.
> There is no conflict between methods/properties names, which might occur with inheritance.
Ok, that's a fair point.
The problem is, some libs are not well maintained, and not that well designed, and you (the generic "you" ) are happy to deal with breaking changes if and when they come along on version upgrades. It can be frustrating if they essentially locked it all down without much thought. But in general, I think the sealed by default is the better advice. You can do cunning ( evil ) things to get around it.
On the plus side, when I encounter this, I generally ask the author on GitHub if it can be 'unsealed', and they generally do so.
Designing for extensibility is not easy in the context of virtual methods. Every virtual method can be overridden, which means that you effectively need to define contracts for all your methods that can be overridden, and only call them in ways that respect those contracts. In a language where everything is virtual by default, like Java, this means all non-private methods. Which is not good, because most of the time, the reason why you're making a method public is to allow calling it, not to allow overriding. That's why C# made "virtual" explicit opt-in.
But for inheritance, there's no such issue. If someone inherits from your class, they can't break your invariants - they still have to invoke your constructor, and they don't have access to any private state. In C#, they also can't override public and protected members, unless those are declared virtual. Thus, there's no safety or correctness reason to prohibit derivation.
It should also be noted that there's no perf gain from declaring a class "sealed", unless it has virtual methods (normally, inherited from a base class, because it doesn't make any sense to have virtual methods declared in a sealed class). Thus, the only time to do so is when you have a class hierarchy, for leaf nodes in that hierarchy.
You're talking about the programming model, where I'm talking about the implementation.
In Java's case, since methods are virtual by default so with open classes a large majority of the code has a possible indirection, hence such optimizations.
If a class is unsealed you are basically not able to change it ever again, since someone might have subclassed it and depend on all kinds of internal behavior. So it should only be done very deliberately.
Is it? A more typical approach, IMO, is for internal classes to be marked as `internal`, and for `public` classes to be considered part of the public API.
The overarching principle is to keep the API as tight as possible, because as soon as an API is public, you have committed to it and even subtle changes in observable behavior may break some client.
Then you only need to design your interfaces in such a way that they can easily be implemented by composition.
This is the approach taken in ASP.Net and in WCF; although the latter does a really bad job at making their interfaces user friendly (for even tiny changes you have to re-implement a lot of functionality - which could have been avoided by better interface design).
It does not say “everything should be closed by default unless explicitly marked otherwise”...
Classes should be designed for extension on purpose, not as oversight, one of the SOLID pillars.
Taking advantage of an oversight is an open door for fragile base class problems and hard to track down bugs.
If the author has spent time optimising to that level, overriding probably isn't necessary or warranted in that area.
Makes me wonder: Can you add extension methods to sealed classes?
Or, reflection if you're breaking the rules anyway.
Seriously? Never had to worry about that in Java land. What would be the reason for this?