Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the performance for parsing props, classes and style #341

Closed
falkoschindler opened this issue Feb 3, 2023 · 4 comments
Closed
Assignees
Labels
enhancement New feature or request
Milestone

Comments

@falkoschindler
Copy link
Contributor

falkoschindler commented Feb 3, 2023

While profiling the UI updates in a production environment we noticed that the .props() method can be a bottleneck in certain situations (see #338). Currently we use the shlex module. But this might be overpowered.

In #338 (comment) I presented an alternative solution:

pattern = re.compile(r'([\w-]+)(?:=(?:("[^"\\]*(?:\\.[^"\\]*)*")|([\w-]+)))?(?:$|\s)')

def extract_dictionary(string):
    dictionary = {}
    for match in pattern.finditer(string):
        key = match.group(1)
        value = match.group(2) or match.group(3)
        if value and value.startswith('"') and value.endswith('"'):
            value = json.loads(value)
        dictionary[key] = value or True
    return dictionary

string = 'dark color=red label="First Name" hint="Your \\"given\\" name" input-style="{ color: #ff0000 }"'
props = extract_dictionary(string)
print(props)

Output:

{'dark': True, 'color': 'red', 'label': 'First Name', 'hint': 'Your "given" name', 'input-style': '{ color: #ff0000 }'}

We should compare both implementations and switch to the regex solution if it's faster. Maybe we can also find regular expressions for parsing classes and style definitions.

@falkoschindler
Copy link
Contributor Author

I compared the speed of extract_dictionary (regex) with Element._parse_props (shlex). I used the same string as above and ran 10,000 iterations:

MacBook M1

0.058 seconds for regex
0.344 seconds for shlex

Jetson Nano

0.762 seconds for regex
4.529 seconds for shlex

This is about a 6x performance improvement. For a simpler string like "disabled" the improvement is almost 10x.

@falkoschindler
Copy link
Contributor Author

I compared the current style parsing

@staticmethod
def _parse_style(text: Optional[str]) -> Dict[str, str]:
return dict(_split(part, ':') for part in text.strip('; ').split(';')) if text else {}

nicegui/nicegui/element.py

Lines 211 to 213 in 95364b7

def _split(text: str, separator: str) -> Tuple[str, str]:
words = text.split(separator, 1)
return words[0].strip(), words[1].strip()

implementation with a trivial one

def parse_trivial(text: str) -> dict:
    result = {}
    for word in text.split(';'):
        word = word.strip()
        if word:
	        key, value = word.split(':', 1)
	        result[key.strip()] = value.strip()
    return result

and one with a compiled regex:

STYLE_PATTERN = re.compile(r'\s*(.*?)\s*:\s*(.*?)\s*(?:;|$)')

def parse_regex(text: str) -> dict:
    return dict(re.findall(STYLE_PATTERN, text))

But the "trivial" implementation is fastest on a MacBook M1

0.182 seconds for current
0.128 seconds for trivial
0.518 seconds for regex

and on a Jetson Nano:

2.925 seconds for current
1.755 seconds for trivial
3.781 seconds for regex

This is for 10,000 iterations with the string "color: red; background-color: green ; width:12em;height:34.5em; transform: translate(120.0px, 50%); box-shadow: 0 0 0.5em #1976d2". But results are similar for a simple "color: red".

I hoped to save some time with the much shorter regex. But apparently we should replace the current implementation with parse_trivial (by the way: auto-completion by GitHub copilot).

@falkoschindler
Copy link
Contributor Author

Ok, I replaced both functions with faster implementations. For classes we don't need to parse anything, but simply split a string into words.

Branch parsing-performance is ready for review and merge.

@falkoschindler falkoschindler added this to the v1.1.6 milestone Feb 4, 2023
@rodja
Copy link
Member

rodja commented Feb 5, 2023

Reviewed and merged. Wonderful!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants