agentsclimarketplace

Matlab profile

Skill richardkmichael/claude-rodin/skills/matlab-profile

A collection of skills, subagents, and hooks for Claude Code.

Install
npx -y skills add richardkmichael/claude-rodin --skill matlab-profile

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 11 days oldThe repository was created 11 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Investigate MATLAB performance problems: time hotspots, memory consumption, parallelism, call patterns. Configures the profiler, runs code under instrumentation, analyzes results across multiple passes, and reports findings with automatic before/after comparison. Use this skill when the user says "profile", "matlab profile", "why is this slow", "memory usage", "hotspot", "performance", "where is time going", "bottleneck", or invokes /matlab-profile. Also use when the user asks to optimize MATLAB code and you need to measure before changing.

SKILL.md

18.0 KB, as published. Nobody here has run it

MATLAB Performance Investigation

Investigate performance problems in the user's MATLAB code. The profiler is a diagnostic tool — choose the right options and passes based on what you are trying to learn. Iterate to build a complete picture, then report findings. Do the investigation; the user decides what to change.

Read ${CLAUDE_SKILL_DIR}/references/PROFILER_REFERENCE.md at the start of every investigation for profiler options, output struct schema, and interpretation rules.

Tools

All scripts are in ${CLAUDE_SKILL_DIR}/scripts/. Invoke them directly; they are executable.

Analysis (take profile.json):

  • rank_functions.py <profile.json> [--top N] [--by time|memory|calls]
  • line_breakdown.py <profile.json> [<function>] [--top N] [--lines M]
  • compare_runs.py <before.json> <after.json> [--top N]

MATLAB-side helpers (inline into matlab -batch commands or run via matlab -batch "run('...'); ..."):

  • export_profile_json.m — function export_profile_json(info, outPath), serialises profile('info') to JSON.
  • history_query.m — function events = history_query(matPath[, fnNames]), flattens FunctionHistory for call-pattern analysis.

Documentation lookup: use the matlab-docs skill when an unfamiliar function appears in results.

Constraints

  • Do NOT optimize or refactor the user's code. The skill profiles and reports; the user decides what to change. Temporary instrumentation (profiler guards, tic/toc, input-capture saves for snapshot-and-replay) is fine, but revert it after the investigation or make the changes in a copy.
  • Do NOT hardcode project-specific function names or fixture paths.
  • Do NOT fabricate profiler options. If unsure whether an option exists, probe with profile('status') first.

Run management

All profile artefacts go in a working directory:

profile_runs/
├── manifest.json
├── last                ← text file containing the most recent run ID
├── Foo_001/
│   ├── profile.json
│   ├── profile.mat
│   └── profile_html/
└── Foo_002/
    └── ...

Manifest entry shape:

{
  "id": "Foo_001",
  "target": "Foo.m",
  "label": "initial time profile",
  "timestamp": "2026-04-16T16:23:00",
  "settings": "-detail builtin -historysize 2e7",
  "concern": "time",
  "wall_time": 12.345,
  "working_tree_diff": "Foo.m | 3 ++-\n1 file changed"
}

Rules:

  1. At invocation start, load manifest.json if it exists and read last to find the baseline for comparison.
  2. Derive a run ID that captures what distinguishes this run from others the user is likely to do. See "Run ID naming" below — avoid generic entry-point names like Kernel or main unless that really is the distinguishing feature. Append a sequence number (_001, _002, ...).
  3. Resolve the sequence number against BOTH the manifest and the filesystem. Take the max of (highest sequence number in the manifest for this slug) and (highest sequence number in profile_runs/<slug>_* directories). Then add 1. This handles cases where the manifest and filesystem drift — user deleted a run directory manually, manifest was lost, or runs were added from elsewhere. Never overwrite an existing directory.
  4. Before creating the run directory, check that the full path profile_runs/<run_id>/ does not exist. If it does (should not happen after rule 3, but defensively), increment the sequence number until you find a free slot.
  5. Derive a short human-readable label from the user's prompt.
  6. Capture git diff --stat at run time — the code is likely uncommitted, so git hash is not a reliable identifier. If a baseline run exists, also capture git diff -- <target_files> to show the actual code changes.
  7. Save artefacts into profile_runs/<run_id>/, update the manifest, update last to the new run ID.
  8. To start fresh without comparison, the user deletes last. To compare against a specific older run, the user edits last or says so explicitly.

Run ID naming

The run ID should identify what makes this run distinct, not just the top-level entry point. If the user repeatedly profiles the same entry point with different inputs, naming by entry point gives Kernel_001, Kernel_002, ... — uninformative. Name by what varies.

Rules of thumb:

  • If the user invokes with a specific function or file target (e.g., /matlab-profile function foo in Bar.m): use the function/file as the slug (foo_001).
  • If the user profiles an entry point with a specific dataset or fixture (e.g., "profile the kernel on EXO.json", "profile Kernel with simulated m3 data"): use the dataset/fixture as the slug (EXO_001, m3_001), not the entry point.
  • If the user describes a mode or scenario ("profile in American mode", "profile with memory tracking"): use the mode/scenario (american_001, memtrack_001).
  • If nothing clearly varies and the user just wants to profile the main entry point: fall back to the entry point name.

When multiple things vary (dataset AND mode), combine: EXO_american_001. Err toward informative over short. If unsure what's distinctive, ask the user briefly before naming: "I'll name this run <id> — does that fit how you'll distinguish it from other runs?"

Phase 1 — Understand the concern and plan the investigation

The user's prompt tells you what to investigate. Arguments are in $ARGUMENTS (the full text after /matlab-profile). Common forms:

  • /matlab-profile function foo_bar in Foo.m — profile a specific function. Read the file, find the function, wrap a call to it under the profiler. Ask for representative inputs if you cannot infer them.
  • /matlab-profile lines 100-200 in Bar.m — profile a specific code region. Extract the lines, wrap them with necessary setup.
  • /matlab-profile Foo.m — profile a script or main function.
  • /matlab-profile svd(rand(500)) — profile a standalone expression.
  • /matlab-profile (no arguments) — ask what to investigate.

If the invocation specifies the target, proceed directly. Ask only when you genuinely need more information.

Identify the concern from the user's prompt and choose profiler settings:

ConcernProfiler settingsAnalysis focus
"slow", "time", "hotspot"-detail builtin, -historysize 2e7TotalTime ranking, per-line timing
"memory", "allocation", "leak"-detail builtin, -historysize 2e7, -memory onPeakMem, AllocatedMemory per function
"parallel", "CPU utilization"Two runs: -timer performance then -timer cpuCompare wall-clock vs CPU time
"call pattern", "why called"-detail builtin, -historysize 2e7, -timestampFunctionHistory entry/exit trace
General "profile this"-detail builtin, -historysize 2e7Broad first pass; follow what stands out

When the concern is unclear, start with a broad time pass. Anomalies in the first pass (wall ≫ CPU, unexpectedly high call counts) suggest what to probe next.

Ask about expected runtime. Confirm before launching anything over 120 seconds.

Handling short workloads

If the target runs in under a second, per-call profiler overhead dominates and relative timing is distorted. Wrap the target in a reps loop so total wall time is at least a few seconds:

for rep = 1:10
    <USER_CODE>;
end

Note the reps count so the user can divide. For sub-millisecond operations, also run an off-profiler tic/toc benchmark as a cross-check. The profiler inflates per-call cost (observed: ~5x for tiny ops) but preserves relative ordering within a single profile.

Phase 2 — Run the profiler

Generate a matlab -batch command that adds paths, configures the profiler, runs the code, and saves artefacts. Call export_profile_json to write the JSON. Template:

matlab -nodisplay -nodesktop -nosplash -batch "
  addpath(genpath('.'));
  addpath('${CLAUDE_SKILL_DIR}/scripts');
  profile clear;
  profile('on', '-detail', 'builtin', '-historysize', 2e7);
  t0 = tic;
  <USER_CODE>;
  wall = toc(t0);
  profile off;
  info = profile('info');
  fprintf('Wall time: %.3fs\n', wall);
  fprintf('FunctionTable rows: %d\n', numel(info.FunctionTable));
  fprintf('FunctionHistory events: %d\n', size(info.FunctionHistory, 2));
  export_profile_json(info, '<RUN_DIR>/profile.json');
  save('<RUN_DIR>/profile.mat', 'info', '-v7.3');
  profsave(info, fullfile(pwd, '<RUN_DIR>', 'profile_html'));
"

Pass profsave an absolute path (via fullfile(pwd, ...), not a relative '<RUN_DIR>/profile_html'). profsave's last action is web(['file:///' fullfile(dirname,'file0.html')],'-browser'); with a relative dirname, that URL parses as /<dirname>/file0.html (absolute from root, doesn't exist) and fails in -batch mode with a misleading "file does not exist" error. Files are written correctly either way, but the absolute-path form avoids the spurious error.

For memory profiling, DO NOT append -memory on to a profile('on', ...) call — that errors with "Only one profiler action is supported per call." Instead, use a configure-only invocation that enables memory, then start with a separate profile on:

profile('-memory', 'on', '-detail', 'builtin', '-historysize', 2e7);
profile on;
% ... user code ...
profile off;

The exporter captures memory fields (PeakMem, TotalMemAllocated, TotalMemFreed) automatically when the struct contains them.

After the run: update the manifest, check FunctionHistory event count against the historysize cap, check for errors or warnings, tell the user the run path.

Phase 3 — Analyze and iterate

Use the analysis scripts against the JSON. Examples:

# Top functions by inclusive time
${CLAUDE_SKILL_DIR}/scripts/rank_functions.py profile_runs/Foo_001/profile.json --top 15

# Per-line breakdown of the hottest function
${CLAUDE_SKILL_DIR}/scripts/line_breakdown.py profile_runs/Foo_001/profile.json

# Per-line breakdown of a specific function
${CLAUDE_SKILL_DIR}/scripts/line_breakdown.py profile_runs/Foo_001/profile.json myHotFunction

# Memory ranking (requires -memory on)
${CLAUDE_SKILL_DIR}/scripts/rank_functions.py profile_runs/Foo_001/profile.json --by memory

# Compare against baseline
${CLAUDE_SKILL_DIR}/scripts/compare_runs.py profile_runs/Foo_001/profile.json \
                                            profile_runs/Foo_002/profile.json

For FunctionHistory call-pattern queries, run history_query.m via MATLAB:

matlab -nodisplay -nodesktop -nosplash -batch "
  addpath('${CLAUDE_SKILL_DIR}/scripts');
  history_query('profile_runs/Foo_001/profile.mat', 'myHotFunction');
"

Patterns to watch for:

  • Functions with Type "Built-in" high in the ranking → time in LAPACK or runtime
  • High NumCalls but low TotalTime → dispatch overhead, not algorithmic cost
  • One or two lines dominating → pinpoint optimization targets
  • ExecutedLines sum much less than TotalTime → hidden built-in cost (use -detail builtin)
  • Large AllocatedMemory with small FreedMemory → potential accumulation or leak
  • Wall time much larger than CPU time → I/O bound or waiting on external resources
  • CPU time much larger than wall time → good BLAS parallelism

When to look up a function

When an unfamiliar MATLAB function appears in the hotspot list, use the matlab-docs skill to fetch its documentation. Use the docs to understand what the function does, whether a better alternative is recommended for the observed pattern (e.g., griddedInterpolant instead of repeated interp1 calls), and what options it supports.

Benchmarking alternatives

When the hotspot is a specific MATLAB pattern, don't stop at reporting the bottleneck — write a short off-profiler benchmark comparing the current approach against plausible alternatives. Save as bench_<description>.m. Verify outputs match before trusting speedup numbers. This turns a report into an actionable recommendation.

Template:

N = 10;  % reps
t = tic; for i=1:N, <CURRENT>; end; tA = toc(t) / N;
t = tic; for i=1:N, <ALT1>; end; tB = toc(t) / N;
t = tic; for i=1:N, <ALT2>; end; tC = toc(t) / N;
fprintf('A: %.3f ms, B: %.3f ms (%.1fx), C: %.3f ms (%.1fx)\n', ...
    tA*1e3, tB*1e3, tA/tB, tC*1e3, tA/tC);

Comparing with the baseline

If last exists, compare automatically with compare_runs.py. Report:

  • Overall wall time delta
  • Functions that improved, regressed, appeared, or disappeared
  • What code changed between runs (git diff stat from the manifest)
  • Prominent flag if anything regressed

Autonomous iteration

After each pass, examine the results for signals that demand a follow-up pass. When a signal fires, run the next pass automatically (within the check-in constraints below) — do not stop and ask. The goal is a complete picture by the time you report, not a ping-pong of "do you want me to look at X?" questions.

Signals that trigger an automatic follow-up pass:

  • History buffer filled → rerun with larger -historysize (same workload, bigger buffer).
  • ExecutedLines sum significantly less than TotalTime and -detail builtin wasn't used → rerun with -detail builtin (same workload, more detail).
  • Wall time much larger than CPU time, or CPU ≫ wall → run a parallelism pass (-timer cpu) to quantify what's happening.
  • Time profile shows a hot function with many allocation-looking built-ins in its ExecutedLines (zeros, ones, repmat, cat, sparse in a loop; or high-call-count operations on growing arrays) → run a memory pass with -memory on to confirm and quantify.
  • One function dominates (>70% of total time) and its internal line attribution doesn't explain where the time goes → snapshot-and-replay that function in isolation for a focused profile.
  • User asked about memory but only time was run → run a memory pass.
  • User asked about parallelism but only wall time was measured → run a CPU-time pass.
  • Memory pass shows large allocations in a function that's also a time hotspot → you already have the cross-reference; just report it.

Check in with the user before proceeding automatically when:

  • The next run will take more than 120 seconds.
  • You've already run 2+ follow-up passes without the picture converging (means the evidence is ambiguous; the user should choose).
  • Multiple signals fire at once and none dominates — picking which to chase first is a judgment call.

Report the chained passes in one consolidated final report, not piecewise after each one. The user wanted an answer, not a blow-by-blow.

Deciding between autonomous follow-up and asking

After an analysis pass surfaces multiple plausible next steps, decide whether to pick one autonomously or ask the user first.

Proceed autonomously when one direction clearly dominates:

  • The first pass already answers the user's question and the follow-ups are nice-to-have polish
  • One signal in the data points unambiguously at a single next pass (e.g., history buffer overflowed — just rerun with a larger buffer; ExecutedLines sum doesn't match TotalTime — rerun with -detail builtin)
  • The user's prompt was specific enough that the follow-up direction is obvious (they asked about memory and you only ran a time pass — go run the memory pass)

Ask the user when multiple directions are roughly equally promising and none is implied by the prompt:

  • Three or more possible follow-up passes each addressing a different dimension (time drill-down, memory, parallelism, call-pattern)
  • The data shows multiple signals (e.g., both a time hotspot AND unexplained wall/CPU gap — either could be worth investigating first)
  • A non-trivial scoping choice (which subfunction to snapshot for replay, which fixture to rerun with)

How to ask: present the options as a numbered list in the report's "Next steps" section, with a short description of what each would reveal and roughly how long it would take. Then stop and wait for the user to pick one (or say "none" / "all" / something else). Do not invent a decision just to keep moving.

If the user asked "why is this slow?", don't just report time hotspots — check whether memory pressure, poor parallelism, or excessive call counts are contributing. Follow the evidence.

Phase 4 — Report

  1. Investigation summary: what was profiled, passes run, settings, wall time, buffer status, run path.
  2. Findings table adapted to the measurement (time, memory, or comparison).
  3. Before/after comparison prominently shown if a baseline existed.
  4. Per-line detail for hotspots. Read the source at those lines and explain what the code is doing, not just report line numbers.
  5. Surprises: anything the first pass didn't predict (built-in dominance, unexpected call counts, wall/CPU mismatch, history overflow, regressions in untouched functions).
  6. Diagnosis: connect findings to the user's concern. Synthesize across passes.
  7. Actionable next steps. Be specific about what to investigate or change. Include alternatives benchmarks when you ran them. Do not make code changes.
  8. Run path for interactive exploration: "Open profile_runs/<run_id>/profile_html/index.html in a browser."

Cleanup

The profile_runs/ directory accumulates across runs. Mention its total size periodically. When the user is done, offer to clean up, but don't delete without asking.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.