Sign in Join
Wiki copytemporary

Profiling: what changed

Revision 3 to 4. 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
34-` aptitude install valgrind valgrind-callgrind`
34+```text
35+ aptitude install valgrind valgrind-callgrind
36+```
3537
3638To profile your program, you need to do three steps:
3739
3840- Compile your program with the `-gv` switch.
3941- Run your program using `valgrind`
4042- Analyze the output with `valgrind_annotate` or the GUI tool `KCacheGrind`
4143
4244Compile your program with
4345
44-` fpc -gv myprogram.pas`
46+```text
47+ fpc -gv myprogram.pas
48+```
4549
4650Run Valgrind using e.g.
4751
48-` valgrind --tool=callgrind ./myprogram --options --to --my --program`
52+``` pascal
53+ valgrind --tool=callgrind ./myprogram --options --to --my --program
54+```
4955
5056You can also profile only specific parts of your program. For example you can start the program with
5157
52-` valgrind --tool=callgrind --instr-atstart=no ./myprogram --options --to --my --program`
58+``` pascal
59+ valgrind --tool=callgrind --instr-atstart=no ./myprogram --options --to --my --program
60+```
5361
5462At the moment you want actual profiling to start, run
5563
56-` callgrind_control -i on`
64+```text
65+ callgrind_control -i on
66+```
5767
5868To 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.
5969
6070Callgrind has created a trace file with a typical name like `callgrind.out.12345`. To get a human-readable statistical report, run e.g.
6171
62-` callgrind_annotate callgrind.out.12345`
72+```text
73+ callgrind_annotate callgrind.out.12345
74+```
6375
6476Of 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.
6577
6678## Using KCacheGrind
6779
6880Though 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
6981
70-` aptitude install kcachegrind graphviz`
82+```text
83+ aptitude install kcachegrind graphviz
84+```
7185
7286(The `graphviz` package is required if you want KCacheGrind to generate call graphs.)
7387
7488![](/wiki-images/0/0a/KCacheGrind_Lazarus_Screenshot.png)
7589
7690Run KCachegrind with a simple call to `kcachegrind`. You can now open a trace file using the GUI and all should be self-explanatory.
7791
7892On the left, you will see a 'flat profile', which list for each function (in the top 100)
7993
8094- Incl: amount of time spent in the function, *including* callees (functions called by the function).
8195- Self: amount of time spent within the function itself, *excluding* callees.
8296- Called: the number of the times the function has been called.
8397
8498*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.
8599
86100> **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.
87101
88102On 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.
89103
90104## Memory Profiling
91105
92106### Heaptrc, LineInfo
93107
94108To 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:
95109
96110- keepreleased: Any freed memory, is kept and checked if overwritten.
97111- disabled: disable tracing. This is useful for program compiled with heaptrc and should work normally.
98112- 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).
99113- 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.
100114- log=\<filename\>: Instead of writing to stdout, you can give a log filename.
101115
102116Note: **heaptrc** will conflict with **cmem** if you have to debug external libraries. Valgrind should be used in this case.
103117
104118### Valgrind Memcheck
105119
106120There 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:
107121
108-` valgrind --tool=memcheck --leak-check=summary --log-file=log.txt ./project1`
122+```text
123+ valgrind --tool=memcheck --leak-check=summary --log-file=log.txt ./project1
124+```
109125
110126The log.txt will almost always find something, because it tries to check the used external libraries too and some common libraries are badly coded.
111127
112128To get even more information run with
113129
114-` valgrind --tool=memcheck --leak-check=full --log-file=log.txt --show-reachable=yes --num-callers=50 ./project1`
130+```text
131+ valgrind --tool=memcheck --leak-check=full --log-file=log.txt --show-reachable=yes --num-callers=50 ./project1
132+```
115133
116134How to run massif:
117135
118-`valgrind --tool=massif --run-libc-freeres=no  ./project1`
136+```text
137+valgrind --tool=massif --run-libc-freeres=no ./project1
138+```
119139
120140## On macOS
121141
122142Most 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.
123143
124144### Instruments
125145
126146Instruments 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.
127147
128148From 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:
129149
130-`open -a /Applications/Xcode.app/Contents/Applications/Instruments.app`
150+```text
151+open -a /Applications/Xcode.app/Contents/Applications/Instruments.app
152+```
131153
132154Apple documentation links:
133155
134156- [Instruments Help](https://help.apple.com/instruments/mac/current/).
135157- [Getting Started with Instruments](https://developer.apple.com/videos/play/wwdc2019/411/) - WWDC 2019 Video
136158- [Creating Custom Instruments](https://developer.apple.com/videos/play/wwdc2018/410/) - WWDC 2018 Video
137159
138160### Shark
139161
140162> **Note:** Since macOS Lion 10.7, Shark is not on the Apple site any more and was replaced by Instruments above.
141163
142164Shark 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
143165
144166If 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.
145167
146168Shark only works for i386 and PowerPC. For 64 bit platforms, you have to use Instruments.app instead, which offers similar functionality.
147169
148170### spindump
149171
150172The 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`.
151173
152174### Valgrind
153175
154176Valgrind 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/).
155177
156178If you experience the following issue:
157179
158-` valgrind: ./test: cannot execute binary file`
180+```text
181+ valgrind: ./test: cannot execute binary file
182+```
159183
160184Chances 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**.
161185
162186### FPProfiler
163187
164188FPProfiler has been ported to macOS as a fink package. For details see here: [[FPProfiler]]
165189
166190## Simple profiling using ifdefs and timing routines
167191
168192Another, 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]].
169193
170194For example, to profile the following procedure:
171195
172196``` pascal
173197procedure TMyClass.DoSomething;
174198begin
175199 ReallyDoSomething;
176200end;
177201```
178202
179203One would add the following extra code around it:
180204
181205``` pascal
182206function DateTimeToMilliseconds(aDateTime: TDateTime): Int64;
183207var
184208 TimeStamp: TTimeStamp;
185209begin
186210 {Call DateTimeToTimeStamp to convert DateTime to TimeStamp:}
187211 TimeStamp:= DateTimeToTimeStamp (aDateTime);
188212 {Multiply and add to complete the conversion:}
189213 Result:= TimeStamp.Time;
190214end;
191215
192216procedure TMyClass.DoSomething;
193217var
194218 {$IFDEF VerboseProfiler}
195219 lTimeStart: TDateTime;
196220 {$ENDIF}
197221 ...
198222begin
199223 {$IFDEF VerboseProfiler}
200224 lTimeStart := Now();
201225 {$ENDIF}
202226
203227 ReallyDoSomething;
204228
205229 {$IFDEF VerboseProfiler}
206230 DebugLn(Format('[TMyClass.DoSomething] Duration: %d ms', [DateTimeToMilliseconds(Now() - lTimeStart)]));
207231 {$ENDIF}
208232end;
209233```
210234
211235Activate the define to get the debug info in the [[DebugLn]] target.
212236
213237---
214238*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