Profiling and Timing
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
Written against MATLAB R2026b. The timing session below was run in GNU Octave 11.3.0, using only code that Octave also accepts (tic, toc, cputime, pause); it prints truth values rather than durations, because a duration measured in Octave says nothing about MATLAB. Octave 11.3.0 has no timeit, and its profile function returns a differently shaped structure from MATLAB’s. Everything about timeit, the Profiler app and memory is therefore checked line by line against the R2026b documentation and labelled "not run here". No timing in this page is a measurement: a number printed by one machine is not a benchmark for another.
The documentation’s advice is to make the first implementation as simple as possible, and to use profiling to find the bottlenecks only if speed is an issue. Code that is optimised too early is more complex without a significant gain. This page is about the measuring half of that advice: first decide whether a piece of code is slow, then find out where the time goes, and only then change it. The changes themselves (vectorization, preallocation) are on Vectorization and Preallocation, and the Code Analyzer’s performance suggestions are on Code Analyzer and Code Quality.
Choose the right instrument
| Instrument | Use it for | Returns |
|---|---|---|
|
A rough time of a portion of code that is not a complete function. |
Wall-clock seconds between the two calls. |
|
A rigorous time of a whole function, given as a function handle. |
The median of several runs, in wall-clock seconds. |
|
The processor time used by MATLAB, for example to see how parallel a computation is. |
CPU seconds since MATLAB started, summed across threads. |
Profiler ( |
Finding which functions and lines take the time. |
Calls, total time and self time per function, and per-line detail. |
|
How much memory MATLAB and the machine have (Windows only). |
User-focused and system-focused memory structures. |
|
Performance regression tests that run in a test suite. |
|
The documentation recommends timeit or tic and toc for measuring performance and says that cputime could be misleading, because it sums CPU time across all threads and does not count time spent waiting.
tic and toc
tic records the current time and toc reads the elapsed time since it. The forms are:
tic % start the stopwatch
% ... code to time ...
toc % prints Elapsed time is ... seconds.
t = toc % return the elapsed time instead of printing it
timerVal = tic; % keep the start time in a variable
% ... code ...
elapsed = toc(timerVal); % elapsed time since that particular tic
(Not run here; the syntax is the documentation’s.) timerVal is an integer that only toc understands. Passing it explicitly is the way to nest timers, because consecutive calls to tic without an output overwrite the internally recorded start time, so a second bare tic silently restarts the first measurement. The documentation also notes that clear does not reset the start time recorded by tic, and that some actions produce unexpected output: using tic and toc inside code that timeit is timing, or timeit between a tic and a toc.
The documentation’s guidance on the measurement itself is practical:
-
Time a portion that takes more than about a tenth of a second. If the code is faster than that, put it in a loop and divide the total by the number of iterations.
-
Put the code in a function rather than timing it at the command line or inside a script.
-
Run it several times unless the first-time cost is what you want to measure.
-
Avoid
clear allwhile measuring. -
Assign the output to a variable instead of letting it default to
ans.
The session below checks the mechanics with tic and toc and prints only logical results. The three 1 values say that toc returned a non-negative scalar, that a 0.2 second pause is measured as at least 0.19 seconds, and that CPU time is non-decreasing; 0 says that Octave has no timeit.
id = tic;
disp(class(id))
x = sum((1:1e5).^2);
t = toc(id);
disp(isnumeric(t) && isscalar(t) && t >= 0)
t1 = tic; pause(0.2); e = toc(t1);
disp(e >= 0.19)
disp(exist('timeit'))
c0 = cputime; s = 0; for k = 1:2e5, s = s + k; end
disp(cputime - c0 >= 0)
Octave 11.3.0 output (MATLAB’s tic also returns an integer; the class name and the exist result are Octave’s own):
uint64
1
1
0
1
Source: MATLAB documentation: tic, toc
timeit
t = timeit(f) measures the time in seconds that the function handle f takes to run. To make the measurement robust, timeit calls the function several times and returns the median; a fast function is called many times. f must be a handle to a function that takes no input, or a handle to an anonymous function with an empty argument list, so data goes in through the closure. t = timeit(f,numOutputs) calls f with the given number of outputs, which matters for functions whose cost depends on the syntax, such as svd with one output or three. The documentation notes that timeit considers first-time costs, which tic and toc around a single call do not, and that it fully supports thread-based environments. It was introduced in R2013b.
A = rand(12000,400);
B = rand(400,12000);
f = @() sum(A.'.*B, 1);
t = timeit(f) % seconds, the median of several runs
X = rand(100);
t1 = timeit(@() svd(X)); % s = svd(X)
t3 = timeit(@() svd(X),3); % [U,S,V] = svd(X)
(Not run here; the first block follows the documentation’s example, whose printed values depend on the machine and are therefore not repeated.)
Measuring two implementations of the same job is the typical use. For the heatflow scenario, the three versions of the Laplacian (heatflow.laplacianLoop, heatflow.laplacianIndexed and heatflow.laplacianExpanded; heatflow.laplacian itself calls the indexed one) are compared like this:
T = rand(200,200); % a stand-in temperature field
dx = 0.01;
tLoop = timeit(@() heatflow.laplacianLoop(T,dx)); % the loop version
tIndexed = timeit(@() heatflow.laplacianIndexed(T,dx)); % shifted-index version
tExpanded = timeit(@() heatflow.laplacianExpanded(T,dx)); % implicit-expansion version
fprintf("loop/indexed = %.1f, loop/expanded = %.1f\n", tLoop/tIndexed, tLoop/tExpanded);
(Not run here: it needs the heatflow project from Vectorization and Preallocation, and the names are the scenario’s.) Compare the medians as a ratio, and compare them on the size of data you will really use, because the best implementation can change with the size of the array.
Wall-clock time and CPU time
t = cputime returns the total CPU time used by MATLAB since it started, in seconds; to time code, call it before and after and subtract. It is a different quantity from the one tic/toc and timeit return. The documentation gives two reasons to prefer wall-clock time:
-
While MATLAB is paused, for example in
pause(1), the CPU time grows by a small amount while the wall-clock time grows by a second. -
A computation that keeps four cores busy accumulates CPU time about four times faster than the wall clock advances.
So cputime answers "how much processor work was done", while the user of your program feels wall-clock time. The Profiler’s -timer option exposes the same choice (see below).
Source: MATLAB documentation: cputime
The Profiler
Timing tells you that something is slow. The Profiler tells you where. It measures the time spent in every function that runs and, within each function, in every line, so you can decide what is worth changing. Open the app from the Apps tab, or type profile viewer. The documentation workflow is:
-
Run the Profiler on your code, either from the edit box of the Profiler tab (type the statement, then click Run and Time) or, for a file open in the Editor, from Editor > Run > Run and Time.
-
Review the Profile Summary.
-
Investigate the functions and lines that take much time or are called most often.
-
Save the results.
-
Change the code, run
clear allas the documentation advises before profiling again, and compare with the saved results. -
Repeat. When most of the time is in a few built-in functions, the code is probably as fast as you can make it.
The Profiler is not supported in MATLAB Online.
The profile function
Everything the app does can be driven from code with profile. The actions are:
| Command | Effect |
|---|---|
|
Start the Profiler and clear previously recorded statistics. |
|
Stop the Profiler. |
|
Restart the Profiler without clearing the recorded statistics. |
|
Stop the Profiler and clear the statistics. |
|
Stop the Profiler and show the results in the Profiler window. |
|
Stop the Profiler and return the results as a structure. |
|
Return the Profiler status ( |
Options follow the action, as in profile on -history:
-
History:
-nohistoryrecords basic statistics only;-historyalso records the exact sequence of function calls with entry and exit events;-timestamp(the default) adds a timestamp for each event;-historysize Nsets how many events are kept (5,000,000 by default). -
Clock:
-timer "performance"(the default, wall-clock time from the operating system),"processor","real"(the most expensive, and the most intrusive for the profiled code) and"cpu"(computer time summed across all threads, ascputime).
If the Profiler is on and you give an option, MATLAB throws an error; turn it off first. The options persist after you stop the Profiler or clear the statistics, until you reset them or start a new session. profsave writes the results as HTML files to a profile_results folder in the current folder.
The structure returned by profile("info") has a FunctionTable field (one element per function, local function or nested function) with CompleteName, FunctionName, FileName, Type, NumCalls, TotalTime and Children, among others, a FunctionHistory array when the history is recorded, and ClockPrecision, ClockSpeed, Name and Overhead. Saving it with save myprofiledata p keeps a profile that you can load later and compare.
A complete profiling run on a function of the heatflow scenario looks like this:
profile on
plate = heatflow.Plate(200,200); % scenario names; any function of yours works
heatflow.stepMany(plate,500); % the code under study
profile off
p = profile("info");
[~,order] = sort([p.FunctionTable.TotalTime],"descend");
for k = order(1:5)
f = p.FunctionTable(k);
fprintf("%-40s %6d calls %8.3f s\n",f.FunctionName,f.NumCalls,f.TotalTime);
end
profile viewer % the same data, interactively
(Not run here: it needs the heatflow project; heatflow.stepMany is a placeholder for any function of yours. The FunctionTable fields are those listed by the profile reference page.) In Octave 11.3.0 the same profile on, profile off and profile("info") calls run, but the structure holds FunctionTable and Hierarchical fields and the function table has FunctionName, TotalTime, NumCalls, IsRecursive, Parents and Children, with no FileName or Type. Do not port profiling code between the two without checking.
Source: MATLAB documentation: profile, profsave
Reading the report
The Profile Summary has two parts:
-
A flame graph at the top. Each function that ran is a bar, parents below and children above, and the width of a bar is the share of the total run time. User-defined functions are blue and MathWorks functions gray. Hovering shows the exact percentage, time and full name, and clicking opens the detail page of that function.
-
A function table with these columns.
| Column | Meaning |
|---|---|
Function Name |
The function called by the profiled code. |
Calls |
How many times the profiled code called it. |
Total Time |
Time in the function, in seconds, including the time in its child functions. The Profiler’s own overhead is included, and the value can be zero for files whose run time is inconsequential. |
Self Time |
Time in the function excluding its child functions. It also includes some profiling overhead. |
Total Time Plot |
A bar comparing self time with total time. |
A function with a high total time and a low self time is only a caller: look at its children. A function with a high self time is where the work is, and the detail page shows which of its lines take the most time (the section "Lines that take the most time"), its parents and children, Code Analyzer messages for the function, and coverage. Two practical readings follow. A cheap function called a very large number of times can dominate (the Calls column shows it), and the fix is usually to call it fewer times, for example by vectorizing the loop. And when the time is spent in a few built-in functions, there is little left to gain by rewriting your own code.
The Profiler also reports which lines did not run, which is why the same tool is used to see the coverage of a test run; see Unit Testing with matlab.unittest for the coverage report of the testing framework.
For code that runs in parallel, the documentation recommends the parallel profiler of the Parallel Computing Toolbox (see Toolboxes and Add-On Products).
Memory
memory prints how much memory the computer has and how much MATLAB uses, and [userview,systemview] = memory returns the same as structures. Use it when MATLAB runs out of memory or you suspect a slowdown from high memory use. userview holds MaxPossibleArrayBytes (an upper bound on the largest array MATLAB can create now, the size of the largest contiguous free block), MemAvailableAllArrays and MemUsedMATLAB; systemview holds VirtualAddressSpace, SystemMemory and PhysicalMemory structures with Available and Total fields. To turn MaxPossibleArrayBytes into elements, divide by the bytes per element (8 for double); the real limit is lower.
user = memory;
maxElements = user.MaxPossibleArrayBytes / 8; % an upper bound for a double array
fprintf("MATLAB uses %.1f GB\n",user.MemUsedMATLAB/2^30);
(Not run here.) The documentation states that the memory function is available only on Microsoft Windows and is not supported in MATLAB Online. On macOS and Linux, whos shows the size in bytes of each variable in the workspace, and the techniques that reduce memory use are on Vectorization and Preallocation and Large Data, Datastores and Tall Arrays.
Source: MATLAB documentation: memory
Performance tests
A timing done once by hand is not a regression test. The performance testing framework reuses the unit-testing interfaces (script, function and class-based tests): runperf runs the tests as an experiment, running each one five times to warm up and then between 4 and 256 times until the sample mean reaches a 5% relative margin of error at 95% confidence, and returns matlab.perftest.TimeResult objects. Qualifications inside the tests keep the code correct while you measure it, and the same tests can run as ordinary regression tests. It is introduced on the Create and Run Performance Tests page; unit testing itself is on Unit Testing with matlab.unittest. Not run here.
Pitfalls
-
Timing in the wrong place. Timing at the command line or in a script, a code path that is less than a tenth of a second, or a single run, gives noisy numbers. Use
timeiton a function handle. -
The first call is slower. One run measures first-time costs too;
timeitaccounts for them, so does repeating the measurement. -
Output to
ans. Assign the output to a variable when you time an expression. -
A bare second
tic. It overwrites the first start time; usetimerVal = ticto nest. -
ticinsidetimeit. Do not time a function that itself callsticandtoc. -
cputimefor performance. It sums over threads and ignores waiting. -
Profiling the profiler. Profiler overhead is included in the times, so very short functions look worse than they are. Profile the real workload, not a toy.
-
Optimising the wrong thing. Check the Calls and Self Time columns before rewriting a function, and re-profile afterwards.
-
memoryon macOS or Linux. It is Windows only.
Where older books differ
The Davis book (written for R2010a) covers efficient code and the Profiler in its performance and tools chapters, and its Profiler screens are those of the older Java desktop. The functions and interfaces that are newer than the book are timeit (R2013b), which replaces a hand-made timing loop; the Profiler app with its flame graph and the Calls, Total Time and Self Time columns; profile("info") and profsave; and the performance testing framework with runperf. tic and toc are unchanged and remain the quick tool. Old code that measures time with clock and etime or with cputime should be moved to tic/toc or timeit, for the reasons given above. Use the pages above for the current interface rather than the book’s screenshots.