-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Use ValueStringBuilder in preference to StringBuilderCache #26934
Changes from 1 commit
3d6a7a3
861e503
8a93a3f
d2be6bc
f0c2a71
3ac1100
b232228
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -173,20 +173,21 @@ public override string ToString() | |
ThrowHelper.ThrowForUnsupportedVectorBaseType<T>(); | ||
|
||
int lastElement = Count - 1; | ||
StringBuilder sb = StringBuilderCache.Acquire(); | ||
Span<char> initialBuffer = stackalloc char[64]; | ||
var sb = new ValueStringBuilder(initialBuffer); | ||
CultureInfo invariant = CultureInfo.InvariantCulture; | ||
|
||
sb.Append('<'); | ||
for (int i = 0; i < lastElement; i++) | ||
{ | ||
sb.Append(((IFormattable)this.GetElement(i)).ToString("G", invariant)) | ||
.Append(',') | ||
.Append(' '); | ||
sb.Append(((IFormattable)this.GetElement(i)).ToString("G", invariant)); | ||
sb.Append(','); | ||
sb.Append(' '); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question/comment as earlier... I wonder if |
||
} | ||
sb.Append(((IFormattable)this.GetElement(lastElement)).ToString("G", invariant)) | ||
.Append('>'); | ||
sb.Append(((IFormattable)this.GetElement(lastElement)).ToString("G", invariant)); | ||
sb.Append('>'); | ||
|
||
return StringBuilderCache.GetStringAndRelease(sb); | ||
return sb.ToString(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -159,15 +159,16 @@ public static string Concat<T>(IEnumerable<T> values) | |
|
||
// Create the StringBuilder, add the chars we've already enumerated, | ||
// add the rest, and then get the resulting string. | ||
StringBuilder result = StringBuilderCache.Acquire(); | ||
Span<char> initialBuffer = stackalloc char[64]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The stack reserved sizes in this file can be probably be a bit higher, like 128 chars. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We've been fairly loose / non-prescriptive with how we do stackallocs like this in the past. In various places around corelib, we use various limits for stackalloc char when we don't know the actual upper limit, including 256, 128, 64, and 32 (lots of other sizes are used of course when we do have an upper bound). Not as part of this change, but it seems we should probably standardize with some const somewhere that everyone just uses. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that a general reasonable sized buffer constant would be useful. We often know the typical maximum size, e.g. 32-chars for a number formatting. I assume that we would keep these scenario specific maximum sizes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yup. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might also want to harmonize with the jit's threshold for converting fixed sized locallocs to locals, which is currently 32. Back when I set this I did not see many > 32 cases. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here we have |
||
var result = new ValueStringBuilder(initialBuffer); | ||
result.Append(c); // first value | ||
do | ||
{ | ||
c = en.Current; | ||
result.Append(c); | ||
} | ||
while (en.MoveNext()); | ||
return StringBuilderCache.GetStringAndRelease(result); | ||
return result.ToString(); | ||
} | ||
} | ||
else | ||
|
@@ -195,7 +196,8 @@ public static string Concat<T>(IEnumerable<T> values) | |
return firstString ?? string.Empty; | ||
} | ||
|
||
StringBuilder result = StringBuilderCache.Acquire(); | ||
Span<char> initialBuffer = stackalloc char[64]; | ||
var result = new ValueStringBuilder(initialBuffer); | ||
|
||
result.Append(firstString); | ||
|
||
|
@@ -210,7 +212,7 @@ public static string Concat<T>(IEnumerable<T> values) | |
} | ||
while (en.MoveNext()); | ||
|
||
return StringBuilderCache.GetStringAndRelease(result); | ||
return result.ToString(); | ||
} | ||
} | ||
} | ||
|
@@ -232,7 +234,8 @@ public static string Concat(IEnumerable<string?> values) | |
return firstValue ?? string.Empty; | ||
} | ||
|
||
StringBuilder result = StringBuilderCache.Acquire(); | ||
Span<char> initialBuffer = stackalloc char[64]; | ||
var result = new ValueStringBuilder(initialBuffer); | ||
result.Append(firstValue); | ||
|
||
do | ||
|
@@ -241,7 +244,7 @@ public static string Concat(IEnumerable<string?> values) | |
} | ||
while (en.MoveNext()); | ||
|
||
return StringBuilderCache.GetStringAndRelease(result); | ||
return result.ToString(); | ||
} | ||
} | ||
|
||
|
@@ -522,12 +525,13 @@ private static string FormatHelper(IFormatProvider? provider, string format, Par | |
if (format == null) | ||
throw new ArgumentNullException(nameof(format)); | ||
|
||
ValueStringBuilder sb = new ValueStringBuilder(format.Length + args.Length * 8); | ||
Span<char> initialBuffer = stackalloc char[64]; | ||
var sb = new ValueStringBuilder(initialBuffer); | ||
|
||
sb.EnsureCapacity(format.Length + args.Length * 8); | ||
sb.AppendFormatHelper(provider, format, args); | ||
|
||
string returnValue = sb.ToString(); | ||
sb.Dispose(); | ||
return returnValue; | ||
return sb.ToString(); | ||
} | ||
|
||
public string Insert(int startIndex, string value) | ||
|
@@ -648,7 +652,9 @@ public static string Join(string? separator, IEnumerable<string?> values) | |
} | ||
|
||
// Null separator and values are handled by the StringBuilder | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Per the other conversation we should probably change Append to accept nulls again. But if we don't, this comment will need to be updated. |
||
StringBuilder result = StringBuilderCache.Acquire(); | ||
Span<char> initialBuffer = stackalloc char[64]; | ||
var result = new ValueStringBuilder(initialBuffer); | ||
|
||
result.Append(firstValue); | ||
|
||
do | ||
|
@@ -658,7 +664,7 @@ public static string Join(string? separator, IEnumerable<string?> values) | |
} | ||
while (en.MoveNext()); | ||
|
||
return StringBuilderCache.GetStringAndRelease(result); | ||
return result.ToString(); | ||
} | ||
} | ||
|
||
|
@@ -693,7 +699,9 @@ private static unsafe string JoinCore(char* separator, int separatorLength, obje | |
return firstString ?? string.Empty; | ||
} | ||
|
||
StringBuilder result = StringBuilderCache.Acquire(); | ||
Span<char> initialBuffer = stackalloc char[64]; | ||
var result = new ValueStringBuilder(initialBuffer); | ||
|
||
result.Append(firstString); | ||
|
||
for (int i = 1; i < values.Length; i++) | ||
|
@@ -706,7 +714,7 @@ private static unsafe string JoinCore(char* separator, int separatorLength, obje | |
} | ||
} | ||
|
||
return StringBuilderCache.GetStringAndRelease(result); | ||
return result.ToString(); | ||
} | ||
|
||
private static unsafe string JoinCore<T>(char* separator, int separatorLength, IEnumerable<T> values) | ||
|
@@ -741,7 +749,8 @@ private static unsafe string JoinCore<T>(char* separator, int separatorLength, I | |
return firstString ?? string.Empty; | ||
} | ||
|
||
StringBuilder result = StringBuilderCache.Acquire(); | ||
Span<char> initialBuffer = stackalloc char[64]; | ||
var result = new ValueStringBuilder(initialBuffer); | ||
|
||
result.Append(firstString); | ||
|
||
|
@@ -757,7 +766,7 @@ private static unsafe string JoinCore<T>(char* separator, int separatorLength, I | |
} | ||
while (en.MoveNext()); | ||
|
||
return StringBuilderCache.GetStringAndRelease(result); | ||
return result.ToString(); | ||
} | ||
} | ||
|
||
|
@@ -1005,7 +1014,9 @@ private unsafe string ReplaceCore(string oldValue, string? newValue, CultureInfo | |
newValue ??= string.Empty; | ||
|
||
CultureInfo referenceCulture = culture ?? CultureInfo.CurrentCulture; | ||
StringBuilder result = StringBuilderCache.Acquire(); | ||
Span<char> initialBuffer = stackalloc char[64]; | ||
var result = new ValueStringBuilder(initialBuffer); | ||
result.EnsureCapacity(this.Length); | ||
|
||
int startIndex = 0; | ||
int index = 0; | ||
|
@@ -1021,7 +1032,7 @@ private unsafe string ReplaceCore(string oldValue, string? newValue, CultureInfo | |
if (index >= 0) | ||
{ | ||
// append the unmodified portion of string | ||
result.Append(this, startIndex, index - startIndex); | ||
result.Append(this.AsSpan(startIndex, index - startIndex)); | ||
|
||
// append the replacement | ||
result.Append(newValue); | ||
|
@@ -1034,16 +1045,16 @@ private unsafe string ReplaceCore(string oldValue, string? newValue, CultureInfo | |
// small optimization, | ||
// if we have not done any replacements, | ||
// we will return the original string | ||
StringBuilderCache.Release(result); | ||
result.Dispose(); | ||
return this; | ||
} | ||
else | ||
{ | ||
result.Append(this, startIndex, this.Length - startIndex); | ||
result.Append(this.AsSpan(startIndex, this.Length - startIndex)); | ||
} | ||
} while (index >= 0); | ||
|
||
return StringBuilderCache.GetStringAndRelease(result); | ||
return result.ToString(); | ||
} | ||
|
||
// Replaces all instances of oldChar with newChar. | ||
|
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.
Can we remove StringBuilderCache.cs from corelib as well, or are there still other uses?
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's still a few other uses; they are more problematic ones as one method gets the cached string builder, then it returns it, passes to other methods, then releases it later. Didn't tackle them as it would over-complicate the review.