-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
fix time complexity bug in string concat logic #10430
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
|
CodSpeed Performance ReportMerging #10430 will not alter performanceComparing Summary
|
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.
Cow::into_owned
does not allocate in case it's not necessary.
let s = lls.into_owned() + &*rls; | ||
// Create a new string with capacity to avoid multiple allocations | ||
let mut s = lls.into_owned() + &*rls; | ||
s.reserve(rls.len()); |
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 inefficient because we already pushed the string
@kdy1 I think now this pr is good to go |
*cooked = | ||
format!("{}{}", convert_str_value_to_tpl_cooked(&ls.value), cooked) | ||
.into() | ||
let str_part = convert_str_value_to_tpl_cooked(&ls.value); |
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 allocates, so it's better to call str_part.reserve(cooked.len());
after this line and push the string to str_part
r_first.raw = new; | ||
let raw_str_part = ls | ||
.raw | ||
.clone() |
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.
.clone() |
.map(|s| convert_str_raw_to_tpl_raw(&s[1..s.len() - 1])) | ||
.unwrap_or_else(|| convert_str_value_to_tpl_raw(&ls.value).into()); | ||
|
||
let mut new_raw = String::with_capacity(raw_str_part.len() + r_first.raw.len()); |
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.
Same here. You don't need to allocate a new string; rather you should append to raw_str_part
// Calculate the total length to avoid multiple allocations | ||
let raw_str_part = rs | ||
.raw | ||
.clone() |
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.
.clone() |
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.
Fix this
format!("{}{}", cooked, convert_str_value_to_tpl_cooked(&rs.value)) | ||
.into(); | ||
// Create a new string with sufficient capacity | ||
let mut new_cooked = String::with_capacity(cooked.len() + rs.value.len()); |
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.
You should not allocate a new string. You should rather append to cooked
3cc2978
to
6ff3958
Compare
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.
Pull Request Overview
This PR addresses performance issues in string concatenation logic by pre-allocating memory to reduce repeated string allocations.
- Pre-allocates string buffers using estimated capacities instead of relying on default allocations.
- Updates string concatenation patterns to use push operations for reduced overhead.
Comments suppressed due to low confidence (1)
crates/swc_ecma_minifier/src/compress/pure/strings.rs:48
- Consider pre-allocating a new String with the combined capacity of 'lls' and 'rls' and using push_str operations rather than the '+' operator to avoid potential hidden reallocations.
let s = lls.into_owned() + &*rls;
9124e35
to
9845fef
Compare
I think now it is ready for review |
@@ -391,20 +409,21 @@ impl Pure<'_> { | |||
); | |||
|
|||
if let Some(cooked) = &mut l_last.cooked { | |||
// Direct concatenation with format! to avoid unnecessary allocations |
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 does not avoid unnecessary allocations
@kdy1 let me know if code is okay then I will fix fmt |
80ecd02
to
9686fcf
Compare
.into(); | ||
let cooked_value = convert_str_value_to_tpl_cooked(&rs.value); | ||
// Pre-allocate additional space | ||
let mut owned_cooked = cooked.to_string(); |
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 code is very inefficient. You don't need an allocation here.
1d75b41
to
0e619fe
Compare
@kdy1 I request you to help me further in this I can't figure out how to finish it I think the PR is almost done just append to fixed |
6b5a066
to
c24ae7a
Compare
Everything is done now ready for review |
@kdy1 general ping for review, let me know if still things left?? |
format!("{}{}", cooked, convert_str_value_to_tpl_cooked(&rs.value)) | ||
.into(); | ||
let mut str_part = convert_str_value_to_tpl_cooked(&rs.value).into_owned(); | ||
str_part.reserve(cooked.len()); |
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 line is not required
// Calculate the total length to avoid multiple allocations | ||
let raw_str_part = rs | ||
.raw | ||
.clone() |
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.
Fix this
format!("{}{}", convert_str_value_to_tpl_cooked(&ls.value), cooked) | ||
.into() | ||
let mut str_part = convert_str_value_to_tpl_cooked(&ls.value).into_owned(); | ||
str_part.reserve(cooked.len()); |
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.
Not required
r_first.raw = new; | ||
// Convert to owned string, reserve space and append | ||
let mut owned_raw = l_last.raw.to_string(); | ||
owned_raw.reserve(r_first.raw.len()); |
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.
Not required
@kdy1 everything is done as you said review required |
let mut str_part = convert_str_value_to_tpl_cooked(&rs.value).into_owned(); | ||
str_part.push_str(cooked); | ||
*cooked = swc_atoms::Atom::from(str_part); |
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 changes behavior (wrongly), allocates more
// Calculate the total length to avoid multiple allocations | ||
let raw_str_part = match &rs.raw { | ||
Some(s) => convert_str_raw_to_tpl_raw(&s[1..s.len() - 1]), | ||
None => convert_str_value_to_tpl_raw(&rs.value).into(), |
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 seems inefficient. You don't need String
here. Use Cow
instead
|
||
let mut raw = raw_str_part.to_string(); |
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 super inefficient
let additional_quasis = rt.quasis.len(); | ||
let additional_exprs = rt.exprs.len(); | ||
|
||
l.quasis.reserve(additional_quasis); |
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 not required
let additional_exprs = rt.exprs.len(); | ||
|
||
l.quasis.reserve(additional_quasis); | ||
l.exprs.reserve(additional_exprs); |
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.
Same here
let new_str = format!("{second_str}{third_str}"); | ||
// Directly create a new string with the combined content | ||
let mut second_owned = second_str.into_owned(); | ||
second_owned.reserve(third_str.len()); |
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 not required
@kdy1 thanks for all your reviews appreciation but I want to say sorry I am tired trying things here in this PR so I request you to help me fix this maybe I am missing something 🙏 |
Description:
So there are some time complexity issues in string concat logic this PR tries to fix them. I have ensured that all tests are passing
BREAKING CHANGE:
N/A
Related issue (if exists):
#10219