Skip to content

⚡️ Speed up function getDynamicBindings by 40%#25

Open
codeflash-ai[bot] wants to merge 1 commit intoreleasefrom
codeflash/optimize-getDynamicBindings-ml226lbl
Open

⚡️ Speed up function getDynamicBindings by 40%#25
codeflash-ai[bot] wants to merge 1 commit intoreleasefrom
codeflash/optimize-getDynamicBindings-ml226lbl

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Jan 31, 2026

📄 40% (0.40x) speedup for getDynamicBindings in app/client/src/utils/DynamicBindingUtils.ts

⏱️ Runtime : 50.1 microseconds 35.9 microseconds (best of 10 runs)

📝 Explanation and details

The optimization achieves a 39% runtime improvement (from 50.1μs to 35.9μs) by eliminating expensive regex operations in the hot path.

What Changed:
The critical optimization replaces the isDynamicValue(segment) regex test with a fast character code check that directly inspects the first two and last two characters of each segment.

Why It's Faster:
The line profiler reveals that isDynamicValue(segment) at line 110 consumed 70.7% of execution time in the original code. This function uses DATA_BIND_REGEX.test(), which involves regex engine overhead for pattern matching. The optimized version replaces this with:

  • Direct character code comparisons using charCodeAt() (checking for { = 123 and } = 125)
  • Simple length check (>= 4)
  • No regex compilation, pattern matching, or backtracking overhead

Character code comparisons are extremely fast primitive operations in JavaScript, while regex tests require the engine to parse and execute pattern matching logic on every call.

Test Case Performance:
The optimization shows particularly strong gains in:

  • Non-string inputs: 87.2% faster (8.17μs → 4.36μs) - early return path improved
  • Empty bindings: 98.1% faster (2.44μs → 1.23μs) - fewer regex operations on simple cases
  • Basic bindings: 149% faster (14.0μs → 5.63μs) - the common case benefits most from avoiding regex
  • JS action entities: 21.8% faster (4.43μs → 3.64μs) - reduced overhead in special case handling

Complex cases with nested braces show minor regressions (6-19% slower) due to the additional character code checks, but these are uncommon edge cases that don't offset the significant gains in typical usage patterns. The overall 39% speedup demonstrates that the optimization excels at the most frequent patterns encountered in dynamic binding parsing.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 11 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 72.7%
🌀 Click to see Generated Regression Tests
// @ts-nocheck
// imports
// Mock external dependency used inside DynamicBindingUtils to control behavior of isJSAction
jest.mock('ee/workers/Evaluation/evaluationUtils', () => ({
  // We'll treat any entity with a truthy `__isJSAction` property as a JS action.
  isJSAction: jest.fn((entity) => !!(entity && entity.__isJSAction)),
  // Other exports are present as no-op placeholders to satisfy the module's imports.
  isAction: jest.fn(),
  getEntityNameAndPropertyPath: jest.fn(),
  isTrueObject: jest.fn(),
  isWidget: jest.fn(),
}));

import { getDynamicBindings } from '../src/utils/DynamicBindingUtils';

// unit tests
describe('getDynamicBindings', () => {
    // Basic Test Cases
    describe('Basic functionality', () => {
        test('should handle normal input with a single binding and surrounding text', () => {
            // Input with a binding in the middle of text
            const input = 'Hello {{user.name}}!';
            const { stringSegments, jsSnippets } = getDynamicBindings(input);

            // Expect segments to separate plain text and the binding
            expect(stringSegments).toEqual(['Hello ', '{{user.name}}', '!']);  // 14.0μs -> 5.63μs (149% faster)
            // Expect JS snippets to extract the inner expression of the binding
            expect(jsSnippets).toEqual(['', 'user.name', '']);
        });

        test('should handle multiple bindings and separators', () => {
            // Multiple bindings with text between them
            const input = 'Sum: {{a}} + {{b}} = {{a + b}}';
            const { stringSegments, jsSnippets } = getDynamicBindings(input);

            expect(stringSegments).toEqual([  // 6.89μs -> 6.95μs (0.892% slower)
                'Sum: ',
                '{{a}}',
                ' + ',
                '{{b}}',
                ' = ',
                '{{a + b}}',
            ]);

            expect(jsSnippets).toEqual([
                '',
                'a',
                '',
                'b',
                '',
                'a + b',
            ]);
        });

        test('should trim whitespace and, when entity is JS action, return sanitized string as both segments and snippets', () => {
            // Provide an entity that the mocked isJSAction will treat as true
            const entity = { __isJSAction: true };
            const input = '   {{foo.bar}}  ';
            const { stringSegments, jsSnippets } = getDynamicBindings(input, entity);

            // For JS actions, the implementation returns the sanitized (trimmed) string
            expect(stringSegments).toEqual(['{{foo.bar}}']);  // 4.43μs -> 3.64μs (21.8% faster)
            expect(jsSnippets).toEqual(['{{foo.bar}}']);
        });
    });

    // Edge Test Cases
    describe('Edge cases', () => {
        test('should return empty arrays for non-string inputs', () => {
            // Non-string inputs should be protected against
            expect(getDynamicBindings(null)).toEqual({ stringSegments: [], jsSnippets: [] });  // 8.17μs -> 4.36μs (87.2% faster)
            expect(getDynamicBindings(undefined)).toEqual({ stringSegments: [], jsSnippets: [] });
            expect(getDynamicBindings(123)).toEqual({ stringSegments: [], jsSnippets: [] });
            expect(getDynamicBindings({})).toEqual({ stringSegments: [], jsSnippets: [] });
        });

        test('should handle strings without any bindings', () => {
            const input = 'plain text without bindings';
            const { stringSegments, jsSnippets } = getDynamicBindings(input);

            // When there are no bindings, the whole string should be returned as a single segment
            expect(stringSegments).toEqual([input]);  // 2.44μs -> 1.23μs (98.1% faster)
            // And there should be a corresponding empty snippet (no expression extracted)
            expect(jsSnippets).toEqual(['']);
        });

        test('should handle unbalanced/malformed bindings by returning original segment', () => {
            // Unclosed binding - sum !== 0 branch should return the original string as a single segment
            const input = 'This has an {{unclosed binding';
            const { stringSegments, jsSnippets } = getDynamicBindings(input);

            expect(stringSegments).toEqual([input]);  // 6.21μs -> 6.71μs (7.41% slower)
            // Because it is malformed, no valid expression is extracted
            expect(jsSnippets).toEqual(['']);
        });

        test('should handle nested/extra braces gracefully', () => {
            // Extra braces around a binding - should still produce segments, and inner substring logic will apply
            const input = '{{{wrapped}}}';
            const { stringSegments, jsSnippets } = getDynamicBindings(input);

            // Entire thing is treated as a single segment
            expect(stringSegments).toEqual(['{{{wrapped}}}']);  // 4.97μs -> 6.16μs (19.3% slower)
            // The inner snippet will be substring(2, length - 2) => '{wrapped}'
            expect(jsSnippets).toEqual(['{wrapped}']);
        });

        test('should handle empty string input', () => {
            const input = '';
            const { stringSegments, jsSnippets } = getDynamicBindings(input);

            // Empty string is returned as a single empty segment
            expect(stringSegments).toEqual(['']);  // 2.96μs -> 1.19μs (149% faster)
            // And no JS snippet is extracted
            expect(jsSnippets).toEqual(['']);
        });
    });

    // Large Scale Test Cases
    describe('Performance tests', () => {
        test('should handle large inputs efficiently (many alternating bindings and text) without timing out or being incorrect', () => {
            // Build a moderately large string with alternating text and bindings.
            // Keep it under the user's limit (under 1000 elements); let's create 200 bindings.
            const count = 200;
            const parts = [];
            for (let i = 0; i < count; i++) {
                parts.push(`Item${i}: `);
                parts.push(`{{items[${i}]}}`);
                parts.push(' | ');
            }
            const largeString = parts.join('');
            const { stringSegments, jsSnippets } = getDynamicBindings(largeString);

            // The expected pattern is: [text, binding, separator, text, binding, separator, ...]
            // So number of segments equals parts.length because getDynamicStringSegments will keep each literal and binding segment.
            expect(stringSegments.length).toBeGreaterThanOrEqual(parts.length - 1);
            // Ensure that the number of actual binding snippets equals the number of bindings inserted
            const extractedBindings = jsSnippets.filter((s) => s && s.trim() !== '');
            expect(extractedBindings.length).toBe(count);

            // Spot-check a few bindings to ensure correctness
            expect(extractedBindings[0]).toBe('items[0]');
            expect(extractedBindings[50]).toBe(`items[50]`);
            expect(extractedBindings[count - 1]).toBe(`items[${count - 1}]`);
        });
    });
});

📊 Performance Profile

View detailed line-by-line performance analysis
To edit these changes git checkout codeflash/optimize-getDynamicBindings-ml226lbl and push.

Codeflash

The optimization achieves a **39% runtime improvement** (from 50.1μs to 35.9μs) by eliminating expensive regex operations in the hot path.

**What Changed:**
The critical optimization replaces the `isDynamicValue(segment)` regex test with a fast character code check that directly inspects the first two and last two characters of each segment.

**Why It's Faster:**
The line profiler reveals that `isDynamicValue(segment)` at line 110 consumed **70.7% of execution time** in the original code. This function uses `DATA_BIND_REGEX.test()`, which involves regex engine overhead for pattern matching. The optimized version replaces this with:
- Direct character code comparisons using `charCodeAt()` (checking for `{` = 123 and `}` = 125)
- Simple length check (`>= 4`)
- No regex compilation, pattern matching, or backtracking overhead

Character code comparisons are extremely fast primitive operations in JavaScript, while regex tests require the engine to parse and execute pattern matching logic on every call.

**Test Case Performance:**
The optimization shows particularly strong gains in:
- **Non-string inputs**: 87.2% faster (8.17μs → 4.36μs) - early return path improved
- **Empty bindings**: 98.1% faster (2.44μs → 1.23μs) - fewer regex operations on simple cases  
- **Basic bindings**: 149% faster (14.0μs → 5.63μs) - the common case benefits most from avoiding regex
- **JS action entities**: 21.8% faster (4.43μs → 3.64μs) - reduced overhead in special case handling

Complex cases with nested braces show minor regressions (6-19% slower) due to the additional character code checks, but these are uncommon edge cases that don't offset the significant gains in typical usage patterns. The overall 39% speedup demonstrates that the optimization excels at the most frequent patterns encountered in dynamic binding parsing.
@codeflash-ai codeflash-ai bot requested a review from misrasaurabh1 January 31, 2026 08:37
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Jan 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants