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

Optimize StringBuilder.as_string by constructing the string in-place and skipping unnecessary checks. #100295

Merged
merged 1 commit into from
Dec 12, 2024

Conversation

Ivorforce
Copy link
Member

@Ivorforce Ivorforce commented Dec 12, 2024

Originally authored by @YYF233333 in #97777.

I'm extracting the StringBuilder part as it's semantically different from the rest of the PR.

Benchmark

I have benchmarked the following code:

	String s1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";

	auto t0 = std::chrono::high_resolution_clock::now();
	for (int i = 0; i < 10000; i ++) {
		StringBuilder builder;
		for (int j = 0; j < 100; j ++) {
			builder.append(s1);
		}
		String s1 = builder.as_string();
	}
	auto t1 = std::chrono::high_resolution_clock::now();
	std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count() << "ms\n";

This yields:

  • 487ms on master (a40fc23)
  • 68ms on this PR (26ac632e364e504b34821674b102a0f7a21112c0)

Therefore, this PR can be expected to speed up uses of StringBuilder by 7x in extreme cases.

Discussion

The reason for this speed up can be separated as follows:

  • The needed space was allocated twice.
  • StringBuilder was calling String(const char *ptr, int clip_to)
    • This function calls the inefficient function _strlen_clipped, to find the length of the string even though it was known.
    • This function copies the string.
    • During the copy, the string checks the input for integrity, even though all inputs are known to be correct (char * is always a correct string, and char32_t * were taken from String where they are known to be correct). Therefore, the check can be skipped.

…e and skipping unnecessary checks.

Co-authored-by: YYF233333 <nbyyf2002@mail.ustc.edu.cn>
@Ivorforce Ivorforce force-pushed the string-builder-inplace branch from 1e06469 to 76af953 Compare December 12, 2024 00:35
Copy link
Member

@clayjohn clayjohn left a comment

Choose a reason for hiding this comment

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

Looks great. In my previous testing, this change made the biggest difference for StringBuilder for compiling shaders

@akien-mga akien-mga merged commit 7b15c06 into godotengine:master Dec 12, 2024
20 checks passed
@akien-mga
Copy link
Member

Thanks!

@Ivorforce Ivorforce deleted the string-builder-inplace branch December 12, 2024 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants