-
-
Notifications
You must be signed in to change notification settings - Fork 602
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 update performance for large UIs on slow machines #338
Comments
Regarding (6):
That's not too hopeful. But it doesn't mean that there isn't a subset of shlex sufficient for this purpose. Update: I opened #341 for this subtopic. |
I started to implement the update queue (2). In principle it's not too hard and improves performance immediately. Due to the fact that NiceGUI elements always have larger IDs than their parents (because parents are always created first), we can easily sort the queue by element ID and skip traversing elements that already have been processed. However, a difficulty arose for So I'm starting to think about a more holistic approach. There are (at the moment) five different socket messages emitted from the server to the client: "update", "run_method", "run_javascript", "open", "notify". In view of (1) there might be "update_classes", "update_style", "update_props", and maybe "update_text" in near future. Can we collect them all in one queue? Especially for Unfortunately, this complicates the algorithm for consuming the loop quite a bit. In general all messages only contain a name, some payload, and a room ID. But updates are special, because they can get replaced by newer updates of the same element (or a parent). In combination with Should we forget about skipping updates? The update queue still saves a lot of task creations. And introducing dedicated updates for props, classes, and style (1) will reduce the payload significantly. But in the example above with the colored buttons there will still be up to seven messages per element. What a waste. |
Regarding faster props parsing (6): Together with ChatGPT I found the following implementation that seems to do what we need. Since it uses a compiled regular expression it should be pretty fast. But we should measure it on a production system. The 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:
Update: I opened #341 for this subtopic. |
I opened a new issue #341 for the props parsing subtopic (6). it's rather independent of when and how to send updates to the client, so we shouldn't mix the discussion. |
Wow, just now I noticed how bad our current update strategy actually is: While developing the update queue, I counted the number of times each element is sent to the client with the previous implementation. For the grid of colored buttons (#338 (comment)) that's up to 7 times for each button:
But that means: Nesting a collection of elements one layer deeper results in another transmission of all of them! That might be better if we add the card to the page content first, then add empty rows, and finally add buttons to the rows. But this is far from nice. Luckily, our update queue (or more general: message queue) will improve this behavior significantly. |
Merged! |
We noticed that large UIs can lead to suboptimal update behavior on slow machines.
Consider the following grid of buttons:
Every
.props
,.classes
, and.style
call will trigger a UI update. After leaving the scope of aui.row
, the whole row will be updated. And after leaving the scope of theui.card
, the card will be updated including all rows and buttons.We identified six ideas for improving the update mechanism:
Update props, classes, and style without using the
update
method.Currently, these methods call
update
internally. This collects and transmits the complete element including all children. Restricting the transmission to the collection of props, classes, or style reduces the payload significantly.Introduce an update queue processed by a dedicated update loop.
In order to avoid creating a new
Task
for every individual update, we could collect updates in a queue that is processed regularly by an update loop.This update loop could be even more efficient by transmitting the whole queue at once, excluding redundant elements: Like with the current implementation, updating an element involves collecting all its descendants. But having a list of queued elements, we can remove duplicates and combine all remaining elements in one update message.
Using
create_lazy
instead ofcreate
to skip creating tasks for outdated updates.This will not apply after implementing (2).
Skip an update if the parent element has not been transmitted to the client.
This will probably be automatically the case after implementing (2).
Use a different API for setting props, classes, and style.
Without the need for parsing strings we could save some computing time. But an alternative API is still an open discussion How to simplify styling? #117.
Replace
shlex
with a different parser for.props
.In order to support quoted strings like
.props('error-message="Invalid email"')
or even.props('error-message="Invalid name \"Alice\""')
we are usingshlex
. But processing the string seems to be the main bottleneck when calling.props
. Maybe there is a faster alternative like regex that serves our needs.The text was updated successfully, but these errors were encountered: