Skip to content

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

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

CodeMan62
Copy link
Contributor

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

@CodeMan62 CodeMan62 requested a review from a team as a code owner May 2, 2025 18:36
Copy link

changeset-bot bot commented May 2, 2025

⚠️ No Changeset found

Latest commit: 766c409

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link

codspeed-hq bot commented May 2, 2025

CodSpeed Performance Report

Merging #10430 will not alter performance

Comparing CodeMan62:fix/time-complexity (766c409) with main (3e99b8b)

Summary

✅ 152 untouched benchmarks

Copy link
Member

@kdy1 kdy1 left a 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());
Copy link
Member

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

@CodeMan62
Copy link
Contributor Author

@kdy1 I think now this pr is good to go

@kdy1 kdy1 self-assigned this May 3, 2025
@kdy1 kdy1 added this to the Planned milestone May 3, 2025
*cooked =
format!("{}{}", convert_str_value_to_tpl_cooked(&ls.value), cooked)
.into()
let str_part = convert_str_value_to_tpl_cooked(&ls.value);
Copy link
Member

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()
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
.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());
Copy link
Member

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()
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
.clone()

Copy link
Member

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());
Copy link
Member

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

@CodeMan62 CodeMan62 force-pushed the fix/time-complexity branch from 3cc2978 to 6ff3958 Compare May 11, 2025 18:43
@kdy1 kdy1 requested a review from Copilot May 11, 2025 23:11
Copy link

@Copilot Copilot AI left a 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;

@kdy1 kdy1 marked this pull request as draft May 11, 2025 23:13
@CodeMan62 CodeMan62 force-pushed the fix/time-complexity branch from 9124e35 to 9845fef Compare May 12, 2025 19:17
@CodeMan62 CodeMan62 marked this pull request as ready for review May 13, 2025 04:56
@CodeMan62
Copy link
Contributor Author

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
Copy link
Member

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 kdy1 marked this pull request as draft May 13, 2025 14:25
@CodeMan62
Copy link
Contributor Author

@kdy1 let me know if code is okay then I will fix fmt

@CodeMan62 CodeMan62 force-pushed the fix/time-complexity branch from 80ecd02 to 9686fcf Compare May 14, 2025 16:01
.into();
let cooked_value = convert_str_value_to_tpl_cooked(&rs.value);
// Pre-allocate additional space
let mut owned_cooked = cooked.to_string();
Copy link
Member

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.

@CodeMan62 CodeMan62 force-pushed the fix/time-complexity branch from 1d75b41 to 0e619fe Compare May 14, 2025 18:33
@CodeMan62
Copy link
Contributor Author

CodeMan62 commented May 14, 2025

@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 raw_str_part remains help me in that 🙏

fixed

@CodeMan62 CodeMan62 force-pushed the fix/time-complexity branch from 6b5a066 to c24ae7a Compare May 16, 2025 05:05
@CodeMan62
Copy link
Contributor Author

Everything is done now ready for review

@CodeMan62 CodeMan62 marked this pull request as ready for review May 16, 2025 05:06
@CodeMan62
Copy link
Contributor Author

@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());
Copy link
Member

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()
Copy link
Member

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());
Copy link
Member

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());
Copy link
Member

Choose a reason for hiding this comment

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

Not required

@CodeMan62
Copy link
Contributor Author

@kdy1 everything is done as you said review required

Comment on lines +412 to +414
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);
Copy link
Member

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(),
Copy link
Member

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();
Copy link
Member

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);
Copy link
Member

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);
Copy link
Member

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());
Copy link
Member

Choose a reason for hiding this comment

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

This is not required

@CodeMan62
Copy link
Contributor Author

@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 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants