-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Avoid cloning text in TextEdit for Arc<str> text buffers
#7673
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
base: main
Are you sure you want to change the base?
Conversation
|
Preview available at https://egui-pr-preview.github.io/pr/7673-textedit-less-cloning View snapshot changes at kitdiff |
edde2b2 to
3da488e
Compare
3da488e to
71f34ea
Compare
|
Actually for my use case it seems sufficient to make the |
b033ae4 to
6e04a39
Compare
6e04a39 to
f5d9f00
Compare
| // * https://github.com/emilk/egui/issues/5163 | ||
|
|
||
| job.wrap.max_width = job.wrap.max_width.round(); | ||
| Arc::make_mut(&mut job).wrap.max_width = job.wrap.max_width.round(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is slightly unfortunate and could be avoided by threading a separate max_width throughout layout but that sounds like a lot of work while when working with a lot of text you probably don't necessarily need line wrapping anyway.
emilk
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good, but I would love it if you added some benchmarks to crates/egui/benches and showed us the before/after numbers of that. Otherwise this may regress again.
| // TODO(afishhh): Keep as `Arc<str>` instead of cloning to a `String` | ||
| let text = (*mask_if_password(password, text.clone_to_arc())).to_owned(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest we do this in this PR, or it feels a bit half-finished
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do agree but this goes straight into a LayoutJob and I'm not sure how exactly to handle Arcifying that.
Should I just split out a builder type and make LayoutJob itself immutable?
A large chunk of time spent each frame on large immutable
TextEdits is cloning the text string repeatedly:This PR attempts to switch surrounding code to
Arc<str>and addsTextBuffer::clone_to_arc+impl TextBuffer for Arc<str>which allow skipping most of the string copying work for buffers that are themselvesArcs.These changes (and modifying the app to pass
Arc<str>) make the profile look like this:Clearly better and decreases the frame times in this test app by ~10ms.
However there is one elephant in the room (the remaining
to_owned) that requires more invasive changes to handle:LayoutJob. This elephant is actually very significant: I am able to achieve acceptable performance (in my real-world app that I'm trying to speed up) with a 1MB XML string if I apply a hacky solution to avoid cloning the string onLayoutJobcreation.Here's what that looks like on the benchmark (#7649 + this + hacky
LayoutJobfix):This does require changing
LayoutJobthough and I'm not exactly sure what to do here which is why this is a draft. Should I just makeLayoutJobitself immutable and add a builder type for it? If so what should the builder type's API look like? There's a few possibilities that come to mind but not sure I particularly like any of them.Footnotes
Ignore the sample count, these profiles were run for completely arbitrary amounts of time. ↩ ↩2
Also suggests that the next optimization opportunity is in skipping undo handling for immutable buffers, along with
PaintStatsrelated stuff which is not visible here but constitutes a large percentage of runtime. ↩