Reduce allocation in Index/Range.ToString#21755
Conversation
GrabYourPitchforks
left a comment
There was a problem hiding this comment.
Nit: should we have a debug assert after the calls to TryFormat? I could go either way on this.
| ((uint)Value).TryFormat(span.Slice(1), out int charsWritten); | ||
| span[0] = '^'; | ||
| return new string(span.Slice(0, charsWritten + 1)); | ||
| } |
There was a problem hiding this comment.
As an alternative - modified version of UInt32ToDecStr without CountDigits, etc
private static unsafe string UInt32ToDecStr(uint value)
{
char* buf = stackalloc char[11];
int i = 11;
do
{
uint div = value / 10;
buf[--i] = (char)('0' + value - (div * 10));
value = div;
}
while (value != 0);
buf[--i] = '^';
return new string(buf, i, 11 - i);
}There was a problem hiding this comment.
Sure. But:
a) I personally don't think it's worth all that extra duplication.
b) That actually allocates more in what may be a common case of small values like an index for 0 or 1, as it'll bypass that small-number string cache that UInt32ToDecStr uses. (EDIT: Though I see now you're proposing only using this for the FromEnd case, in which case that cache wouldn't apply anyway.)
c) It's not clear to me yet whether these methods really need to be hyper-optimized; I suspect they don't.
There was a problem hiding this comment.
@stephentoub yeah, surely doesn't make any sense, I was actually curious if your technique with stackalloc[11] could be used in UInt32ToDecStr, but looks like GetDigitsCount+string.FastAllocate is better 🙂
Thanks, Levi. I considered that, too, but was on the fence and ended up removing them to avoid the clutter. Since you also suggested it, I'll add them back. |
|
@dotnet-bot test Windows_NT arm Cross Debug Innerloop Build please |
* Reduce allocation in Index/Range.ToString * Address PR feedback Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
* Reduce allocation in Index/Range.ToString * Address PR feedback Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
* Reduce allocation in Index/Range.ToString * Address PR feedback Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
* Reduce allocation in Index/Range.ToString * Address PR feedback Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
* Reduce allocation in Index/Range.ToString * Address PR feedback Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
* Reduce allocation in Index/Range.ToString * Address PR feedback Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
* Reduce allocation in Index/Range.ToString * Address PR feedback Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
* Reduce allocation in Index/Range.ToString * Address PR feedback Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com> (cherry picked from commit ceee48b)
* Reduce allocation in Index/Range.ToString * Address PR feedback Commit migrated from dotnet/coreclr@88eb93a
Index.ToString first calls ToString on the integer value, and then if it's FromEnd, concatenates with "^", resulting in an extra string allocation in the FromEnd case. Range.ToString then builds on that, not only generating the intermediate Index strings, but boxing those Index structs to call string.Concat(object, object, object).
This change removes the extra allocation, so there's at most one allocation for Index/Range.ToString, the resulting string itself. For Index, there might still be zero allocations in the case where it just delegates to Value.ToString(), and it's able to return a cached string for a single-digit integer value.
Before:
After:
cc: @tarekgh, @jkotas