install config, not config.in
[mono.git] / web / performance
1 * Writing performant .NET and Mono applications
2
3         The following document contains a few hints on how to improve
4         the performance of your Mono/.NET applications.
5
6         These are just guidelines, and you should still profile your
7         code to find the actual performance problems in your
8         application. It is never a smart idea to make a change with the
9         hopes of improving the performance of your code without first
10         measuring. In general, these guidelines should serve as ideas
11         to help you figure out `how can I make this method run faster'.
12         
13         It is up to you to figure out, `Which method is running slowly.'
14
15 ** Using the Mono profiler
16         
17         So, how does one measure what method are running slowly? A profiler
18         helps with this task. Mono includes a profiler that is built
19         into the runtime system. You can invoke this profiler on your program
20         by running with the --profile flag.
21
22 <pre>
23         mono --profile program.exe
24 </pre>
25
26         The above will instruct Mono to instrument your application
27         for profiling.  The default Mono profiler will record the time
28         spent on a routine, the number of times the routine called,
29         the memory consumed by each method broken down by invoker, and
30         the total amount of memory consumed.
31
32         It does this by asking the JIT to insert a call to the profiler
33         every time a method is entered or left. The profiler times the
34         amount of time elapsed between the beginning and the end of the
35         call. The profiler is also notified of allocations.
36         
37         When the program has finished executing, the profiler prints the
38         data in human readable format. It looks like:
39
40 <pre>
41 Total time spent compiling 227 methods (sec): 0.07154
42 Slowest method to compile (sec): 0.01893: System.Console::.cctor()
43 Time(ms) Count   P/call(ms) Method name
44 ########################
45   91.681       1   91.681   .DebugOne::Main()
46   Callers (with count) that contribute at least for 1%:
47            1  100 % .DebugOne::Main(object,intptr,intptr)
48 ...
49 Total number of calls: 3741
50 ...
51 Allocation profiler
52 Total mem Method
53 ########################
54      406 KB .DebugOne::Main()
55          406 KB     1000 System.Int32[]                                  
56   Callers (with count) that contribute at least for 1%:
57            1  100 % .DebugOne::Main(object,intptr,intptr)
58 Total memory allocated: 448 KB
59 </pre>
60
61         At the top, it shows each method that is called. The data is sorted
62         by the total time that the program spent within the method. Then
63         it shows how many times the method was called, and the average time
64         per call.
65         
66         Below this, it shows the top callers of the method. This is very useful
67         data. If you find, for example, that the method Data::Computate () takes
68         a very long time to run, you can look to see if any of the calls can be
69         avoided.
70         
71         Two warnings must be given about the method data. First,
72         the profiler has an overhead associated with it. As such,
73         a high number of calls to a method may show up as comsuming
74         lots of time, when in reality they do not consume much time
75         at all. If you see a method that has a very high number of
76         calls, you may be able to ignore it. However, do consider
77         removing calls if possible, as that will sometimes help
78         performance. This problem is often seen with the use
79         of built in collection types.
80         
81         Secondly, due to the nature of the profiler, recursive calls
82         have extermely large times (because the profiler double counts
83         when the method calls itself). One easy way to see this problem
84         is that if a method is shown as taking more time than the Main
85         method, it is very likely recursive, and causing this problem.
86         
87         Below the method data, allocation data is shown. This shows
88         how much memory each method allocates. The number beside
89         the method is the total amount of memory. Below that, it
90         is broken down into types. Then, the caller data is given. This
91         data is again useful when you want to figure out how to eliminate calls.
92         
93         You might want to keep a close eye on the memory consumption
94         and on the method invocation counts.   A lot of the
95         performance gains in MCS for example came from reducing its
96         memory usage, as opposed to changes in the execution path.
97
98 ** Memory Management in the .NET/Mono world.
99
100         Since Mono and .NET offer automatic garbage collection, the
101         programmer is freed from having to track and dispose the
102         objects it consumes (except for IDispose-like classes).   This
103         is a great productivity gain, but if you create thousands of
104         objects, that will make the garbage collector do more work,
105         and it might slow down your application.
106         
107         Remember, each time you allocate an object, the GC is forced
108         to find space for the object. Each object has an 8 byte overhead
109         (4 to tell what type it is, then 4 for a sync block). If
110         the GC finds that it is running out of room, it will scan every
111         object for pointers, looking for unreferenced objects. If you allocate
112         extra objects, the GC then must take the effort to free the objects.
113         
114         Mono uses the Boehm GC, which is a conservative collector,
115         and this might lead to some memory fragmentation and unlike
116         generational GC systems, it has to scan the entire allocated
117         memory pool.
118         
119 *** Boxing
120         The .NET framework provides a rich hierchy of object types.
121         Each object not only has value information, but also type
122         information associated with it. This type information makes
123         many types of programs easier to write. It also has a cost
124         associated with it. The type information takes up space.
125         
126         In order to reduce the cost of type information, almost every
127         Object Oriented language has the concept of `primitatives'.
128         They usually map to types such as integers and bools. These
129         types do not have any type information associated with them.
130         
131         However, the language also must be able to treat primitatives
132         as first class datums -- in the class with objects. Languages
133         handle this issue in different ways. Some choose to make a
134         special class for each primative, and force the user to do an
135         operation such as:
136 <pre>
137 // This is Java
138 list.add (new Integer (1));
139 System.out.println (list.get (1).intValue ());
140 </pre>
141
142         The C# design team was not satisfied with this type 
143         of construct. They added a notion of `boxing' to the language.
144         
145         Boxing preforms the same thing as Java's <code>new Integer (1)</code>.
146         The user is not forced to write the extra code. However,
147         behind the scenes the <em>same thing</em> is being done
148         by the runtime. Each time a primative is cast to an object,
149         a new object is allocated.
150         
151         You must be careful when casting a primative to an object.
152         Note that because it is an implicit conversion, you will
153         not see it in your code. For example, boxing is happening here:
154
155 <pre>
156 ArrayList foo = new ArrayList ();
157 foo.Add (1);
158 </pre>
159         
160         In high performance code, this operation can be very costly.
161
162 *** Using structs instead of classes for small objects
163
164         For small objects, you might want to consider using value
165         types (structs) instead of object (classes).
166         
167         However, you must be careful that you do not use the struct
168         as an object, in that case it will actually be more costly.
169         
170         As a rule of thumb, only use structs if you have a small
171         number of fields (totaling less than 32 bytes), and
172         need to pass the item `by value'. You should not box the object.
173
174 *** Assisting the Garbage Collector
175
176         Although the Garbage Collector will do the right thing in
177         terms of releasing and finalizing objects on time, you can
178         assist the garbage collector by clearing the fields that
179         points to objects.  This means that some objects might be
180         elegible for collection earlier than they would, this can help
181         reduce the memory consumption and reduce the work that the GC
182         has to do.
183
184 ** foreach
185
186         The <tt>foreach</tt> C# statement handles various kinds of
187         different constructs (about seven different code patterns are
188         generated).   Typically foreach generates more efficient code
189         than loops constructed manually, and also ensures that objects
190         which implement IDispose are properly released.
191
192         But foreach sometimes might generate code that under stress
193         performs badly.  Foreach performs badly when its used in tight
194         loops, and its use leads to the creation of many enumerators.
195         Although technically obtaining an enumerator for some objects
196         like ArrayList is more efficient than using the ArrayList
197         indexer, the pressure introduced due to the extra memory
198         requirements and the demands on the garbage collector make it
199         more inneficient.
200
201         There is no straight-forward rule on when to use foreach, and
202         when to use a manual loop.  The best thing to do is to always
203         use foreach, and only when profile shows a problem, replace
204         foreach with for loops.