Kuikly Recomposition Analyzer
Three-stage funnel analysis for KuiklyUI Compose DSL recomposition performance issues: Report Screening → Frames Deep Dive → Source Code Verification.
Threshold Configuration
Read
to get default thresholds. Users can override them by specifying parameters in requests (e.g.,
).
Workflow
Phase 0 — Obtain Logs
Read
to understand log field formats. Retrieve
and
in the following priority order:
- User provides direct path → Read via Read tool
- Check conventional paths in current directory: ,
- Automatically pull from device → Execute platform-specific commands as described in
references/log-retrieval.md
- If all fail → Output guidance:
Profiler logs not found. Please collect data first:
- Call
RecompositionProfiler.start()
in code to start recording, then call after operations are completed
- Or click "Start" in the Profiler Overlay panel to record, click "Stop" after operations, then click "Get Report"
- After collection, tell me the file path, or provide the App package name and I'll pull it for you
Phase 1 — Data Health Check
totalFrames < minFramesThreshold
(default 30) → Issue warning and ask whether to continue
- → Prompt that no recomposition records exist
- is not empty → Declare excluded components in the report
Phase 2 — Report Screening
Prerequisite (must execute first): Sort all non-noScope components in descending order by
, list TOP 20.
Any component with must be included in the report, regardless of total duration. This step prevents high-frequency but low-duration components from being missed when sorted by total duration later.
Read
references/detection-rules.md
and iterate through
:
| Condition | Handling |
|---|
noScopeRecompositions == recompositionCount
| Classify into Normal Recomposition List, skip subsequent analysis |
maxDurationMs > singleRecompDurationThreshold
(default 10ms) | Must be output to report regardless of recomposition count, proceed to Phase 3 for deep dive |
| Count of a key in > | Mark as suspicious, proceed to Phase 3 |
paramChangeFrequency["#N"] / recompositionCount > paramChangeRateThreshold
| Mark as RULE-C suspicion (need to combine source code to judge parameter type) |
triggerStates[i].readers.length > stateReadersThreshold
| Mark as RULE-B suspicion |
Phase 3 — Frames Deep Dive + Source Code Verification
Note:
is in JSONL format (each line is an independent JSON object), not a single JSON file. Must read and parse line by line, not parse the entire file as JSON. Use the Read tool to read and process line by line.
If the analysis target is an item component inside LazyList/LazyGrid/Pager, read
references/lazylist-rules.md
to understand the difference between item closure reconstruction and business component skip.
Read
line by line, and route by the
field (
/
/
).
Frame-level Check:
For each frame with excessive duration, process as follows:
-
First judge if it's normal:
- Cross-reference with : If the frame follows a scroll event and most events in the frame are (first-time composition) → Classify as normal rendering overhead, briefly explain the reason in the report, and skip subsequent analysis
- If there are many events in the frame but most are noScope → Same as above, belongs to normal batch first-time composition when list slides in
-
After confirming it's a real issue, perform root cause analysis within the frame:
- Find the composable events with the longest duration in the frame (top few with largest )
- Check if these components are triggered cascadingly by the same State (same )
- Perform complete chain reasoning (Step 1-5, same as suspicious item process) for the component with the highest duration
-
Each real issue frame in the report must include:
- Frame duration + number of events in the frame
- Judgment conclusion (normal / problematic) and reasons
- If problematic: Names, durations, and triggering States of the top 1-3 components with highest duration
- Root cause analysis (refer to chain reasoning Step 2-4)
- Optimization suggestions (give specific directions if possible, explain what information needs to be supplemented if unable to judge)
- Single
composable_recomposed.durationMs > durationThreshold
→ Enter chain reasoning
- Multiple components in the same frame are triggered by the same State → Cascading suspicion, analyze the writing timing of this State
Context-assisted Judgment (when touch/scroll is available):
- A scope recomposes >3 times between touchBegin~touchEnd → Mark as "One click triggers N recompositions, suspected to be optimizable"
- scroll_context index changes + item recompositions ≈ number of slid-in items → Classify as normal
- scroll_context index unchanged + item recompositions → Mark as "Recomposition not caused by scrolling, needs analysis"
Source Code Verification (only for confirmed suspicious items):
For each suspicious item, perform in-depth analysis following the chain reasoning steps (cannot skip):
Step 1 — Locate Code
Take
(format
), use
to locate the file, and read the function declaration and 30 lines of surrounding code.
Step 2 — Understand Data Signals
Answer: Which scope is repeatedly triggered according to the component's
? Which State is driving it according to
? Which parameter changes every time in
? Write out the specific values (e.g., "scope=223833166 was triggered 61 times, average duration 0.75ms").
If
shows a parameter changes frequently,
must first judge the nature of the change:
- Business data actually changes (e.g., coordinates differ per frame during scrolling, list content updates during page turning) → Root cause is writing logic, not type stability issue
- Data content remains the same but reference changes (new instance passed each time, values are the same but is not equal) → This is a type stability or object creation issue
The root causes of the two are completely different and cannot be confused.
Step 3 — Trace Root Cause
Combine with code, answer: Who is writing this State? When is it written? Why is it triggered every recomposition? Find the real "writer" (not "reader"). If the State is written in side effects like
/
/
, specify the triggering timing.
For CompositionLocal subtree recomposition, additionally answer: Is the value passed to
a new instance or a cached instance?
uses
reference comparison; even if the content is the same, passing a new instance each time will trigger recomposition of the entire subtree. The root cause may be "copy()/new object created every recomposition", not necessarily type instability.
General Process for Judging Root Cause of Parameter Changes (applicable to any high-frequency paramChangeFrequency situation):
- Read source code to find the caller side of the parameter — Who is passing this parameter?
- Is a new object (, , lambda) passed, or a stable reference (singleton, cached)?
- If it's a new object: Check if it's necessary to create a new one every time, or if it can be cached with
- If it's a stable reference but still judged as changed: Consider type stability (whether there is , , cross-module type)
RULE-C Special: Additional Execution When RULE-C is Hit
Read
references/stability-rules.md
to understand complete stability judgment rules, then:
- Read source code, map to specific parameter names and types in declaration order
- First judge the nature of the change: Does the parameter value actually differ each time (business data changes)? Or is the value the same but a new instance is passed each time (reference not equal)? The former is not a stability issue, the latter requires consideration of type stability
- / annotations override compiler inference: For classes with annotations, the compiler trusts their stability and will not judge them as unstable due to /. If a parameter with still changes 100% of the time, the real reason is "new instance passed each time" rather than type inference issue
- Note the bug of + var direct assignment: Skip will occur, but the interface will not update (display outdated data), which is more dangerous than "not skipping"
- If confirmed as "same value repeatedly creating new instances", select a solution according to the type, see
references/optimization-patterns.md
for details
- When Strong Skipping is enabled, it is forbidden to suggest wrapping lambda with handwritten — Handwriting is redundant. If lambda parameters still change frequently, the problem lies in the stability of variables captured by lambda
Step 4 — Evaluate Impact Scope
Answer: How many components subscribe to this State (readers)? Do all these components really need to recompose every time the State changes? Which can be skipped? Which must respond?
Step 5 — Propose Solutions and Explain Trade-offs
Read
references/optimization-patterns.md
to obtain optimization solutions corresponding to each rule. Provide 1-2 specific optimization solutions, each must:
- Provide code comparison before/after modification
- Explain why this modification solves the problem (from the perspective of Compose runtime mechanism)
- Explain possible side effects or precautions
- If there are multiple solutions, explain which is recommended and under what scenarios to choose the other
Before recommending adding / annotations, must pass the following two checks; do not recommend if either is not met:
- Do the properties of the class meet the promise of the annotation ( = never changes after construction; = changes are only notified via MutableState)? If it contains direct assignment, skip will still occur (annotation makes the compiler trust it), but the interface will not update (Compose doesn't know the value changed), which will cause interface bugs
- Will the caller reuse instances or pass the same reference? If // is used to create new objects every time, annotations cannot make skip happen
If the two checks fail, do not recommend adding annotations, but provide solutions from the perspective of how the caller passes parameters or how the data model is designed.
If encountering situations where analysis is limited (unable to locate source code, parameter index cannot be mapped, etc.), read
references/known-limitations.md
to confirm if it belongs to known limitations, and handle according to the limitation description.
Phase 4 — Output Report
Read
references/report-template.md
to generate:
- Conversation Summary: Data overview + TOP 3 issues
- Markdown Report:
recomp-analysis-YYYYMMDD-HHmm.md
, including data overview, normal recomposition list, issue diagnosis (sorted in descending order of severity), and filter configuration declaration. Severity rating and sorting rules are in references/detection-rules.md
: Total duration (recomposition count × average single duration) is the first sorting dimension; issues with high count but extremely low single duration are ranked after issues with real high duration.
Report Writing Specifications (must comply):
- Frame statistics in data overview: Only write the number of frames, not the proportion. For example, "Slow frames: 14 frames", not "14 frames (7.4%)".
- Prohibit using internal terms in issue descriptions: Do not use terms like , , , in issue descriptions. Use language understandable to users, e.g., "This component is re-rendered every time scrolling occurs" instead of "Hit RULE-B". Rule identifiers are only allowed in the Filter Configuration Declaration section.
- Context description must distinguish trigger sources: When describing recomposition counts, must clearly state whether it is "One click triggers N recompositions" or "M recompositions triggered cumulatively during N frames of scrolling"; the two cannot be mixed. If it's cumulative across multiple frames, explain "During X frames of scrolling, this component recomposed N times in total".
- Each issue must contain two key data: ① Number of recompositions triggered by the same scope (or total recomposition count); ② Average single duration (avgDurationMs). Mark "Insufficient data, unable to evaluate severity" if either data is missing.
- Issue analysis must be in-depth: Root cause analysis must explain "Who is writing this State, when it is written, why it is triggered frequently", not just "Recomposition caused by State change". Optimization suggestions must provide code comparison before/after modification and explain why the modification works, not just give conclusions.
- Directly inform when cause is unknown and provide troubleshooting guidance: If the root cause of a problem (e.g., abnormal single duration) cannot be located with existing logs and source code, do not guess or give vague conclusions. Directly write: "Current logs are insufficient to determine the root cause, further troubleshooting is recommended", and provide specific troubleshooting suggestions, e.g.:
- Add duration tracking () inside the component function to locate which sub-operation is slow
- Or directly say "You can tell me, and I'll help you conduct a more in-depth analysis"
- Components with high recomposition count but low total duration cannot be omitted: All components that hit detection rules must be included in the report, cannot be skipped due to low total duration. For components with significantly high recomposition count (e.g., >50 times) but extremely low single duration (<0.5ms):
- Still include in the report, mark severity as "Low"
- Explain the recomposition count and average duration
- If no in-depth analysis is done, clearly note "Single duration is extremely low, no in-depth analysis yet, but recomposition count is high, recommended to pay attention"
- For those with extremely high count (e.g., >100 times), even if duration is low, brief root cause analysis should be done (at least explain which State is driving it and whether recomposition count can be reduced)
- Respect existing / annotations, do not question their accuracy:
- Class is marked with or → Compiler trusts it is stable, do not say "marking is inaccurate" "marking is invalid"
- Class contains // properties but is marked with → Annotation overrides compiler inference, this is intentional design by developers, not an error
- When Strong Skipping is enabled, class contains lambda property → lambda is automatically , reference is stable, do not say "lambda reference stability depends on caller"
- If parameters of an annotated class still change frequently, the problem is caller passes new instance every time, not the annotation. Analysis direction is how the caller passes parameters, not questioning the annotation
- When suggesting using to cache objects, must analyze dependencies:
- Prohibit directly suggesting without key, unless it is confirmed that object creation does not depend on any external state
- If the factory function may read CompositionLocal (e.g., theme color, font size, dark mode) → must have the correct key (e.g.,
remember(isDarkTheme) { markdownColor() }
), otherwise configuration will not update after theme switch, causing interface bugs
- If unable to confirm internal dependencies of the factory function (no source code read) → Do not recommend , instead suggest "Check whether the function depends on external states like theme before deciding whether to cache"
- Wrong example:
val colors = remember { markdownColor() }
— If reads dark mode, color will not update after theme switch
- Correct example:
val isDark = isAppInDarkTheme(); val colors = remember(isDark) { markdownColor() }
Form C: Focus on Specific Page
When user says "I only want to see page XX":
Please click the "Reset" button in the profiler panel, enter the target page and perform operations once, then let me analyze.
References
- — Default configurable threshold values
- — Field descriptions for report.json / frames.jsonl
references/log-retrieval.md
— Pull commands for various platforms (adb/xcrun/hdc)
references/detection-rules.md
— Detailed logic of detection rules
references/lazylist-rules.md
— Recomposition analysis rules for LazyList items (closure reconstruction vs business component skip, avoiding wrong conclusions)
references/stability-rules.md
— Compose stability rules (verified by actual tests): compiler inference rules, skip conditions, valid/dangerous scenarios of annotations, performance differences in Profiler
references/optimization-patterns.md
— Optimization solutions and code samples corresponding to each rule
references/known-limitations.md
— Known limitations (paramChanges index has no parameter name, unstable type scope reconstruction, etc.)
references/report-template.md
— Markdown report template