Generated banner for Matlab Optimize Performance
Generated logo for Matlab Optimize Performance

Matlab Optimize Performance

MathWorksOfficialCreated by MathWorks

  • Skill
  • Workflow
  • Software development
  • BSD-2-Clause
  • No issues found

From the author

Use when MATLAB code is too slow or the user wants it faster, or asks to profile it, find its hot line or where the time goes, count how many times a function runs, or benchmark or compare timings. Profiles before changing code (Sampling Profiler on R2026b+), checks the output against the original, and reports only measured speedups. Not for writing performance tests.
— the description by MathWorks

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a rigorous, data-driven methodology for identifying and resolving performance bottlenecks in MATLAB. It emphasizes profiling before modification, verifying performance gains through ablation, and ensuring numerical correctness throughout the optimization process. The workflow is designed to move beyond guesswork by using precise timing tools and established optimization patterns, ensuring that reported speedups are genuine and reproducible.

What it does

  • Establishes performance baselines using timeit for accurate measurement.
  • Identifies bottlenecks using the Sampling Profiler or standard MATLAB profiler.
  • Provides a catalog of proven optimization patterns like vectorization and preallocation.
  • Validates performance gains through ablation and interleaved paired timing.
  • Ensures numerical correctness with tolerance-scaled comparisons.
  • Generates structured performance reports for documentation and verification.

When to use it

  • When MATLAB code execution is too slow and requires optimization.
  • When you need to identify specific lines or functions causing performance degradation.
  • When you need to benchmark or compare the performance of different code versions.
  • When you need to verify that an optimization maintains numerical accuracy.

Advantages

  • Reduces reliance on intuition by using empirical profiling data.
  • Prevents false optimizations through the ablation measurement protocol.
  • Ensures code remains correct while becoming faster.
  • Provides a standardized approach for reporting performance improvements.

Usage examples

“My MATLAB script is running slowly. How can I find the bottleneck?”
The agent will guide you through establishing a baseline with timeit and using the Sampling Profiler to identify the specific lines of code consuming the most execution time.
“I think vectorizing this loop will speed up my code. How do I verify it?”
The agent will help you implement the vectorized version, perform an ablation test to confirm the speedup, and run a correctness check to ensure the output matches the original loop.

Install

Documentation by MathWorks

MATLAB Performance Optimization Workflow

Systematic 7-step workflow for finding and fixing performance bottlenecks in MATLAB code.

When to Use

  • User asks to speed up MATLAB code, or to optimize it for execution time

  • User wants to find why their MATLAB code is slow

  • User has a function or script that takes too long to run

  • User asks to benchmark or time MATLAB code in the current session (a one-off timing or an A/B of two versions)

  • User wants to compare performance before and after a change

  • User asks about MATLAB performance best practices

When NOT to Use

  • Optimizing Simulink model simulation speed (use Simulink Profiler)

  • Speeding code up by compiling it with MATLAB Coder (codegen, MEX), or tuning generated MEX (use the matlab-generate-code skill)

  • The problem is memory, not speed: out-of-memory errors or peak memory (use the matlab-optimize-memory skill)

  • The performance issue is purely I/O-bound (file reads, network, database)

  • User wants to write performance tests, or track timings across builds or in CI (use the matlab-write-performance-tests skill)

  • Note: for exact invocation counts, or a sub-10 ms / MEX / parallel workload, use the MATLAB profiler (profile) rather than the Sampling Profiler (Step 2) — that's a profiler choice, not a reason to skip the skill

The 7-Step Workflow

Step 1: Establish Baseline

Measure current performance so you have a number to improve against:

f = @() targetFunction(input1, input2);
baseline = timeit(f);   % handles warm-up and repeats; prefer it to tic/toc

For GPU code use gputimeit. For a script, run it once to warm up, then time a second run with tic/toc. Templates: assets/measurement-templates.md.

Step 2: Profile and Analyze

Find where the time actually goes — don't guess, profile.

Pick the profiler first: check, don't assume. In R2026b+ the Sampling Profiler is the default: a low-overhead statistical profiler that samples the execution thread at ~100 Hz. This skill's sampling_profiler helper drives it, and sampling_profiler('available') tells you whether this MATLAB has it. Use the MATLAB profiler (profile) only when available is false, when you need exact call counts, or for a one-shot call too short to loop, or MEX or parallel code.

% Sampling Profiler — use when sampling_profiler('available') is true (clean up after)
addpath(fullfile(skillRoot, "scripts"));   % skillRoot = directory containing this SKILL.md
assert(sampling_profiler('available'));     % false -> use the MATLAB profiler block below
nReps = 1;   % raise until the loop below runs several seconds (~10 s is ideal)
sampling_profiler('on');
sampling_profiler('pause');
targetFunction(input1, input2);            % warm-up: pays first-call cost, not collected
sampling_profiler('clear');
sampling_profiler('resume');
for r = 1:nReps, targetFunction(input1, input2); end
[~, stacks] = sampling_profiler('off');
sampling_profiler('destroy');              % mandatory cleanup: skipping it corrupts later runs
% MATLAB profiler — any release; use when the Sampling Profiler isn't available
profile on; targetFunction(input1, input2); profile off; profile viewer;

Reading the sampler:

  • Run several seconds; ~10 s is ideal (~1000 samples; ~100 is the bare minimum). Loop a quick workload by raising nReps, and keep the warm-up call. If it can't be looped, use profile.

  • The counts describe the profiled run (stacks.TotalSamples/100 is close to its wall clock) and include no invocation counts. For how many times something ran, use profile, and record "call_count_source": "builtin_profile_numcalls", "sampling_can_count_invocations": false and "sampling_profiler_samples" in perf_report.json.

  • Neither profiler sees inside a builtin or P-coded frame: the time lands on the call site with no line detail. Confirm such a hotspot by removing it and re-timing (Step 5).

Work from the profile to a change:

  • Rank functions by self time, then read the lines inside the top one. Per-line time includes callees, so a line that only makes a call can outrank the line doing the work.

  • Before changing anything, say from the code why that line is slow: a real mechanism (a reallocation in a loop, a builtin on a large array), not just a big percentage.

  • Step 5 is the proof: if re-timing doesn't move the clock, your explanation was wrong; profile again.

For the full Sampling Profiler API, results model, traps, and helpers, see references/sampling-profiler.md.

Step 3: Identify Optimization Opportunities

Based on profiling results, identify which patterns apply. Read references/optimization-patterns.md for the full catalog.

PatternTypical SpeedupLook For
Vectorization2–200xLoops doing element-wise math on arrays
Preallocation2–100xArrays growing inside loops (x = [x; newRow])
Unnecessary recomputation2–50xSame expensive expression computed multiple times
discretize/histcounts2–50xLoops binning or classifying data
Persistent caching1.5–95xRepeated load() or expensive object creation
Logical indexing1.2–5xUsing find() just to index into an array
arguments block1.1–1.8xFunctions using inputParser
Algebraic simplification1.5–3xRedundant sqrt, abs, or matrix ops

Before optimizing, verify the target is worth it:

  • Is it called in a tight loop? High call count × small time = big total.

  • Is it M-code or a built-in? You can't make a built-in faster, but you can often call it fewer times (e.g., pass a matrix to filtfilt/filter instead of looping over columns).

Step 4: Implement Optimizations

Apply the patterns from Step 3, starting with the highest-impact one. Before/after examples are in references/optimization-patterns.md.

  • Move invariant work out of loops (object creation, option parsing, constant expressions)

  • Replace element-wise loops with array operations where possible

  • Use purpose-built functions (discretize, cumsum, hypot) instead of hand-written equivalents

  • For large data, batch the vectorization to control memory (see Pattern 9 in catalog)

  • Try each new variant on a small slice of the input first: a wrong orientation in implicit expansion can build a huge array

Step 5: Measure Optimized Performance

Quick check, while you are still iterating: re-time with timeit as in Step 1 and compare with the baseline. A speedup of 1.2x or more is considered significant; below that, measurement noise makes it hard to be confident the change helped.

Verified result — required whenever the user asks for a number to report, commit, or share, when they gave you a hypothesis about what is slow, or when two profilers disagree. Follow references/measurement-protocol.md in full:

  1. Measure the ceiling before optimizing — delete or hoist the suspected work, re-time end-to-end, and compute what fraction of total time it actually accounted for; profiler attribution has been observed to overstate it by more than 10x. (This removal-and-re-time step is called ablation, and is recorded as adjudication_method: "removal_ablation".)

  2. Correctness gate against the original in the same session, with a tolerance scaled to the output (100*eps(max(abs(original(:))))), plus an independent witness statistic of the output.

  3. Mutation test — perturb the input; the output must change. Catches code that is fast because it stopped doing the work.

  4. Interleaved paired timing, one session, N ≥ 5, median(tb ./ to). Never compare across sessions or in sequential blocks — machine drift lands entirely on one arm.

  5. Write perf_report.json — every field from a variable a run of the current code computed, never a typed-in literal. Run each step in its own tool call and save its results to a .mat as it finishes, so a fix to the report re-writes it from those files instead of re-running the steps.

The headline speedup_median is always end-to-end. A sub-kernel number goes in kernel_speedup. A refuted hypothesis is a result: report it with the honest speedup, including 1.0.

Step 6: Verify Correctness

Every optimization must produce the same results as the original:

original = originalFunction(input1, input2);
fast = optimizedFunction(input1, input2);

% Scale the tolerance to the output — a fixed 1e-10 is wrong in both directions
tol = 100 * eps(max(abs(original(:))));
maxErr = max(abs(original(:) - fast(:)));
fprintf('Max error: %.2e (tol %.2e)\n', maxErr, tol);
assert(isequal(isnan(original), isnan(fast)) && maxErr <= tol, 'Results differ beyond tolerance!');   % max skips NaN

For non-numeric outputs:

assert(isequal(original, fast), 'Results differ!');

Error at or below eps(max(abs(original(:)))) is floating-point reordering noise — acceptable; document it. Error orders of magnitude above it is a bug, however clean the algebra looked. Watch for catastrophic cancellation: an algebraically exact shortcut (e.g. the running-sums variance formula) can be dramatically faster and still wrong on data with a large mean offset. Subtract the mean first, then check the result against the tolerance again.

An error of exactly 0 proves nothing on its own. Also record an independent statistic of the optimized output (min(fast(:)), a checksum) — see references/measurement-protocol.md.

Step 7: Report Results

Report the baseline, the optimized time, the speedup (for each change and end to end), the max error, and the pattern applied. If you followed the verified-result protocol in Step 5, perf_report.json is the deliverable and this is its human-readable summary.

Key Rules

  1. Always profile before optimizing — never guess where the bottleneck is

  2. Measure the ceiling before you optimize — remove or hoist the suspect and re-time end-to-end; that difference, not the profiler's % column, is the ceiling on what a rewrite can win (this step is ablation)

  3. One change at a time — measure after each optimization to know what helped

  4. Verify correctness — every optimization must produce equivalent output, with a tolerance scaled to the output

  5. Report end-to-end — the headline number is the whole workload; a sub-kernel speedup is reported separately and labelled as such

  6. Every reported number comes from a variable — computed by a run of the current code, never typed in from memory

  7. 1.2x threshold — speedups below 1.2x are not reliably distinguishable from noise

  8. GPU timing — always wait(gpuDevice) before and after timing GPU code

  9. Use timeit — it handles warmup and averaging; avoid raw tic/toc for benchmarks

  10. On R2026a, clear what you profiled before you time anything — code run under profile on R2026a stays ~100x slower for the rest of the session until you clear it, so a baseline taken after a profiled run flatters every "after" number. R2026a also has no Sampling Profiler.

Common Mistakes

MistakeWhy It's WrongDo This Instead
Growing arrays in loopsEach append copies the entire arrayPreallocate before the loop
Vectorizing huge arrays blindlyMay exceed memoryUse chunked processing for large data
Assuming faster = correctBugs can make code fast (by skipping work)Verify results match (Step 6) + mutation test
Fixed assert(maxErr < 1e-10)Rejects clean results on large-magnitude data; passes broken ones on smallScale tolerance to the output: 100*eps(max(abs(original(:))))
Quoting a sub-kernel speedup as the headline8x on 20% of runtime is ~1.2x end-to-endHeadline is end-to-end; kernel number reported separately
Manufacturing a win when the hypothesis was wrongA refuted hypothesis is a valid resultReport speedup_median honestly, including 1.0

Reference Files

  • references/measurement-protocol.md — the verified-result protocol for Step 5 and every perf_report.json field

  • references/sampling-profiler.md — Sampling Profiler (R2026b+): commands, results model, traps, and the scripts/ helper index

  • references/optimization-patterns.md — the pattern catalog, with before/after code and measured speedups

  • assets/measurement-templates.md — templates for a timeit baseline, GPU timing, and a scaling test

  • scripts/ — MATLAB helpers for the Sampling Profiler; addpath this folder to use them

Copyright 2026 The MathWorks, Inc.

Files

references/measurement-protocol.md

Verified Measurement Protocol

A profiler tells you where time is attributed. It does not tell you what your change will save. This protocol closes that gap and produces a machine-checkable record of the result.

When to use it

SituationWhat to do
Exploring, iterating, "is this faster?"Quick check: timeit before/after, report the ratio. Stop here.
The user asks for a number to report, commit, share, or put in a PRFull protocol below, all five steps, and write perf_report.json.
The user gives you a hypothesis about what's slowFull protocol. Step 1 either confirms or refutes it; say which.
You are asked to profile with both profilers, or the two disagreeFull protocol. Step 1 adjudicates.

The quick check is fine for your own navigation. It is not fine as a claim. A ratio from two timeit calls has no correctness gate, no drift control, and no evidence that the optimized code still does the work.

Step 1 — Measure the ceiling before you optimize

Before writing the optimized version, measure the ceiling: delete or hoist the suspected work and re-time end to end. Confirm the target code is a significant fraction of total time before you spend any effort on it.

This removal-and-re-time step is called ablation (the term comes from ablation studies in experimental design). It is recorded in perf_report.json as adjudication_method: "removal_ablation", and the rest of this document uses the short name.

tFull       = timeit(@() pipeline(data));              % everything
tNoSuspect  = timeit(@() pipelineNoSuspect(data));     % user's suspect removed
tNoDominant = timeit(@() pipelineNoDominant(data));    % dominant stage removed
userSuspectedStageSharePct = 100 * (tFull - tNoSuspect)  / tFull;
dominantStageSharePct      = 100 * (tFull - tNoDominant) / tFull;

Each share is the hard upper bound on any speedup you can get from that stage. If it is 8%, no rewrite of that stage will produce a 3x end-to-end win, whatever the profiler's % column says.

Do this before optimizing, not after. Afterwards you are motivated to believe the number.

Why this step exists. Profiler attribution is not causation, and the error can be an order of magnitude — a per-iteration allocation on a hot loop was attributed ~9.6% of self-time against a true ~0.7% share (measured), and at a low sample count the same line inverted to 60% while the real hotspot read 0%. Ablation gives you the ceiling: a line attributed 60% but worth 0.7% cannot return more than 1.007x however well you rewrite it.

When the two profilers disagree, or when a single profiler's attribution looks too good, ablation is the tiebreaker. Record:

  • adjudication_method: "removal_ablation"

  • user_suspected_stage_share_pct: the measured share of the stage the user suspected, from removal ablation

  • dominant_stage_share_pct: the measured share of the stage that actually dominates, from removal ablation

  • profiler_time_inflation_x: sum(ExecutedLines(:,3)) / timeit(@() pipeline(data)) — how far the instrumented run's own accounting exceeds the true unprofiled time. Report it whenever you dispute a % column: a large value is evidence that the column's denominator is not the wall clock, so shares read off it do not mean what they appear to. Read it for what it is — ExecutedLines(:,3) is inclusive of callees, so nesting counts the same seconds once per stack level; it is not a measure of instrumentation overhead (for that, use profiled wall ÷ unprofiled wall) and not a profiler-selection gate.

  • profiler_attribution_overstatement_x: a stage's profiler-attributed share ÷ that same stage's measured ablation share. 1.0 means the profiler attributed that stage correctly; 2.0 means it overstated the stage twofold. State which stage it refers to. Optional diagnostic — profiler_time_inflation_x is the one to report first.

Do not use one name for both. They answer different questions: inflation is about the total, overstatement is about one stage's slice of it, and for an honest profiler the first is large while the second is near 1.0.

Step 2 — Correctness gate, against the original, in the same session

original = originalFunction(input1, input2);
fast     = optimizedFunction(input1, input2);

maxAbsErr = max(abs(original(:) - fast(:)));
optimizedMinValue = min(fast(:));     % independent witness — see below

Set correctness_check to "elementwise_vs_original_same_session" (or "isequal_vs_original_same_session" for non-numeric output). Both functions must be called by a run of the current code — a remembered number from an earlier session is not a check.

Choose the tolerance from the data, not from habit

A fixed 1e-10 is wrong in both directions. The floor on honest floating-point reordering error is set by the magnitude of the output:

tol = 100 * eps(max(abs(original(:))));
  • Error at or below eps(max(abs(original(:)))) is reordering noise. Accept it.

  • Error orders of magnitude above it is a bug, however elegant the algebra looked.

Record an independent witness

max_abs_err alone is unfalsifiable: a report claiming 0 looks identical whether the check ran or not. So also record a scalar statistic of the optimized output itself:

optimizedMinValue = min(fast(:));

optimized_min_value is a value only MATLAB can tell you, and it is not derived from the error. When an algebraically-equivalent shortcut cancels catastrophically, the error and the witness break together — a numerically dead output collapses to exactly 0, while every correct route reports the same nonzero minimum. Two numbers that must agree are much harder to invent than one.

Catastrophic cancellation is the canonical trap here. The textbook running-sums variance shortcut, sqrt((S2 - S1^2/w)/(w-1)), is algebraically exact and dramatically faster, but on data with a large mean offset it is wrong — measured error orders of magnitude above the true spread, with some outputs collapsing to exactly zero (which is where the witness statistic catches it). Subtract the mean first, then check the result against the tolerance again.

Step 3 — Mutation test

Confirm the optimized function still depends on its input:

perturbed = input1;
perturbed(1) = perturbed(1) + 1;
mutationTestPass = ~isequal(optimizedFunction(perturbed, input2), fast);

This catches the failure mode a correctness check cannot: code that is fast because it stopped doing the work. A cached return value, a short-circuit that skips the loop, or an accidentally hoisted computation all match the original on the one input you tested and are all caught here. Record mutation_test_pass.

Step 4 — Interleaved paired timing, same session, N ≥ 5

Never compare a baseline timed in one session against an optimized version timed in another. Machine state drifts: thermal throttling, other load, JIT and memory-pool warmth. Sequential blocks (all baselines, then all optimized) hand the entire drift to one arm.

Alternate them inside one loop, in one MATLAB session:

n = 7;
tb = zeros(n,1);
to = zeros(n,1);

originalFunction(input1, input2);    % warm both paths first
optimizedFunction(input1, input2);

for k = 1:n
    tb(k) = timeit(@() originalFunction(input1, input2));
    to(k) = timeit(@() optimizedFunction(input1, input2));
end

speedupMedian = median(tb ./ to);    % median of per-rep RATIOS

Take the median of the per-rep ratios, not the ratio of the means. One slow rep from an unrelated process skews a mean; the median absorbs it. Report n_reps so the reader knows how much averaging is behind the number.

Each timed call must span ≥ 1.0 s. If the target runs shorter, wrap a repetition loop, and record profiled_duration_s — the wall-clock span you actually measured. A speedup quoted from a 2 ms measurement is not a result.

One second, not less, for two independent reasons: it puts the measurement well clear of the timer's own resolution, and if you profile the same region with the Sampling Profiler (100 Hz) it is what buys you ≥ 100 samples. Below ~20 samples a hotspot is indistinguishable from noise.

Derive the repetition count from a pilot measurement rather than picking one — a hand-chosen nRep on a fast function silently leaves you back at the noise floor. Then verify the span you actually achieved, because nRep * pilot overpredicts it: timeit strips its own per-call overhead, so inside a loop each call costs less than the pilot suggested (measured: ~18% short of the prediction). Trust the measured span, not the prediction.

function [nRep, span] = repsForSpan(f, targetS)
    pilot = timeit(f);
    nRep  = max(1, ceil(targetS / pilot));
    span  = timeit(@() repeatN(f, nRep));
    while span < targetS                       % trust the measurement, not the prediction
        nRep = ceil(nRep * 1.2 * targetS / span);
        span = timeit(@() repeatN(f, nRep));
    end
end

function repeatN(f, n)
    for k = 1:n
        f();
    end
end

Size each arm separately, and divide each timing by its own nRep. A shared nRep is not merely wasteful — sized for the fast arm it is unrunnable on the slow one. In the worked case the optimized arm needed nRep in the thousands; applying that to a 0.23 s baseline would be hundreds of seconds per timed call, and there are 2n of them.

[nRepB, spanB] = repsForSpan(@() originalFunction(input1, input2),  1.0);
[nRepO, spanO] = repsForSpan(@() optimizedFunction(input1, input2), 1.0);

for k = 1:n
    tb(k) = timeit(@() repeatN(@() originalFunction(input1, input2),  nRepB)) / nRepB;
    to(k) = timeit(@() repeatN(@() optimizedFunction(input1, input2), nRepO)) / nRepO;
end

speedupMedian     = median(tb ./ to);
profiledDurationS = min(spanB, spanO);   % the weaker of the two spans is what you can defend

Report profiled_duration_s as the smaller achieved span. It is the arm with the least averaging behind it, so it bounds the confidence of the whole comparison.

Step 5 — Write perf_report.json

Every number in this file must come from a variable computed by a run of the current code. Never type a measurement as a literal. save each step's results to a .mat as it finishes, so a fix to the report re-writes it from those files instead of re-running the steps.

Always required
KeyTypeMeaning
targetstringWhat was optimized
pattern_appliedstringWhich optimization pattern
measurement_modestring"interleaved_paired_same_session"
n_repsintReps in the paired loop, ≥ 5
baseline_median_sdoublemedian(tb)
optimized_median_sdoublemedian(to)
speedup_mediandoublemedian(tb ./ to)
headline_scopestring"end_to_end" — see below
correctness_checkstring"elementwise_vs_original_same_session"
max_abs_errdoubleFrom Step 2
optimized_min_valuedoubleThe witness, from Step 2
mutation_test_passlogicalFrom Step 3
Required when applicable
KeyTypeWhen
adjudication_methodstring"removal_ablation" when you resolved a disputed hotspot
profiler_time_inflation_xdoubleWhenever you dispute a profiler % column
profiler_attribution_overstatement_xdoubleOptional; when you compared one stage's profiler attribution against its ablation share
prediction_gap_pctdouble100*(predicted - achieved)/achieved. Always report it if you predicted anything.
profiled_duration_sdoubleAlways when a repetition loop was needed; the smaller achieved span, ≥ 1.0 s
kernel_speedupdoubleWhen you also quote a sub-kernel number
user_hypothesis_supportedlogicalWhenever the user proposed a cause
user_suspected_stage_share_pctdoubleAlways when Step 1 ran and the user named a suspect
dominant_stage_share_pctdoubleAlways when Step 1 ran
call_count_sourcestring"builtin_profile_numcalls" when invocation counts are needed
invocation_count_sourcestringSame — never "sampling"
sampling_can_count_invocationslogicalfalse. The Sampling Profiler cannot count calls.
sampling_profiler_samplesintTotalSamples. Below ~20 samples a hotspot is noise.
The headline number is end-to-end

headline_scope must be "end_to_end", and speedup_median must be the end-to-end figure. If you made an inner kernel 8x faster and the kernel is a fifth of runtime, the honest headline is about 1.2x. Put the 8x in kernel_speedup, where it is informative rather than misleading.

Report a refuted hypothesis as a result

If the user's suspected bottleneck turns out to be 2% of runtime, that is a successful investigation, not a failure. Set user_hypothesis_supported to false, record both shares, and report the honest speedup_median — including 1.0 when there was nothing to win. Do not manufacture a speedup to have something to show.

Writing the JSON
report = struct();
report.target = 'targetFunction';
report.measurement_mode = 'interleaved_paired_same_session';
report.n_reps = n;
report.speedup_median = speedupMedian;
report.headline_scope = 'end_to_end';
report.correctness_check = 'elementwise_vs_original_same_session';
report.max_abs_err = maxAbsErr;
report.optimized_min_value = optimizedMinValue;
report.mutation_test_pass = mutationTestPass;   % must be logical, not 1

fid = fopen('perf_report.json', 'w');
fprintf(fid, '%s', jsonencode(report, 'PrettyPrint', true));
fclose(fid);

Two encoding gotchas that silently produce an unreadable report:

  • Booleans must be logical. report.mutation_test_pass = 1 encodes as 1, not true, because jsonencode maps the double 1. Use true/false or logical(...).

  • Use 'PrettyPrint', true. Compact jsonencode emits "key":value with no space after the colon. Both spellings are valid JSON, but pretty-printed output is what a reader — and most simple text checks — expects.

Also note MATLAB's exponent format: 1.318e-09 is emitted as 1.318E-9 — uppercase E, no +, no zero padding.

Checklist

Before quoting a performance number as a result:

  • [ ] Measured the ceiling before optimizing — removed and re-timed the suspected and dominant stages (ablation)

  • [ ] Elementwise correctness against the original, same session, tolerance justified from the data

  • [ ] Recorded an independent witness statistic of the output

  • [ ] Mutation test passes

  • [ ] Paired, interleaved, one session, N ≥ 5, median of ratios

  • [ ] Each timed call spans ≥ 1.0 s (repetition loop if needed)

  • [ ] Headline number is end-to-end

  • [ ] perf_report.json written, every field from a computed variable

Copyright 2026 The MathWorks, Inc.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
MathWorks
Publisher
MathWorks (Official)
Source
matlab/matlab-agentic-toolkit · skills-catalog/matlab-software-development/matlab-optimize-performance · commit 2a7f215
Licence
BSD 2-Clause License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE.md)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by MathWorks.

Get it from the source

1 of this skill’s files are not mirrored here, so there is no ZIP. Get the complete skill from its source: matlab/matlab-agentic-toolkit