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

perf(buffer)!: filled moves the cell to be filled #1148

Merged
merged 4 commits into from
May 27, 2024

Conversation

EdJoPaTo
Copy link
Contributor

@EdJoPaTo EdJoPaTo commented May 26, 2024

This removes a clone from inside the method allowing the user to do it explicitly when needing.

BREAKING CHANGE: Buffer::filled moves the cell instead of taking a reference.

-Buffer::filled(area, &Cell::new("X"));
 Buffer::filled(area, Cell::new("X"));

#1143 (comment)

@github-actions github-actions bot added the Type: Breaking Change This change will cause application code to break and must be noted in the breaking changes docs etc. label May 26, 2024
Copy link

codecov bot commented May 26, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 94.2%. Comparing base (8b447ec) to head (e38bd57).
Report is 1 commits behind head on main.

Additional details and impacted files
@@          Coverage Diff          @@
##            main   #1148    /-   ##
=====================================
  Coverage   94.2%   94.2%           
=====================================
  Files         60      60           
  Lines      14511   14511           
=====================================
  Hits       13672   13672           
  Misses       839     839           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@orhun
Copy link
Member

orhun commented May 26, 2024

Q: Isn't taking a reference better in terms of performance? (no copy & clone?)

@EdJoPaTo
Copy link
Contributor Author

EdJoPaTo commented May 26, 2024

Depends on the case. Moving is always the simplest solution with the best performance. (= moving the ownership, the data stays exactly the same, nothing needs to be done at runtime with the data, no (64-bit) reference pointer needs to be created). When the data is only read it depends on the size of the data (u16 for example is smaller than a pointer on a 64-bit system → its more efficient to copy then move the u16 instead of using a 64 bit reference. Which is why it's often a performance improvement to #[derive(Copy)] enum and moving them rather than taking a reference of them).
When the data is used (written to or whatever) and also required after the method then it needs to be cloned at some point. So either take a reference and clone it inside the method or clone it outside the method.
Cloning it outside the method allows the user to make that choice depending on their use case. Cloning it inside the method forces the user with an additional clone / drop even when it might not be required like in this case.

Buffer::filled(area, &Cell::new()) results basically in this code:

let cell = Cell::new();
Buffer::filled(area, &cell)
drop(cell); // Rust silently does this for everything not moved away at the end of the context

In this case we can save the drop and one clone as we can move in the cell directly.

This is not very significant on a Cell as a cell is rather small. With stuff like String or Vec this can get rather expensive.

Basically… when the method takes a reference and clones it directly, then it could just take the value directly and allow the caller to decide whether cloning or moving is the better choice.

Fun fact: Into can hide situations like this resulting in additional clones.

fn something<S: Into<String>>(input: S) {}

let huge_string: String = …;
something(&huge_string); // into silently clones in this case
drop(huge_string); // Rust silently does this at the end of the context

Changing the reference to a move can be a performance improvement (and did for me in the past) which is why I avoid Into which is not a pure into and only a to. Especially as .into() might change to a different implementation silently on a refactoring, and it's quite annoying to find the actually used implementation making sure it doesn't do something performance critical.

Copy link
Member

@joshka joshka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typos: fill -> filled in the change and the commit message / PR

It would be nice to have a succinct rationale added for this to explain why this change makes sense. Not a massive issue as this is not a widely used function.

Probably off-topic to this PR, any idea what the effect of calling Buffer::filled with a double width string should be / actually is?

BREAKING-CHANGES.md Outdated Show resolved Hide resolved
BREAKING-CHANGES.md Outdated Show resolved Hide resolved
BREAKING-CHANGES.md Outdated Show resolved Hide resolved
BREAKING-CHANGES.md Outdated Show resolved Hide resolved
Co-authored-by: Josh McKinney <[email protected]>
@EdJoPaTo EdJoPaTo changed the title perf(buffer)!: fill moves the cell to be filled perf(buffer)!: filled moves the cell to be filled May 27, 2024
@EdJoPaTo
Copy link
Contributor Author

EdJoPaTo commented May 27, 2024

Probably off-topic to this PR, any idea what the effect of calling Buffer::filled with a double width string should be / actually is?

Likely gibberish, but that's hard to detect as cells with width > 1 are possible and allowed (emojis, …). The ones after the width > 1 cells are skipped I think?

It might be an idea to Buffer::filled(area, symbol) directly and set Cell private. The only use case of filled is probably testing. No need to expose internals for that when not needed. And prevents changes to this more internal stuff as it ends up being breaking fast.

@joshka
Copy link
Member

joshka commented May 27, 2024

Yeah, I think it might be ok, AAAA AA -> A A A . Every second cell is hidden, but will display correctly regardless I think.

src/buffer/buffer.rs Show resolved Hide resolved
@joshka joshka merged commit 4ce67fc into ratatui:main May 27, 2024
42 checks passed
@EdJoPaTo EdJoPaTo deleted the buffer-fill-move branch May 27, 2024 18:13
itsjunetime pushed a commit to itsjunetime/ratatui that referenced this pull request Jun 23, 2024
joshka pushed a commit to erak/ratatui that referenced this pull request Oct 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Breaking Change This change will cause application code to break and must be noted in the breaking changes docs etc.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants