Sign in Join
Wiki copytemporary

Profiling: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11**Profiling** is a performance analysis technique that can be used to find bottlenecks in your application. In particular it encompasses measuring the frequency and duration of function calls.
22
33## Profiling and optimization
44
55Profiling can help to identify “critical” parts of your code, whose optimization could seriously improve the performance of your program. It is done best at times when you are preparing for a release and having a feature-freeze for your code. It is a waste of time and resources to optimize something that will be thrown away the next day during some code restructuring.
66
77Be careful not to over-optimize your program. It is seldom worth it to spent days on getting only a 2% performance gain. It is mostly cutting down run-time with 30% in just half an hour of coding a few optimizations that will pay off.
88
99Of course optimizing code that is reused very often is worth a little more effort for performance gain.
1010
1111## Free Pascal profiler support
1212
1313Free Pascal has built-in support for generating executables suitable for (at least) two profilers:
1414
1515- **gprof**, the Gnu Profiler: enable with `-pg`.
1616 - **Availability**: Most FPC target platforms
1717 - **Notes**:
1818 - Support for using gprof under Linux is broken in FPC 2.2.0-2.4.0; it is fixed in FPC 2.6+
1919 - Under Windows, additional libraries are required to get gprof support working;
2020- **Valgrind**, using the plug-in Callgrind: enable with `-gv`.
2121 - **Availablility**: Linux, Mac OS X 10.5 (10.6 in Valgrind development versions), and some other UNIXes.
2222 - **Notes**
2323 - Should work fine on all platforms supported by Valgrind, as it does not require any compiler support (other than that the profiled program should be dynamically linked, and that the compiler should not generate certain kinds of debug information -- both issues are taken care of by the `-gv` option)
2424 - The `-gv` option implicitly includes the [[CMem]] unit; thus you need to remove it from your uses clause
2525
2626There's also an FPC native profiler project called [[FPProfiler]]. Because the profiler is native to FPC it should work on all platforms that FPC supports.
2727
2828A Lazarus IDE addon which enables you to profile your project with one click is also available: [[LazProfiler]]
2929
3030## Using Valgrind/Callgrind
3131
3232Valgrind was originally only a memory debugger, but now it also supports e.g. profiling using the Callgrind plug-in (which has replaced the Calltree plugin). To install Valgrind with Callgrind on Debian, run e.g.
3333
3434` aptitude install valgrind valgrind-callgrind`
3535
3636To profile your program, you need to do three steps:
3737
3838- Compile your program with the `-gv` switch.
3939- Run your program using `valgrind`
4040- Analyze the output with `valgrind_annotate` or the GUI tool `KCacheGrind`
4141
4242Compile your program with
4343
4444` fpc -gv myprogram.pas`
4545
4646Run Valgrind using e.g.
4747
4848` valgrind --tool=callgrind ./myprogram --options --to --my --program`
4949
5050You can also profile only specific parts of your program. For example you can start the program with
5151
5252` valgrind --tool=callgrind --instr-atstart=no ./myprogram --options --to --my --program`
5353
5454At the moment you want actual profiling to start, run
5555
5656` callgrind_control -i on`
5757
5858To stop the profiling, run the same command with `off` instead of `on`. To get only profiling information when you are inside a particular function, you can pass the `--toggle-collect=funcNamePrefix` option to valgrind. Refer to the Valgrind documentation for further details.
5959
6060Callgrind has created a trace file with a typical name like `callgrind.out.12345`. To get a human-readable statistical report, run e.g.
6161
6262` callgrind_annotate callgrind.out.12345`
6363
6464Of course you can also experiment with the other tool plug-ins that ship with Valgrind. Cachegrind, for example, profiles the cache use. Other plug-ins can be used to profile or debug memory allocation or detect data race conditions.
6565
6666## Using KCacheGrind
6767
6868Though the output of `callgrind_annotate` can definitely shed some light on the performance characteristics of your program, the GUI tool KCacheGrind can give you even more interesting information, presented in different forms: caller/callee lists, caller/callee maps ans call graphs. To install it on Debian, run
6969
7070` aptitude install kcachegrind graphviz`
7171
7272(The `graphviz` package is required if you want KCacheGrind to generate call graphs.)
7373
74-<figure>
7574<img src="KCacheGrind_Lazarus_Screenshot.png" title="Example of KCacheGrind showing profiling information for Lazarus (Click to enlarge)" width="500" />
76-<figcaption>Example of KCacheGrind showing profiling information for Lazarus (Click to enlarge)</figcaption>
77-</figure>
7875
7976Run KCachegrind with a simple call to `kcachegrind`. You can now open a trace file using the GUI and all should be self-explanatory.
8077
8178On the left, you will see a 'flat profile', which list for each function (in the top 100)
8279
8380- Incl: amount of time spent in the function, *including* callees (functions called by the function).
8481- Self: amount of time spent within the function itself, *excluding* callees.
8582- Called: the number of the times the function has been called.
8683
8784*Note:* if you click on one of the column headers to sort the list, you might need to do a reload (F5) to ensure that the top 100 *with respect to that column* will be shown. Another hint: in the configuration options, you can make the list longer than only the top 100.
8885
8986> **Note:** "Time" in valgrind/callgrind is based on instructions/cycles executed. It is not exact. A CPU executing a function in real life (outside the profiler) may sometimes spent less time for more cycles.
9087
9188On the right, you'll see a number of tabs, allowing you to see lists and maps of callers and callees, as well as a caller graph, which is very useful to see in a glance who calls whom the most.
9289
9390## Memory Profiling
9491
9592### Heaptrc, LineInfo
9693
9794To spot memory leaks, simply compile with -gh. This will compile as if you added the **heaptrc** unit as first unit to the uses section of your program. Heaptrc links into the memory manager of fpc and checks various things. Together with the -gl option (adding the lineinfo unit) it will give you useful information and backtraces, where a memory block was allocated. It supports some options that can be controlled via environment option *HEAPTRC* containing keywords:
9895
9996- keepreleased: Any freed memory, is kept and checked if overwritten.
10097- disabled: disable tracing. This is useful for program compiled with heaptrc and should work normally.
10198- nohalt: normally heaptrc stops on the first error it finds. Add this keyword if the program should continue (as it would normally do without heaptrc).
10299- haltonnotreleased: Normally heaptrc prints for every not freed block a backtrace, which can take a long time. Add this keyword to stop after first reported mem leak.
103100- log=\<filename\>: Instead of writing to stdout, you can give a log filename.
104101
105102Note: **heaptrc** will conflict with **cmem** if you have to debug external libraries. Valgrind should be used in this case.
106103
107104### Valgrind Memcheck
108105
109106There are mem leaks which heaptrc does not find. The valgrind program has a tool called memcheck, which does a lot more bookkeeping than heaptrc and finds some more bugs. The drawback is that valgrind is snail slow, so do this only if heaptrc could not help you. Here is an example:
110107
111108` valgrind --tool=memcheck --leak-check=summary --log-file=log.txt ./project1`
112109
113110The log.txt will almost always find something, because it tries to check the used external libraries too and some common libraries are badly coded.
114111
115112To get even more information run with
116113
117114` valgrind --tool=memcheck --leak-check=full --log-file=log.txt --show-reachable=yes --num-callers=50 ./project1`
118115
119116How to run massif:
120117
121118`valgrind --tool=massif --run-libc-freeres=no  ./project1`
122119
123120## On macOS
124121
125122Most of what is said above for Linux also applies on macOS. gprof is however not supported by FPC on Darwin/i386 and on Darwin/ARM at this time.
126123
127124### Instruments
128125
129126Instruments is a powerful performance analysis and testing tool that's part of Xcode toolset. Instruments can help you profile your apps on all platforms - iOS, macOS, watchOS, tvOS - in order to better understand and optimize their behavior and performance.
130127
131128From Xcode's product menu, choose Profile. Although Instruments is embedded within and is often used with Xcode, Instruments is a separate app which may be used independently as needed. For example:
132129
133130`open -a /Applications/Xcode.app/Contents/Applications/Instruments.app`
134131
135132Apple documentation links:
136133
137134- [Instruments Help](https://help.apple.com/instruments/mac/current/).
138135- [Getting Started with Instruments](https://developer.apple.com/videos/play/wwdc2019/411/) - WWDC 2019 Video
139136- [Creating Custom Instruments](https://developer.apple.com/videos/play/wwdc2018/410/) - WWDC 2018 Video
140137
141138### Shark
142139
143140> **Note:** Since macOS Lion 10.7, Shark is not on the Apple site any more and was replaced by Instruments above.
144141
145142Shark is a sampling-based profiler. It supports time profiles, profiles based on performance counters (such as L2 cache misses), application-specific profiles and whole-system profiles. By default, it's installed at /Developer/Applications/Performance Tools/Shark.app
146143
147144If you compile the profiled code with debug information (either Stabs or DWARF), Shark will display the (Pascal) source code in addition to the assembler code.
148145
149146Shark only works for i386 and PowerPC. For 64 bit platforms, you have to use Instruments.app instead, which offers similar functionality.
150147
151148### spindump
152149
153150The macOS `spindump` utility can be used with the `-timeline` option to sample and profile your application in order to determine which QoS class applies as a specific portion of code executes at a given time. For more details, see the manual page by opening an Applications \> Utilities \> Terminal and typing `man spindump`.
154151
155152### Valgrind
156153
157154Valgrind has been ported to AMD64/macOS 10.12 (Sierra). There is also preliminary support for X86/macOS 10.13, AMD64/macOS 10.13 (High Sierra). For current details, check valgrind's [homepage](http://valgrind.org/).
158155
159156If you experience the following issue:
160157
161158` valgrind: ./test: cannot execute binary file`
162159
163160Chances are that your valgrind only supports 64 bit while your FPC program is compiled for 32 bit. To make your application compatible simply add the switch **-Px86_64**.
164161
165162### FPProfiler
166163
167164FPProfiler has been ported to macOS as a fink package. For details see here: [[FPProfiler]]
168165
169166## Simple profiling using ifdefs and timing routines
170167
171168Another, simpler, way of profiling is simply adding ifdefs and time counting. For counting the time the simples way is just using Now() from SysUtils. Other possibilities are NowUTC() from lazutf8sysutils or [[EpikTimer]].
172169
173170For example, to profile the following procedure:
174171
175172``` pascal
176173procedure TMyClass.DoSomething;
177174begin
178175 ReallyDoSomething;
179176end;
180177```
181178
182179One would add the following extra code around it:
183180
184181``` pascal
185182function DateTimeToMilliseconds(aDateTime: TDateTime): Int64;
186183var
187184 TimeStamp: TTimeStamp;
188185begin
189186 {Call DateTimeToTimeStamp to convert DateTime to TimeStamp:}
190187 TimeStamp:= DateTimeToTimeStamp (aDateTime);
191188 {Multiply and add to complete the conversion:}
192189 Result:= TimeStamp.Time;
193190end;
194191
195192procedure TMyClass.DoSomething;
196193var
197194 {$IFDEF VerboseProfiler}
198195 lTimeStart: TDateTime;
199196 {$ENDIF}
200197 ...
201198begin
202199 {$IFDEF VerboseProfiler}
203200 lTimeStart := Now();
204201 {$ENDIF}
205202
206203 ReallyDoSomething;
207204
208205 {$IFDEF VerboseProfiler}
209206 DebugLn(Format('[TMyClass.DoSomething] Duration: %d ms', [DateTimeToMilliseconds(Now() - lTimeStart)]));
210207 {$ENDIF}
211208end;
212209```
213210
214211Activate the define to get the debug info in the [[DebugLn]] target.
215212
216213---
217214*From the Free Pascal wiki, page "Profiling", as it stood on 2021-11-07 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history