7

I am originally a native C++ programmer, in C++ every process in your program is bound to your code, i.e, nothing happens unless you want it to happen. And every bit of memory is allocated (and deallocated) according to what you wrote. So, performance is all your responsibility, if you do good, you get great performance.

(Note: Please don't complain about the code one haven't written himself such as STL, it's a C++ unmanaged code after all, that is the significant part).

But in managed code, such as code in Java and C#, you don't control every process, and memory is "hidden", or not under your control, to some extent. And that makes performance something relatively unknown, mostly you fear bad performance.

So my question is: What issues and Bold Lines should I look after and keep in mind to achieve a good performance in managed code?

I could think only of some practices such as:

  • Being aware of boxing and unboxing.
  • Choosing the correct Collection that best suites your needs and has the lowest operation cost.

But these never seem to be enough and even convincing! In fact perhaps I shouldn't have mentioned them.

Please note I am not asking for a C++ VS C# (or Java) code comparing, I just mentioned C++ to explain the problem.

Tamer Shlash
  • 9,314
  • 5
  • 44
  • 82

6 Answers6

5

There is no single answer here. The only way to answer this is: profile. Measure early and often. The bottlenecks are usually not where you expect them. Optimize the things that actually hurt. We use mvc-mini-profiler for this, but any similar tool will work.

You seem to be focusing on GC; now, that can sometimes be an issue, but usually only in specific cases; for the majority of systems the generational GC works great.

Obviously external resources will be slow; caching may be critical: in odd scenarios with very-long-lived data there are tricks you can do with structs to avoid long GEN-2 collects; serialization (files, network, etc), materialization (ORM), or just bad collection/algorithn choice may be the biggest issue - you cannot know until you measure.


Two things though:

  • make sure you understand what IDisposable and "using" mean
  • don't concatenate strings in loops; mass concatenation is the job of StringBuilder
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
4

Reusing large objects is very important in my experience.

Objects on the large object heap are implicitly generation 2, and thus require a full GC to clean up. And that's expensive.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
1

The main thing to keep in mind with performance with managed languages is that your code can change structure at runtime to be better optimized.

For example the default JVM most people use is Sun's Hotspot VM which will actually optimize your code as it runs by converting parts of the program to native code, in-lining on the fly and other optimizations (such as the CLR or other managed runtimes) which you will never get using C++. Additionally Hotspot will also detect which parts of you're code are used the most and optimize accordingly. So as you can see optimising performance on a managed system is slightly harder than on an un-managed system because you have an intermediate layer that can make code faster without your intervention.

I am going to invoke the law of premature optimization here and say that you should first create the correct solution then, if performance becomes an issue, go back and measure what is actually slow before attempting to optimize.

ahjmorton
  • 965
  • 1
  • 5
  • 18
  • .NET always JIT compiles everything to native code. And I don't think it does speculative inlining, or recompilation. – Ben Voigt Jan 08 '12 at 19:14
  • @BenVoigt according to [this answer](http://stackoverflow.com/questions/4043821/performance-differences-between-debug-and-release-builds/4045073#4045073) the .NET JIT compiler does do a number of optimizations including method in-lining. The point I was making is that managed languages do offer the ability to perform such optimizations at runtime, either adaptively or when used. – ahjmorton Jan 08 '12 at 19:27
  • It does inlining when the target can be statically determined. Speculative inlining is a much more advanced technique that Hotspot does and .NET doesn't. – Ben Voigt Jan 08 '12 at 20:08
0

I would suggest understanding better garbage collection algorithms. You can find good books on that matter, e.g. The Garbage Collection Handbook (by Richard Jones, Antony Hosking, Eliot Moss).

Then, your question is practically related to particular implementation, and perhaps even to a specific version of it. For instance, Mono used (e.g. in version 2.4) to use Boehm's garbage collector, but now uses a copying generational one.

And don't forget that some GC techniques can be remarkably efficient. Remember A.Appel's old paper Garbage Collection can be faster than stack allocation (but today, the cache performance matters much much more, so details are different).

I think that being aware of boxing (& unboxing) and allocation is enough. Some compilers are able to optimize these (by avoiding some of them).

Don't forget that GC performance can vary widely. There are good GCs (for your application) and bad ones.

And some GC implementations are quite fast. For example the one inside Ocaml

I would not bother that much: premature optimization is evil.

(and C++ memory management, even with smart pointers, or with ref-counters, can often be viewed as a poor man's garbage collection technique; and you don't have full control on what C++ is doing -unless you re-implement your ::operator new using operating system specific system calls-, so you don't really know a priori its performance)

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
0

.NET Generics don't specialize on reference types, which severely limits how much inlining can be done. It may (in certain performance hotspots) make sense to forgo a generic container type in favor of a specific implementation that will be better optimized. (Note: this doesn't mean to use .NET 1.x containers with element type object).

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
-1

you must : using large objects is very important in my experience.

Objects on the large object heap are implicitly generation 2, and thus require a full GC to clean up. And that's expensive.

saddd
  • 1