.NET性能優化-復用StringBuilder

在之前的文章中,我們介紹了 dotnet 在字符串拼接時可以使用的一些性能優化技巧。比如:

  • StringBuilder設置 Buffer 初始大小

  • 使用ValueStringBuilder等等 不過這些都多多少少有一些局限性,比如StringBuilder還是會存在new StringBuilder()這樣的對象分配(包括內部的 Buffer)。ValueStringBuilder無法用于async/await的上下文等等。都不夠的靈活。

那么有沒有一種方式既能像StringBuilder那樣用于async/await的上下文中,又能減少內存分配呢?

其實這可以用到存在很久的一個 Tips,那就是想辦法復用StringBuilder。目前來說復用StringBuilder推薦兩種方式:

  • 使用 ObjectPool 來創建StringBuilder的對象池

  • 如果不想單獨創建一個對象池,那么可以使用StringBuilderCache

使用 ObjectPool 復用

這種方式估計很多小伙伴都比較熟悉,在.NET Core 的時代,微軟提供了非常方便的對象池類ObjectPool,因為它是一個泛型類,可以對任何類型進行池化。使用方式也非常的簡單,只需要在引入如下 nuget 包:

dotnet?add?package?Microsoft.Extensions.ObjectPool

Nuget 包中提供了默認的StringBuilder池化策略StringBuilderPooledObjectPolicyCreateStringBuilderPool()方法,我們可以直接使用它來創建一個 ObjectPool:

var?provider?=?new?DefaultObjectPoolProvider();
//?配置池中StringBuilder初始容量為256
//?最大容量為8192,如果超過8192則不返回池中,讓GC回收
var?pool?=?provider.CreateStringBuilderPool(256,?8192);var?builder?=?pool.Get();
try
{for?(int?i?=?0;?i?<?100;?i++){builder.Append(i);}builder.ToString().Dump();
}
finally
{//?將builder歸還到池中pool.Return(builder);
}

運行結果如下圖所示:df308263445977fc091e09625d8395f8.png

當然,我們在 ASP.NET Core 等環境中可以結合微軟的依賴注入框架使用它,為你的項目添加如下 NuGet 包:

dotnet?add?package?Microsoft.Extensions.DependencyInjection

然后就可以寫下面這樣的代碼,從容器中獲取ObjectPoolProvider達到同樣的效果:

var?objectPool?=?new?ServiceCollection().AddSingleton<ObjectPoolProvider,?DefaultObjectPoolProvider>().BuildServiceProvider().GetRequiredService<ObjectPoolProvider>().CreateStringBuilderPool(256,?8192);var?builder?=?objectPool.Get();
try
{for?(int?i?=?0;?i?<?100;?i++){builder.Append(i);}builder.ToString().Dump();
}
finally
{objectPool.Return(builder);
}

更加詳細的內容可以閱讀蔣老師關于ObjectPool的系列文章[1]

使用 StringBuilderCache

另外一個方案就是在.NET 中存在很久的類,如果大家翻閱過.NET 的一些代碼,在有字符串拼接的場景可以經常見到它的身影。但是它和ValueStringBuilder一樣不是公開可用的,這個類叫StringBuilderCache5930a57094a8d45c4c3dcd4c17b85afe.png下方所示就是它的源碼,源碼鏈接點擊這里[2]

namespace?System.Text
{///?<summary>為每個線程提供一個緩存的可復用的StringBuilder的實例</summary>internal?static?class?StringBuilderCache{//?這個值360是在與性能專家的討論中選擇的,是在每個線程使用盡可能少的內存和仍然覆蓋VS設計者啟動路徑上的大部分短暫的StringBuilder創建之間的折衷。internal?const?int?MaxBuilderSize?=?360;private?const?int?DefaultCapacity?=?16;?//?==?StringBuilder.DefaultCapacity[ThreadStatic]private?static?StringBuilder??t_cachedInstance;//?<summary>獲得一個指定容量的StringBuilder.</summary>。//?<remarks>如果一個適當大小的StringBuilder被緩存了,它將被返回并清空緩存。public?static?StringBuilder?Acquire(int?capacity?=?DefaultCapacity){if?(capacity?<=?MaxBuilderSize){StringBuilder??sb?=?t_cachedInstance;if?(sb?!=?null){//?當請求的大小大于當前容量時,//?通過獲取一個新的StringBuilder來避免Stringbuilder塊的碎片化if?(capacity?<=?sb.Capacity){t_cachedInstance?=?null;sb.Clear();return?sb;}}}return?new?StringBuilder(capacity);}///?<summary>如果指定的StringBuilder不是太大,就把它放在緩存中</summary>public?static?void?Release(StringBuilder?sb){if?(sb.Capacity?<=?MaxBuilderSize){t_cachedInstance?=?sb;}}///?<summary>ToString()的字符串生成器,將其釋放到緩存中,并返回生成的字符串。</summary>public?static?string?GetStringAndRelease(StringBuilder?sb){string?result?=?sb.ToString();Release(sb);return?result;}}
}

這里我們又復習了ThreadStatic特性,用于存儲線程唯一的對象。大家看到這個設計就知道,它是存在于每個線程的StringBuilder緩存,意味著只要是一個線程中需要使用的代碼都可以復用它,不過它的是復用小于 360 個字符StringBuilder,這個能滿足絕大多數場景的使用,當然大家也可以根據自己項目實際情況,調整它的大小。

要使用的話,很簡單,我們只需要把這個類拷貝出來,變成一個公共的類,然后使用相同的測試代碼即可。fd188e65f9e0c856f84d1826e5172a2e.png

跑分及總結

按照慣例,跑個分看看,這里模擬的是小字符串拼接場景:

using?System.Text;
using?BenchmarkDotNet.Attributes;
using?BenchmarkDotNet.Order;
using?BenchmarkDotNet.Running;
using?Microsoft.Extensions.ObjectPool;BenchmarkRunner.Run<Bench>();[MemoryDiagnoser]
[HtmlExporter]
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
public?class?Bench
{private?readonly?int[]?_arr?=?Enumerable.Range(0,50).ToArray();[Benchmark(Baseline?=?true)]public?string?UseStringBuilder(){return?RunBench(new?StringBuilder(16));}[Benchmark]public?string?UseStringBuilderCache(){var?builder?=?StringBuilderCache.Acquire(16);try{return?RunBench(builder);}finally{StringBuilderCache.Release(builder);}}private?readonly?ObjectPool<StringBuilder>?_pool?=?new?DefaultObjectPoolProvider().CreateStringBuilderPool(16,?256);[Benchmark]public?string?UseStringBuilderPool(){var?builder?=?_pool.Get();try{return?RunBench(builder);}finally{_pool.Return(builder);}}public?string?RunBench(StringBuilder?buider){for?(int?i?=?0;?i?<?_arr.Length;?i++){buider.Append(i);}return?buider.ToString();}
}

結果如下所示,和我們想象中的差不多。20c84af772778cdca2824051e102f2c2.png

根據實際的高性能編程來說:

  • 代碼中沒有async/await最佳是使用ValueStringBuilder,前面文章也說明了這一點

  • 代碼中盡量復用StringBuilder,不要每次都new()創建它

  • 在方便依賴注入的場景,可以多使用StringBuilderPool這個池化類

  • 在不方便依賴注入的場景,使用StringBuilderCache會更加方便

另外StringBuilderCacheMaxBuilderSizeStringBuilderPoolMaxSize都快可以根據項目類型和使用調整,像我們實際中一般都會調整到 256KB 甚至更大。

附錄

本文源碼鏈接:

https://github.com/InCerryGit/RecycleableStringBuilderExample

參考資料

[1]

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

[2]

源碼鏈接點擊這里: https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/System/Text/StringBuilderCache.cs

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281360.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281360.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281360.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何使用vue組件搭建網頁并打包發布

vue組件化項目搭建及編譯打包發布引言開發環境開發環境介紹開發環境安裝使用模板創建項目編譯及打包發布引言 最近開始學習Vue&#xff0c;Vue 是一個前端框架&#xff0c;特點是數據綁定和組件化。網上很多教程&#xff0c;數據綁定和組件學習起來也都不困難&#xff0c;但是…

MyBatis 分頁插件 PageHelper

插件官網 簡介&#xff1a; 支持常見的 12 種數據庫。Oracle,MySql,MariaDB,SQLite,DB2,PostgreSQL,SqlServer 等&#xff1b; 支持常見的RowBounds(PageRowBounds)&#xff0c;PageHelper.startPage 方法調用&#xff0c;Mapper 接口參數調用&#xff1b; 獲取maven配置 示例&…

Facebook揭秘其應用測試平臺,并開源核心技術

本周&#xff0c;Facebook讓一群記者參觀了它位于俄勒岡州普賴恩維爾市的數據中心&#xff0c;在這個過程中也展示了他們的一個移動應用測試實驗室&#xff0c;用來測試Facebook app、 Messenger 和Instagram。 目前 這個實驗室總共含有60個機架&#xff0c;每個機架上放置32臺…

責任鏈模式 職責鏈模式 Chain of Responsibility Pattern 行為型 設計模式(十七)

責任鏈模式&#xff08;Chain of Responsibility Pattern&#xff09;職責鏈模式意圖 使多個對象都有機會處理請求&#xff0c;從而避免請求的發送者和接受者之間的耦合關系將這些對象連接成一條鏈&#xff0c;并沿著這條鏈傳遞請求&#xff0c;直到有一個對象處理它為止。責任…

YOLOv7 在 ML.NET 中使用 ONNX 檢測對象

本文介紹如何在 ML.NET 中使用 YOLOv7 的 ONNX 模型來檢測圖像中的對象。什么是 YOLOYOLO&#xff08;You Only Look Once&#xff09;是一種先進的實時目標檢測系統。它是一個在COCO數據集上預訓練的物體檢測架構和模型系列&#xff0c;其版本也是在不斷優化更新。2022年7月&a…

Vue中render: h = h(App)的含義

原文地址&#xff1a;Explanation for render: h > h(App) please 翻譯如下&#xff1a; render: h > h(App) 是下面內容的縮寫&#xff1a; render: function (createElement) {return createElement(App); }進一步縮寫為(ES6 語法)&#xff1a; render (createElem…

NppFTP小插件的使用

大家在Linux系統中配置運行環境時&#xff0c;一定會遇到相關配置文件的修改&#xff0c;雖說在Linux系統中可以使用vi,vim的命令進行文本編輯&#xff0c;但是操作起來還是沒有在Windows系統中用的爽&#xff0c;特別是操作大文本量的文件時。Notepad里提供了一個小插件&#…

『實戰』使用Excel催化劑二維碼功能批量生成帶不同圖案二維碼。

熟悉Excel催化劑的老讀者都知道&#xff0c;每逢圖書大促&#xff0c;筆者都會向出版社申請優惠碼優惠券來惠及廣大粉絲。當然公眾號上幫出版社推薦圖書&#xff0c;會有傭金回報&#xff0c;這也是筆者唯一能接受的推廣方式。公眾號自創立以來&#xff0c;沒有接過軟文&#x…

springboot ElasticSearch 簡單的全文檢索高亮

前陣子和張三豐聊天提到了es。這次正好有機會學習并使用 首先引入依賴 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> 配置文件 spring.data.…

JavaScriptSerializer類 對象序列化為JSON,JSON反序列化為對象

JavaScriptSerializer 類由異步通信層內部使用&#xff0c;用于序列化和反序列化在瀏覽器和 Web 服務器之間傳遞的數據。說白了就是能夠直接將一個C#對象傳送到前臺頁面成為javascript對象。要添加System.Web.Extensions.dll的引用。該類位于System.Web.Script.Serialization命…

nodejs 實踐:express 最佳實踐(六) express 自省獲得所有的路由

nodejs 實踐&#xff1a;express 最佳實踐(六) express 自省獲得所有的路由 某些情況下&#xff0c;你需要知道你的應用有多少路由&#xff0c;這在 express 中沒有方法可以。因此我這邊曲線了一下&#xff0c;做成了一個函數進行處理。遍歷所有的方法進行處理。 代碼 const _ …

使用vue組件搭建網頁應用

使用vue組件搭建網頁應用搭建開發環境開發組件搭建開發環境 搭建一個 vue 項目最快的方式就是使用 vue cli 腳手架進行初始化&#xff0c;包含了所有完整的依賴及開發配置。 首先全局安裝 vue cli&#xff0c;打開 cmd 命令提示符 或者 power shell&#xff0c;輸入以下命令&…

重新整理 .net core 實踐篇 —linux上排查問題實用工具 [外篇]

前言介紹下面幾個工具:Lldbcreatedumpdotnet-dumpdotnet-gcdumpdotnet-symbolProcdump該文的前置篇為:https://www.cnblogs.com/aoximin/p/16839812.html獻給初學者&#xff0c;這篇就只介紹下看下日志和lldb&#xff0c;畢竟東西太多了。正文我以官網的例子作為演示&#xff1…

Office 365離線安裝

Office 365除了可以在線安裝外&#xff0c;還可以進行離線安裝&#xff0c;但激活還是需要連接互聯網的喲首先下載Office部署工具https://www.microsoft.com/en-us/download/details.aspx?id49117 下載文件后&#xff0c;運行自解壓縮可執行文件&#xff0c;其中包含 Office 部…

reduceByKey和groupByKey區別與用法

2019獨角獸企業重金招聘Python工程師標準>>> 在Spar看中&#xff0c;我們知道一切的操作都是基于RDD的。在使用中&#xff0c;RDD有一種非常特殊也是非常實用的format——pair RDD&#xff0c;即RDD的每一行是&#xff08;key, value&#xff09;的格式。這種格式很…

python自動化測試-D6-學習筆記之一(常用模塊補充datetime模塊)

# datetime 模塊import datetimeprint(datetime.datetime.today()) #當前日期&#xff0c;到秒 打印結果&#xff1a;2018-01-21 10:23:46.034410print(datetime.datetime.now()) # 當前日期&#xff0c;到秒 打印結果&#xff1a;2018-01-21 10:23:46.034410print(datetime.d…

Vue如何在data中正確引入圖片路徑

方法一&#xff1a;將圖片資源放入項目 /static 目錄下&#xff0c;使用絕對或相對路徑引用即可 // 文件結構 |-- src | |-- components | | |-- banner.vue |-- static | |-- images | | |-- pic.jpg<template><div id"banner"><img :s…

軟件工程的第一性原理丨SmartIDE

作者&#xff1a;徐磊原文地址&#xff1a;https://smartide.cn/zh/blog/2022-1022-software-engineering/徐磊英捷創軟科技&#xff08;北京&#xff09;有限公司創始?/?席架構師 / CEO / SmartIDE開源項?創始?。微軟最有價值專家MVP&#xff0c;微軟區域技術總監&#xf…

排序算法之快速排序詳解

一、算法介紹 快速排序&#xff1a;快速排序的基本思想是通過一次排序將等待的記錄分成兩個獨立的部分&#xff0c;其中一部分記錄的關鍵字小于另一部分的關鍵字。C部分的快速排序一直持續到整個序列被排序。 任取一個元素 (如第一個) 為中心提出所有小于它的元素&#xff0c;并…

openstack 中國聯盟公開課參會總結

主流趨勢 1. openstack defcore 互操作性認證。打通不同的openstack 廠商之間的連接2. 首批OpenStack管理員認證(COA)將于2016年進行3. 混合云應用廣泛 Cloud Broker,cascading openstack 云連接器4. DevOps5. 虛擬桌面6. Storage 方面&#xff0c;Ceph和Glusterfs 7. Network…