-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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 using StringBuilder #24755
Optimize using StringBuilder #24755
Conversation
@SergerGood any specific reason to do this change? Are you seeing performance differences between appending a char and a string? @ArminShoeibi your link seems to point to a Java-related question, not .NET. |
@@ -44,12 +44,12 @@ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOpt | |||
var singleLine = (options & MetadataDebugStringOptions.SingleLine) != 0; | |||
if (singleLine) | |||
{ | |||
builder.Append($"ViewColumn: {Table.Name}."); | |||
builder.Append("ViewColumn: ").Append(Table.Name).Append('.'); |
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 makes the code less readable, in a codepath that is not perf-sensitive in any way.
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.
In the same place:
builder.Append(Name).Append(" ("); |
builder.Append(StoreType).Append(")"); |
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.
Still doesn't seem like an improvement to me (note that here there are three things, whereas in your two examples there are two). Can you please revert unless there's a good reason to do this?
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.
I don't know if it's good reason or not.
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
Intel Core i5-9300H CPU 2.40GHz, 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=6.0.100-preview.3.21202.5
[Host] : .NET Core 6.0.0 (CoreCLR 6.0.21.20104, CoreFX 6.0.21.20104), X64 RyuJIT
DefaultJob : .NET Core 6.0.0 (CoreCLR 6.0.21.20104, CoreFX 6.0.21.20104), X64 RyuJIT
Method | CharactersCount | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|
StringInterpolation | 5 | 32.56 ns | 0.679 ns | 1.764 ns | 32.28 ns | 0.0134 | - | - | 56 B |
Append | 5 | 14.08 ns | 0.309 ns | 0.356 ns | 14.13 ns | - | - | - | - |
StringInterpolation | 10 | 35.03 ns | 0.714 ns | 1.046 ns | 35.00 ns | 0.0153 | - | - | 64 B |
Append | 10 | 14.56 ns | 0.903 ns | 2.502 ns | 13.31 ns | - | - | - | - |
Benchmark code
[MemoryDiagnoser]
public class Program
{
private StringBuilder _builder;
private string _text;
[Params(5, 10)]
public int CharactersCount { get; set; }
[Benchmark]
public void StringInterpolation()
{
_builder.Clear();
_builder.Append($"Column: {_text}.");
}
[Benchmark]
public void Append()
{
_builder.Clear();
_builder.Append("Column: ").Append(_text).Append('.');
}
[GlobalSetup]
public void Setup()
{
_builder = new StringBuilder();
_text = new string('a', CharactersCount);
}
private static void Main() => BenchmarkRunner.Run<Program>();
}
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.
String interpolation indeed has a perf cost, but this code is completely non-perf-sensitive, so readability is more important than 20ns. Nanosecond-level optimizations which degrade readability really belong only in hot paths where they have a demonstrable impact on end performance.
oh, my bad. |
@roji https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1834 |
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.
Here's a quick benchmark:
Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|
AppendChar | 5.923 ns | 0.0093 ns | 0.0072 ns | - | - | - | - |
AppendString | 6.682 ns | 0.1175 ns | 0.0981 ns | - | - | - | - |
So there's roughly a half-nanosecond difference between these two methods, which doesn't seem significant in any of the code paths being touched. Not all quality rules make sense in all contexts...
We can still do this, but please revert the other changes which remove string interpolation - IMO these just make the code less readable for no reason.
Benchmark code
[MemoryDiagnoser]
public class Program
{
private StringBuilder _builder;
[Benchmark]
public void AppendChar()
{
_builder.Clear();
_builder.Append('x');
}
[Benchmark]
public void AppendString()
{
_builder.Clear();
_builder.Append("X");
}
[GlobalSetup]
public void Setup() => _builder = new StringBuilder();
static void Main(string[] args)
=> BenchmarkRunner.Run<Program>();
}
@@ -289,13 +289,13 @@ public virtual string Identifier(string name, ICollection<string>? scope = null) | |||
|
|||
if (partStart != name.Length) | |||
{ | |||
builder.Append(name.Substring(partStart)); | |||
builder.Append(name, partStart, name.Length - partStart); |
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.
Is there any reason to do this change? The previous code seems simpler.
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.
The reason is that there is a ready-made native better method for this.
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
Intel Core i5-9300H CPU 2.40GHz, 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=6.0.100-preview.3.21202.5
[Host] : .NET Core 6.0.0 (CoreCLR 6.0.21.20104, CoreFX 6.0.21.20104), X64 RyuJIT
DefaultJob : .NET Core 6.0.0 (CoreCLR 6.0.21.20104, CoreFX 6.0.21.20104), X64 RyuJIT
Method | CharactersCount | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|
StringSubstring | 5 | 23.03 ns | 1.147 ns | 3.308 ns | 21.55 ns | 0.0076 | - | - | 32 B |
AppendSubstring | 5 | 11.99 ns | 0.134 ns | 0.119 ns | 11.96 ns | - | - | - | - |
StringSubstring | 10 | 22.92 ns | 0.341 ns | 0.302 ns | 22.81 ns | 0.0076 | - | - | 32 B |
AppendSubstring | 10 | 11.77 ns | 0.216 ns | 0.169 ns | 11.78 ns | - | - | - | - |
Benchmark code
[MemoryDiagnoser]
public class Program
{
private StringBuilder _builder;
private string _text;
private int _partStart;
[Params(5, 10)]
public int CharactersCount { get; set; }
[Benchmark]
public void StringSubstring()
{
_builder.Clear();
_builder.Append(_text.Substring(_partStart));
}
[Benchmark]
public void AppendSubstring()
{
_builder.Clear();
_builder.Append(_text, _partStart, _text.Length - _partStart);
}
[GlobalSetup]
public void Setup()
{
_builder = new StringBuilder();
_text = new string('a', CharactersCount);
_partStart = CharactersCount / 2;
}
private static void Main() => BenchmarkRunner.Run<Program>();
}
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.
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.
I revert using string interpolation.. but there are many examples. And I'm still in favor of uniform approach.
.Append("FK_") |
builder.Append("CommandTimeout=").Append(Extension._commandTimeout).Append(' '); |
What about Substring in the same file?
builder.Append(name, partStart, i - partStart); |
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.
We can certainly make things more consistent - but given that nothing here is perf-sensitive, I'd go with what produces the shortest/simplest code, which is string interpolation in most cases (e.g. the CommandTimeout case above).
Accordingly, can you revert this particular change as well? All the rest looks fine.
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.
Yes. Done
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.
Thanks.
@@ -158,12 +158,12 @@ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOpt | |||
var singleLine = (options & MetadataDebugStringOptions.SingleLine) != 0; | |||
if (singleLine) | |||
{ | |||
builder.Append($"Column: {Table.Name}."); | |||
builder.Append("Column: ").Append(Table.Name).Append('.'); |
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.
Please revert
@@ -44,12 +44,12 @@ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOpt | |||
var singleLine = (options & MetadataDebugStringOptions.SingleLine) != 0; | |||
if (singleLine) | |||
{ | |||
builder.Append($"FunctionColumn: {Table.Name}."); | |||
builder.Append("FunctionColumn: ").Append(Table.Name).Append('.'); |
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.
Please revert
@@ -44,12 +44,12 @@ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOpt | |||
var singleLine = (options & MetadataDebugStringOptions.SingleLine) != 0; | |||
if (singleLine) | |||
{ | |||
builder.Append($"SqlQueryColumn: {Table.Name}."); | |||
builder.Append("SqlQueryColumn: ").Append(Table.Name).Append('.'); |
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.
Please revert
@@ -44,12 +44,12 @@ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOpt | |||
var singleLine = (options & MetadataDebugStringOptions.SingleLine) != 0; | |||
if (singleLine) | |||
{ | |||
builder.Append($"ViewColumn: {Table.Name}."); | |||
builder.Append("ViewColumn: ").Append(Table.Name).Append('.'); |
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.
Still doesn't seem like an improvement to me (note that here there are three things, whereas in your two examples there are two). Can you please revert unless there's a good reason to do this?
.AppendLine($"FROM \"{tableName}\"") | ||
.AppendLine($"WHERE {columnType} != 'null'") | ||
.AppendLine($"GROUP BY {columnType}") | ||
.Append("SELECT ").AppendLine(columnType) |
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.
Please revert
Hello @roji! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
Apologies, while this PR appears ready to be merged, I've been configured to only merge when all checks have explicitly passed. The following integrations have not reported any progress on their checks and are blocking auto-merge:
These integrations are possibly never going to report a check, and unblocking auto-merge likely requires a human being to update my configuration to exempt these integrations from requiring a passing check. Give feedback on thisFrom the bot dev teamWe've tried to tune the bot such that it posts a comment like this only when auto-merge is blocked for exceptional, non-intuitive reasons. When the bot's auto-merge capability is properly configured, auto-merge should operate as you would intuitively expect and you should not see any spurious comments. Please reach out to us at [email protected] to provide feedback if you believe you're seeing this comment appear spuriously. Please note that we usually are unable to update your bot configuration on your team's behalf, but we're happy to help you identify your bot admin. |
No description provided.