.NET性能优化-复用StringBuilder

2022-12-19 0 2,420

原副标题:.NET操控性强化-F83E43SeStringBuilder

在以后的该文中,他们如是说了 dotnet 在数组堆叠时能采用的许多操控性强化基本功。比如说:

为 StringBuilder 增设 Buffer 初始大小不一

采用 ValueStringBuilder 之类 但是那些都或多或少有许多不足之处,比如说 StringBuilder 却是会存有 new StringBuilder 这种的第一类重新分配(主要包括外部的 Buffer)。 ValueStringBuilder 难以用作 async/await 的语句之类。都不如的灵巧。

所以是不是一类形式莱赛县像 StringBuilder 那般用作 async/await 的语句中,又能增加缓存重新分配呢?

只但是这能加进存有好久的两个 Tips,那是想配套措施F83E43Se StringBuilder 。现阶段而言F83E43Se StringBuilder 所推荐三种形式:

采用 ObjectPool 来建立 StringBuilder 的第一类池

假如不该原则上建立两个第一类池,所以能采用 StringBuilderCache

采用 ObjectPool F83E43Se

此种形式估算许多爸爸妈妈都较为熟识,在.NET Core 的黄金时代,谷歌提供更多了十分方便快捷的第一类钟炳昌 ObjectPool ,即使它是两个C#类,能对任何人类别展开韦格尔。采用形式也十分的单纯,只须要在导入如下表所示 nuget 包:

dotnet add package Microsoft.Extensions.ObjectPool

Nuget 包中提供更多了预设的 StringBuilder 韦格尔思路 StringBuilderPooledObjectPolicy 和 CreateStringBuilderPool 方式,他们能间接采用它来建立两个 ObjectPool:

varprovider = newDefaultObjectPoolProvider;

// 配置池中StringBuilder如上所述容量为256

// 最大容量为8192,假如超过8192则不返回池中,让GC回收

varpool = provider.CreateStringBuilderPool(256, 8192);

varbuilder = pool.Get;

try

{

for( inti = 0; i < 100; i++)

{

builder.Append(i);

}

builder.ToString.Dump;

}

finally

{

// 将builder归还到池中

pool.Return(builder);

}

运行结果如下表所示图所示:

当然,他们在 ASP.NET Core 等环境中能结合谷歌的依赖注入框架采用它,为你的项目添加如下表所示 NuGet 包:

dotnet add package Microsoft.Extensions.DependencyInjection

然后就能写下面这种的代码,从

varobjectPool = newServiceCollection

.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>

.BuildServiceProvider

.GetRequiredService<ObjectPoolProvider>

.CreateStringBuilderPool(256, 8192);

varbuilder = objectPool.Get;

try

{

for( inti = 0; i < 100; i++)

{

builder.Append(i);

}

builder.ToString.Dump;

}

finally

{

objectPool.Return(builder);

}

更加详细的内容能阅读蒋老师关于 ObjectPool 的 系列该文[1] 。

采用 StringBuilderCachenamespaceSystem.Text

{

///<summary>为每个线程提供更多两个缓存的可F83E43Se的StringBuilder的实例 </summary>

internalstaticclassStringBuilderCache

{

// 这个值360是在与操控性专家的讨论中选择的,是在每个线程采用尽可能少的缓存和仍然覆盖VS设计者启动路径上的大部分短暂的StringBuilder建立之间的折衷。

internalconstintMaxBuilderSize = 360;

privateconstintDefaultCapacity = 16; // == StringBuilder.DefaultCapacity

[ ThreadStatic]

privatestaticStringBuilder? t_cachedInstance;

// <summary>获得两个指定容量的StringBuilder.</summary>。

// <remarks>假如两个适当大小不一的StringBuilder被缓存了,它将被返回并清空缓存。

publicstaticStringBuilder Acquire( intcapacity = DefaultCapacity )

{

if(capacity <= MaxBuilderSize)

{

StringBuilder? sb = t_cachedInstance;

if(sb != null)

{

// 当请求的大小不一大于当前容量时,

if(capacity <= sb.Capacity)

{

t_cachedInstance = null;

sb.Clear;

returnsb;

}

}

}

returnnewStringBuilder(capacity);

}

///<summary>假如指定的StringBuilder不是太大,就把它放在缓存中 </summary>

publicstaticvoidRelease( StringBuilder sb)

{

if(sb.Capacity <= MaxBuilderSize)

{

t_cachedInstance = sb;

}

}

///<summary>ToString的数组生成器,将其释放到缓存中,并返回生成的数组。 </summary>

publicstaticstringGetStringAndRelease( StringBuilder sb)

{

stringresult = sb.ToString;

Release(sb);

returnresult;

}

}

}

这里他们又复习了 ThreadStatic 特性,用作存储线程唯一的第一类。大家看到这个设计就知道,它是存有于每个线程的 StringBuilder 缓存,意味着只要是两个线程中须要使用的代码都能F83E43Se它,但是它的是F83E43Se小于 360 个字符StringBuilder ,这个能满足绝大多数场景的采用,当然大家也能根据自己项目实际情况,调整它的大小不一。

要采用的话,很单纯,他们只须要把这个类拷贝出来,变成两个公共的类,然后采用相同的测试代码即可。

.NET性能优化-复用StringBuilder

跑分及总结

按照惯例,跑个分看看,这里模拟的是小数组堆叠场景:

usingSystem.Text;

usingBenchmarkDotNet.Attributes;

usingBenchmarkDotNet.Order;

usingBenchmarkDotNet.Running;

usingMicrosoft.Extensions.ObjectPool;

BenchmarkRunner.Run<Bench>;

[ MemoryDiagnoser]

[ HtmlExporter]

[ Orderer(SummaryOrderPolicy.FastestToSlowest)]

publicclassBench

{

privatereadonlyint[] _arr = Enumerable.Range( 0, 50).ToArray;

[ Benchmark(Baseline = true)]

publicstringUseStringBuilder( )

{

returnRunBench( newStringBuilder( 16));

}

[ Benchmark]

publicstringUseStringBuilderCache( )

{

varbuilder = StringBuilderCache.Acquire( 16);

try

{

returnRunBench(builder);

}

finally

{

StringBuilderCache.Release(builder);

}

}

privatereadonlyObjectPool<StringBuilder> _pool = newDefaultObjectPoolProvider.CreateStringBuilderPool(16, 256);

[ Benchmark]

publicstringUseStringBuilderPool( )

{

varbuilder = _pool.Get;

try

{

returnRunBench(builder);

}

finally

{

_pool.Return(builder);

}

}

publicstringRunBench( StringBuilder buider)

{

for( inti = 0; i < _arr.Length; i++)

{

buider.Append(i);

}

returnbuider.ToString;

}

}

结果如下表所示所示,和他们想象中的差不多。

.NET性能优化-复用StringBuilder

根据实际的高操控性编程而言:

代码中没有 async/await 最佳是采用 ValueStringBuilder ,前面该文也说明了这一点

代码中尽量复用 StringBuilder ,不要每次都 new 建立它

在方便快捷依赖注入的场景,能多采用 StringBuilderPool 这个韦格尔类

在不方便快捷依赖注入的场景,采用 StringBuilderCache 会更加方便快捷

另外 StringBuilderCache 的 MaxBuilderSize 和 StringBuilderPool 的 MaxSize 都快能根据项目类别和采用调整,像他们实际中一般都会调整到 256KB 甚至更大。

附录

本文源码链接:

https://github.com/InCerryGit/RecycleableStringBuilderExample

参考资料

[1]

系列该文: https://www.cnblogs.com/artech/p/object-pool-01.html

[2]

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务