From dba38127375cdfea9bf833bba96d8a2101bc579d Mon Sep 17 00:00:00 2001 From: CyrilFerlicot Date: Sat, 29 Aug 2026 16:49:28 +0200 Subject: [PATCH] Add skill about profiling --- AGENTS.md | 4 +- docs/dev/developing-mcp.md | 3 +- templates/AGENTS.md | 2 + .../skills/pharo-code-profiling/SKILL.md | 84 +++++++++++++++++++ .../pharo-code-profiling/agents/openai.yaml | 4 + 5 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 templates/skills/pharo-code-profiling/SKILL.md create mode 100644 templates/skills/pharo-code-profiling/agents/openai.yaml diff --git a/AGENTS.md b/AGENTS.md index c6392edf..06c86c01 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,8 +28,8 @@ Apply every matching route, reading user guidance before developer guidance. - Before verifying MCP changes: read `docs/dev/mcp-verification.md`. - Before image-to-Git handoff: use `templates/skills/pharo-image-git-handoff/SKILL.md`. -- For project loading, compatibility, or CI reproduction, use the matching - skill under `templates/skills/`. +- For project loading, compatibility, CI reproduction, or code profiling, use + the matching skill under `templates/skills/`. ## Project Facts diff --git a/docs/dev/developing-mcp.md b/docs/dev/developing-mcp.md index c51b5abb..304b2c25 100644 --- a/docs/dev/developing-mcp.md +++ b/docs/dev/developing-mcp.md @@ -94,7 +94,8 @@ workflow-oriented: - project loading; - supported-version compatibility; - image-to-Git handoff; -- CI/smalltalkCI reproduction. +- CI/smalltalkCI reproduction; +- code profiling and performance optimization. A generic Pharo clean-code skill can be useful if shared across repositories, but for MCP development the coding rules and style heuristics should remain in diff --git a/templates/AGENTS.md b/templates/AGENTS.md index 2147a245..3db3aa6c 100644 --- a/templates/AGENTS.md +++ b/templates/AGENTS.md @@ -25,6 +25,8 @@ guessing from source. `skills/pharo-image-git-handoff/SKILL.md`. - Before reproducing smalltalkCI or GitHub failures: use `skills/pharo-ci-repro/SKILL.md`. +- Before profiling or optimizing Pharo code: use + `skills/pharo-code-profiling/SKILL.md`. - Before recovering a stopped standalone image: use `skills/pharo-mcp-recovery/SKILL.md`. - Before debugger sessions or repairs: read the MCP diff --git a/templates/skills/pharo-code-profiling/SKILL.md b/templates/skills/pharo-code-profiling/SKILL.md new file mode 100644 index 00000000..02f236a5 --- /dev/null +++ b/templates/skills/pharo-code-profiling/SKILL.md @@ -0,0 +1,84 @@ +--- +name: pharo-code-profiling +description: Profile running Pharo code to find where time is spent and drive performance optimization. Use when diagnosing slow methods, comparing before/after timings, or when a single `#timeToRun` measurement would be too noisy or too coarse to act on. +--- + +# Pharo Code Profiling + +## Model + +A sampling profiler periodically snapshots the running process, so it answers +"not how long" and "where the time went": call distribution, leaf hotspots, +own vs. child time, GC, and process activity. A single `#timeToRun` sample only +returns one noisy wall-clock number (JIT warm-up, GC, and scheduling skew it) +and gives no reason *why* the time went there. + +Two profilers ship in the `Tool-Profilers` package of every supported Pharo: + +- `AndreasSystemProfiler` — VM-supported sampling that answers a textual report + (`report` returns a `String`). Preferred for agent-driven optimization. +- `TimeProfiler` — a graphical tree browser and a front end for `MessageTally`. + Useful only when a human will browse the profile interactively. + +`MessageTally` is the sampling engine underneath `TimeProfiler`; use it directly +only when you need its process-aware variants (`spyAllOn:`, `tallySendsTo:`). + +## Workflow + +1. Choose a representative, CPU-bound workload and run it enough times to last + a few seconds. Do a warm-up run before measuring so the JIT is hot. +2. Profile and read the report as text: + +```smalltalk +report := (AndreasSystemProfiler new spyOn: [ workload ]; report). +``` + + The cascade `; report` is required: `spyOn:` returns the block's value, not + the profiler. Do not use `AndreasSystemProfiler spyOn:`; it routes through + `doReport`, which opens a text editor window instead of returning text. +3. If the report is too large, re-render it with a higher cutoff percentage + instead of dumping the whole tree: + +```smalltalk +reportShort := String streamContents: [ :s | profiler report: s cutoff: 2 ]. +``` + +4. Read the tree top-down: separate each method's own time from the time spent + in its children, and check the GC and process stats at the end. A hotspot in + a leaf is actionable; a large own-time caller is where micro-optimization + usually starts. +5. For interactive human browsing only, open the graphical profiler: + +```smalltalk +TimeProfiler spyOn: [ workload ]. +``` + +6. Make the narrowest change, re-profile the same workload with the same cutoff, + and verify behavior with focused SUnit tests. + +## Token Efficiency + +- Profile a workload that runs for seconds, not milliseconds, to collect enough + samples. A block that ends too soon leaves the sampler catching unrelated + processes. +- Warm up before profiling; compare shapes, not exact numbers, between runs. +- Prefer `report:cutoff:` over printing the full `report` string. +- Summarize the report yourself: top entries with percentages, not the raw tree. + Write the report to a file only when a full copy is needed. + +## Stop Before + +- Do not leave profiler windows open or call `doReport` on a headless image. +- Sampling covers the whole image, including the MCP server process serving the + tool call and other background processes. Verify the report actually shows + your workload's methods; otherwise lengthen or CPU-bind the block. +- Profiling forks a background process and mutates image state; run it inside a + safe image boundary, and remember that a successful `image_evaluate` can save + the image. +- Sampling approximates execution. Primitives and machine-code paths are charged + to their callers, so trust the ranking and proportions, not exact numbers. + +## Report + +Report the workload, profiler used, cutoff, top entries with own vs. child +percentages, the change made, the before/after comparison, and the tests run. \ No newline at end of file diff --git a/templates/skills/pharo-code-profiling/agents/openai.yaml b/templates/skills/pharo-code-profiling/agents/openai.yaml new file mode 100644 index 00000000..50e85c5f --- /dev/null +++ b/templates/skills/pharo-code-profiling/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Pharo Code Profiling" + short_description: "Find Pharo performance hotspots" + default_prompt: "Use $pharo-code-profiling to profile this Pharo code and find where the time is spent before optimizing." \ No newline at end of file