mirror of
https://github.com/rivo/tview.git
synced 2026-04-26 21:35:54 +03:00
[GH-ISSUE #686] TextView consumes significant CPU when wrapping is enabled #501
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/tview#501
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @denandz on GitHub (Jan 7, 2022).
Original GitHub issue: https://github.com/rivo/tview/issues/686
Hello, I'm currently using tview for an interactive HTTP/HTTPS intercept proxy (https://github.com/denandz/glorp) and have come across an issue. In my case, i'd like to display large HTTP responses with word wrapping enabled, which causes issues.
If word wrapping is enabled on a TextView, and a large amount of data is written to the view, then a massive CPU consumption and application lockup until
TextView.reindexBufferreturns.I found out this is due to
runewidth.Truncatewith large amounts of data. pprof output below, web attached.The following code can be used to replicate this issue, and was used to generate the profile above:
@denandz commented on GitHub (Jan 7, 2022):
In my case, I don't really care about rune processing right now and I'm fairly happy just displaying bytes. I was able to fix my issue by tweaking TextView to hack up the string using slices instead.
Would setting a "raw" mode on a TextView be an option? I'd envisage some kind of flag where I could specify
NewTextArea.SetRaw(true)which would disable the rune processing and just slam bytes into the TextView with reckless abandon. This might be an easy win?This was my hacky change to textview.go
reindexBuffer, just for reference:If raw mode was going to be implemented, then
writewould also need to be tweaked to not do its partial trailing utf8 rune logic when raw mode is enabled.If this is something you'd consider I can take a closer look and try get a PR together.
@denandz commented on GitHub (Jan 8, 2022):
Another, less hacky option, would be to change the
reindexBufferwrap logic to avoid the use ofrunewidth.Truncateall together. I tested the following change with an 8MB textview buffer and did not experience the CPU consumption issue (I didn't test the word-wrap functionality, only wrap, FWIW):@rivo commented on GitHub (Feb 15, 2022):
I'll preface my response by saying that I've mentioned multiple times in other issues that
TextViewis not suitable for very large texts. You will always run into performance issues such as the one you encountered. I'm actually going to add a note about this in the officialTextViewdocumentation because this comes up quite often.As for your suggestions, I'm afraid that this will not be a route I will take. It all started like your first example. Then I quickly learned that it wasn't that simple. So I implemented your second example. And that wasn't enough either. (Check https://github.com/rivo/uniseg for an explanation.) Dozens of issues and complaints later, we're at an implementation that works with Unicode, different languages, wide characters, even emojis. Of course, all of these requirements from users come with a performance hit. If you find a way to make
runewidth.Truncate()faster, I think @mattn and I will be all ears. Buttviewwon't go back to the naive implementation. (And I currently don't intend to write and maintain an entire second implementation for "raw" mode.)You may want to consider
TextView.SetMaxLines()to limit the number of lines in the text view. Or maybe use virtual tabes if random access is required. (But then you won't get word wrapping for free.)@denandz commented on GitHub (Feb 16, 2022):
Okay, if you don't intend to address the performance issues in TextView then you can close this issue WONTFIX.
TextView.SetMaxLines() doesn't work for my application, I'd like to be able to view 100 or 200kb of text in a text view without pinning my cpu.
I do have two requests though:
@rivo commented on GitHub (Feb 17, 2022):
Actually, one other suggestion that just came to mind was that you might experience the high CPU load when you're redrawing the
TextViewcomponent very frequently. It's probably worth looking into reducing the number of redraws to maybe a few times per second. I haven't seen your implementation but if you update the screen after everyio.Write()to theTextView, this might be a bit too much if only a few bytes are written to the text view at a time.Finally, if your application really only deals with ASCII characters, it shouldn't be very difficult to implement line-breaking yourself and simply pipe that pre-broken-over text into the text view. Then you can turn off wrapping and
runewidth.Truncate()will never be called.@denandz commented on GitHub (Feb 17, 2022):
You can replicate this issue using the original code snippet in the original issue message, which has no calls to draw on each
io.Write(). It's fairly simple and should demonstrate the performance hits.I can figure out a solution for my specific use case that doesn't involve changing anything in tview
@denandz commented on GitHub (Feb 28, 2022):
In case anyone stumbles across this issue, I ended up implementing a very basic line wrapped text view primitive here: https://github.com/denandz/glorp/blob/master/views/textprimitive.go
Multi-megabyte buffers can be handled in a reasonable time frame, but none of the other TextView functionality is implemented.