mirror of
https://github.com/Kae7in/perfview
synced 2026-08-09 12:09:44 +00:00
Reduce allocations in TraceActivity.Name
Introduces StringBuilderCache
This commit is contained in:
@@ -123,6 +123,7 @@
|
||||
<Compile Include="TraceLog.cs" />
|
||||
<Compile Include="Utilities\command.cs" />
|
||||
<Compile Include="TraceUtilities\HistoryDictionary.cs" />
|
||||
<Compile Include="Utilities\StringBuilderCache.cs" />
|
||||
<Compile Include="WPPTraceEventParser.cs" />
|
||||
<Compile Include="ZippedETL.cs" />
|
||||
<Compile Include="_README.cs" />
|
||||
|
||||
@@ -8548,6 +8548,32 @@ namespace Microsoft.Diagnostics.Tracing.Etlx
|
||||
FxTimer = 34, // FxTransfer + kind(1)
|
||||
}
|
||||
|
||||
private static string ActivityKindToString(ActivityKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ActivityKind.Invalid: return nameof(ActivityKind.Invalid);
|
||||
case ActivityKind.Initial: return nameof(ActivityKind.Initial);
|
||||
case ActivityKind.TaskScheduled: return nameof(ActivityKind.TaskScheduled);
|
||||
case ActivityKind.TaskStarted: return nameof(ActivityKind.TaskStarted);
|
||||
case ActivityKind.AwaitTaskScheduled: return nameof(ActivityKind.AwaitTaskScheduled);
|
||||
case ActivityKind.ClrThreadStart: return nameof(ActivityKind.ClrThreadStart);
|
||||
case ActivityKind.ClrThreadPool: return nameof(ActivityKind.ClrThreadPool);
|
||||
case ActivityKind.ClrIOThreadPool: return nameof(ActivityKind.ClrIOThreadPool);
|
||||
case ActivityKind.FxThreadPool: return nameof(ActivityKind.FxThreadPool);
|
||||
case ActivityKind.FxTransfer: return nameof(ActivityKind.FxTransfer);
|
||||
case ActivityKind.FxAsyncIO: return nameof(ActivityKind.FxAsyncIO);
|
||||
case ActivityKind.FxWinRTDispatch: return nameof(ActivityKind.FxWinRTDispatch);
|
||||
case ActivityKind.Implied: return nameof(ActivityKind.Implied);
|
||||
case ActivityKind.TaskWait: return nameof(ActivityKind.TaskWait);
|
||||
case ActivityKind.TaskWaitSynchronous: return nameof(ActivityKind.TaskWaitSynchronous);
|
||||
case ActivityKind.FxTimer: return nameof(ActivityKind.FxTimer);
|
||||
default:
|
||||
Debug.Fail("Missing ActivityKind case statement.");
|
||||
return kind.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>A trace-wide unique id identifying an activity</summary>
|
||||
public ActivityIndex Index { get { return activityIndex; } }
|
||||
/// <summary>The activity that initiated or caused the current one</summary>
|
||||
@@ -8598,12 +8624,29 @@ namespace Microsoft.Diagnostics.Tracing.Etlx
|
||||
{
|
||||
get
|
||||
{
|
||||
string name = IsThreadActivity ? "ThreadActivity" : ((rawID >> 32) & 1) != 0 ? "Activity (concurrent)" : "Activity (continuation)";
|
||||
if (Thread == null)
|
||||
return string.Format("<{0} Index=\"{1}\" RawID=\"0x{2:x}\"/>", name, (int)Index, rawID);
|
||||
// PERF: Hand-optimized string.Format to minimize allocations
|
||||
var sb = StringBuilderCache.Acquire();
|
||||
sb.Append('<');
|
||||
sb.Append(IsThreadActivity ? "ThreadActivity" : ((rawID >> 32) & 1) != 0 ? "Activity (concurrent)" : "Activity (continuation)");
|
||||
sb.Append(" Index=\"");
|
||||
sb.Append((int)Index);
|
||||
|
||||
return string.Format("<{0} Index=\"{1}\" Thread=\"{2}\" Create=\"{3:f3}\" Start=\"{4:f3}\" kind=\"{5}\" RawID=\"0x{6:x}\"/>",
|
||||
name, (int)Index, Thread.VerboseThreadName, CreationTimeRelativeMSec, StartTimeRelativeMSec, kind, rawID);
|
||||
if (Thread != null)
|
||||
{
|
||||
sb.Append("\" Thread=\"");
|
||||
sb.Append(Thread.VerboseThreadName);
|
||||
sb.Append("\" Create=\"");
|
||||
sb.Append(CreationTimeRelativeMSec.ToString("f3"));
|
||||
sb.Append("\" Start=\"");
|
||||
sb.Append(StartTimeRelativeMSec.ToString("f3"));
|
||||
sb.Append("\" kind=\"");
|
||||
sb.Append(ActivityKindToString(kind));
|
||||
}
|
||||
|
||||
sb.Append("\" RawID=\"0x");
|
||||
sb.Append(rawID.ToString("x"));
|
||||
sb.Append("\"/>");
|
||||
return StringBuilderCache.GetStringAndRelease(sb);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
/*============================================================
|
||||
**
|
||||
** Class: StringBuilderCache
|
||||
**
|
||||
** Purpose: provide a cached reusable instance of stringbuilder
|
||||
** per thread it's an optimisation that reduces the
|
||||
** number of instances constructed and collected.
|
||||
**
|
||||
** Acquire - is used to get a string builder to use of a
|
||||
** particular size. It can be called any number of
|
||||
** times, if a stringbuilder is in the cache then
|
||||
** it will be returned and the cache emptied.
|
||||
** subsequent calls will return a new stringbuilder.
|
||||
**
|
||||
** A StringBuilder instance is cached in
|
||||
** Thread Local Storage and so there is one per thread
|
||||
**
|
||||
** Release - Place the specified builder in the cache if it is
|
||||
** not too big.
|
||||
** The stringbuilder should not be used after it has
|
||||
** been released.
|
||||
** Unbalanced Releases are perfectly acceptable. It
|
||||
** will merely cause the runtime to create a new
|
||||
** stringbuilder next time Acquire is called.
|
||||
**
|
||||
** GetStringAndRelease
|
||||
** - ToString() the stringbuilder, Release it to the
|
||||
** cache and return the resulting string
|
||||
**
|
||||
===========================================================*/
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Diagnostics.Tracing.Utilities
|
||||
{
|
||||
internal static class StringBuilderCache
|
||||
{
|
||||
// The value 360 was chosen in discussion with performance experts as a compromise between using
|
||||
// as litle memory (per thread) as possible and still covering a large part of short-lived
|
||||
// StringBuilder creations on the startup path of VS designers.
|
||||
private const int MAX_BUILDER_SIZE = 360;
|
||||
|
||||
[ThreadStatic]
|
||||
private static StringBuilder CachedInstance;
|
||||
|
||||
public static StringBuilder Acquire(int capacity = 16 /*StringBuilder.DefaultCapacity*/)
|
||||
{
|
||||
if (capacity <= MAX_BUILDER_SIZE)
|
||||
{
|
||||
StringBuilder sb = StringBuilderCache.CachedInstance;
|
||||
if (sb != null)
|
||||
{
|
||||
// Avoid stringbuilder block fragmentation by getting a new StringBuilder
|
||||
// when the requested size is larger than the current capacity
|
||||
if (capacity <= sb.Capacity)
|
||||
{
|
||||
StringBuilderCache.CachedInstance = null;
|
||||
sb.Clear();
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new StringBuilder(capacity);
|
||||
}
|
||||
|
||||
public static void Release(StringBuilder sb)
|
||||
{
|
||||
if (sb.Capacity <= MAX_BUILDER_SIZE)
|
||||
{
|
||||
StringBuilderCache.CachedInstance = sb;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetStringAndRelease(StringBuilder sb)
|
||||
{
|
||||
string result = sb.ToString();
|
||||
Release(sb);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user