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

Remove probably unused impl ImplicitClone on Cow<str> #2447

Merged

Conversation

cecton
Copy link
Member

@cecton cecton commented Feb 9, 2022

Description

This trait implies that cloning will be always cheap. In Cow<str> this might not be the case if the variant is Cow::Owned and the String is big. Unfortunately this is something verifiable only at runtime. Therefore it makes sense to remove ImplicitClone for Cow<str> as a way to guarantee the cheap cloning.

(Not sure if that's super clear xD is it? feel free to edit)

Link to related experiment examples: https://github.com/rustminded/yew-immutable/tree/main/examples

Checklist

  • I have run cargo make pr-flow nahh
  • I have reviewed my own code
  • I have added tests not possible to write a test to ensure a trait is not implemented since all tests need to compile

github-actions[bot]
github-actions bot previously approved these changes Feb 9, 2022
@cecton cecton force-pushed the cecton-remove-implicit-clone-on-cow-str branch from 4a4ab91 to 516abd5 Compare February 9, 2022 12:36
github-actions[bot]
github-actions bot previously approved these changes Feb 9, 2022
github-actions[bot]
github-actions bot previously approved these changes Feb 9, 2022
@github-actions
Copy link

github-actions bot commented Feb 9, 2022

Visit the preview URL for this PR (updated for commit eeb09aa):

https://yew-rs-api--pr2447-cecton-remove-implic-m874ivwi.web.app

(expires Wed, 16 Feb 2022 14:19:29 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

@cecton
Copy link
Member Author

cecton commented Feb 9, 2022

@hamza1311 all checks passed ✔️

So... the last question remaining is: do we want to break the API of 0.20 for this? cc @siku2

For me it sounds really minor for this particular case but I don't mind to continue working on a fork of yew until I reach the conclusions of my experiment.

@siku2
Copy link
Member

siku2 commented Feb 9, 2022

I absolutely don't mind the breaking change and I agree with your semantic definition of ImplicitClone. What I'm worried about is that it's a bit hypocritical to remove the implementation for Cow when the same argument can be applied to AttrValue as well.

@cecton
Copy link
Member Author

cecton commented Feb 9, 2022

@siku2 fair. This lacks of consistency.

... 🤔

Maybe I can just remove the variant Owned on AttrValue.

@@ -76,7 +76,7 @@ impl IntoPropValue<AttrValue> for Classes {
None => unsafe { unreachable_unchecked() },
}
} else {
AttrValue::Owned(self.to_string())
Copy link
Member Author

Choose a reason for hiding this comment

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

In the end I think it is probably more optimal with an Rc here. I can imagine tons of classes being passed.

I don't know how big a string needs to be before it's more optimal to use Rc. 🤔

Besides, if I understood correctly, there is no cost of double indirection here since the String is actually transformed to Rc<str>.

Related conversation: https://twitter.com/Argorak/status/1491044312595185667?s=20&t=5e2MZV7sWpdCA3GlZpaQlA

Copy link
Member Author

Choose a reason for hiding this comment

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

To be honest I'm really not looking at performance but more at correctness. The fact that ImplicitClone should guarantee - at compile time - that this will be cheap to clone sounds important to me. (Critics are welcome)

Copy link
Member

Choose a reason for hiding this comment

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

My initial concern when not removing Owned was this unsolved question: which one of the is cheaper to clone (if there's a difference at all):

let s = String::new();
let rc = Rc::from("")'

This can be applied with small strings, which are not just empty strings

Copy link
Member Author

Choose a reason for hiding this comment

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

[package]
name = "tmp-rlrkag"
version = "0.1.0"
edition = "2021"

[dependencies]

[dev-dependencies]
criterion = "0.3"

[[bench]]
name = "my_benchmark"
harness = false
use std::rc::Rc;
use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn rc_str(s: Rc<str>) -> Rc<str> {
    s.clone()
}

fn string(s: String) -> String {
    s.clone()
}

fn criterion_benchmark_rc_str(c: &mut Criterion) {
    let s = String::new();
    let rc = Rc::from(s);
    c.bench_function("Rc<str>", |b| b.iter(|| rc_str(black_box(Rc::clone(&rc)))));
}

fn criterion_benchmark_string(c: &mut Criterion) {
    let s = String::new();
    c.bench_function("String", |b| b.iter(|| string(black_box(s.clone()))));
}

criterion_group!(benches, criterion_benchmark_rc_str, criterion_benchmark_string);
criterion_main!(benches);

Results:

Rc<str>                 time:   [1.0763 ns 1.0769 ns 1.0774 ns]                     
Found 31 outliers among 100 measurements (31.00%)
  6 (6.00%) low severe
  8 (8.00%) low mild
  14 (14.00%) high mild
  3 (3.00%) high severe

String                  time:   [7.7115 ns 7.7901 ns 7.8681 ns]                    
Found 21 outliers among 100 measurements (21.00%)
  2 (2.00%) low severe
  7 (7.00%) low mild
  1 (1.00%) high mild
  11 (11.00%) high severe

It seems cloning the Rc is faster than the empty String.

Copy link
Member Author

@cecton cecton Feb 11, 2022

Choose a reason for hiding this comment

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

I also checked if reversing the order of the benchmarks change anything (in case of CPU warm up) but apparently it doesn't affect the performances:

String                  time:   [7.8812 ns 8.0260 ns 8.1961 ns]                    
                        change: [+2.5291% +4.2576% +6.1807%] (p = 0.00 < 0.05)
                        Performance has regressed.

Rc<str>                 time:   [1.0555 ns 1.0572 ns 1.0595 ns]                     
                        change: [-2.2550% -1.8980% -1.5580%] (p = 0.00 < 0.05)
                        Performance has improved.

(It says "regressed" / "increased" but that's probably because the measure is so small. It doesn't really improve or increased anything)

Copy link
Member

@ranile ranile Feb 11, 2022

Choose a reason for hiding this comment

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

Can you create an issue for removing AttrValue::Owned with these benchmarks?

Nevermind, it's already removed.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup! ^_^'

@ranile
Copy link
Member

ranile commented Feb 11, 2022

Maybe I can just remove the variant Owned on AttrValue.

That was actually mentioned when I implemented AttrValue but I never with it. See #1994 (comment)

@cecton cecton requested a review from ranile February 11, 2022 19:58
@ranile ranile merged commit 95fb5dc into yewstack:master Feb 11, 2022
@cecton cecton deleted the cecton-remove-implicit-clone-on-cow-str branch March 31, 2022 10:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants