Optimize debugger polling to avoid unnecessary computations#372
Conversation
- Move variable extraction and index building to useEffect initialization instead of running on every poll cycle - Build complete variableInfoMap once during debugger session start, including all program variables and function block outputs - Simplify pollVariables function to only check which variables currently need querying based on debug flags and UI visibility - Remove duplicate function block lookup logic from poll cycle - now only checks which variables to include from pre-built map - This significantly reduces CPU usage during debugging by avoiding repeated traversal of POUs, function block definitions, and variable lookups every 200ms Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
Important Review skippedBot user detected. To trigger a single review, invoke the You can disable this status message by setting the Comment |
There was a problem hiding this comment.
Pull Request Overview
This PR optimizes the debugger polling routine by moving expensive computations from the 200ms polling loop to initialization time, reducing CPU usage during debugging sessions.
- Moved function block output variable lookup from polling loop to initialization phase
- Simplified polling logic to filter pre-built variable map instead of rebuilding lookups
- Merged duplicate ladder diagram logic blocks
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| functionBlockInstances.forEach((fbInstance) => { | ||
| const fbTypeName = fbInstance.type.value.toUpperCase() | ||
|
|
||
| let fbVariables: | ||
| | Array<{ name: string; class: string; type: { definition: string; value: string } }> | ||
| | undefined | ||
|
|
||
| const standardFB = StandardFunctionBlocks.pous.find( | ||
| (fb: { name: string }) => fb.name.toUpperCase() === fbTypeName, | ||
| ) | ||
| if (standardFB) { | ||
| fbVariables = standardFB.variables | ||
| } else { | ||
| const customFB = currentProject.data.pous.find( | ||
| (pou) => pou.type === 'function-block' && pou.data.name.toUpperCase() === fbTypeName, | ||
| ) | ||
| if (customFB && customFB.type === 'function-block') { | ||
| fbVariables = customFB.data.variables as Array<{ | ||
| name: string | ||
| class: string | ||
| type: { definition: string; value: string } | ||
| }> | ||
| Array.from(variableInfoMapRef.current!.entries()).forEach(([_, varInfo]) => { | ||
| if ( | ||
| varInfo.pouName === programInstance.name && | ||
| varInfo.variable.name.startsWith(`${fbInstance.name}.`) | ||
| ) { | ||
| const compositeKey = `${varInfo.pouName}:${varInfo.variable.name}` | ||
| debugVariableKeys.add(compositeKey) | ||
| } | ||
| } | ||
|
|
||
| if (fbVariables) { | ||
| const boolOutputs = fbVariables.filter( | ||
| (v) => | ||
| (v.class === 'output' || v.class === 'inOut') && | ||
| v.type.definition === 'base-type' && | ||
| v.type.value.toUpperCase() === 'BOOL', | ||
| ) | ||
|
|
||
| boolOutputs.forEach((outputVar) => { | ||
| const debugPath = `RES0__${programInstance.name.toUpperCase()}.${fbInstance.name.toUpperCase()}.${outputVar.name.toUpperCase()}` | ||
| const index = debugVariableIndexes.get(debugPath) | ||
|
|
||
| if (index !== undefined) { | ||
| const blockVarName = `${fbInstance.name}.${outputVar.name}` | ||
| const compositeKey = `${programInstance.name}:${blockVarName}` | ||
| debugVariableKeys.add(compositeKey) | ||
|
|
||
| if (!variableInfoMapRef.current?.has(index)) { | ||
| variableInfoMapRef.current?.set(index, { | ||
| pouName: programInstance.name, | ||
| variable: { | ||
| name: blockVarName, | ||
| type: { definition: 'base-type', value: 'bool' }, | ||
| class: 'local', | ||
| location: '', | ||
| documentation: '', | ||
| debug: false, | ||
| }, | ||
| }) | ||
| } | ||
| } | ||
| }) | ||
| } | ||
| }) | ||
| }) |
There was a problem hiding this comment.
The nested loop creates O(n×m) complexity where the outer loop iterates over function block instances and the inner loop iterates over all variable entries. Consider building a Map keyed by POU name and function block instance name during initialization to avoid this nested iteration during polling.
Pull request info
References
Link to Devin run: https://app.devin.ai/sessions/60040dc5fcd84bd094c3394df41ae101
Requested by: Thiago Alves (@thiagoralves)
Description of the changes proposed
This PR optimizes the debugger polling routine to eliminate unnecessary computations that were being performed every 200ms during debugging sessions.
Before: The
pollVariablesfunction was extracting variable names, building indexes, looking up function block definitions, and filtering outputs on every poll cycle (every 200ms).After: All expensive computations are now performed once during debugger session initialization in the
useEffecthook:variableInfoMapfor all program variables and function block outputsdebug=trueflags on variablesKey changes:
pollVariablesto use pre-built map withstartsWithfiltering instead of rebuilding function block lookups (lines 305-319)if (currentPou && currentPou.data.body.language === 'ld')blocks into oneExpected impact: Significant reduction in CPU usage during debugging sessions by avoiding repeated POU traversal, function block definition lookups, and variable filtering.
Important items for review
startsWithmatching correctly identifies function block variables (format:${fbInstance.name}.${outputVar.name})DOD checklist