臨時字符串拼接的優(yōu)化

字符串拼接在高級語言中事件很讓人頭疼的事情,無論是內存申請的額外耗時還是產生臨時內存造成的大量gc都是很難優(yōu)化的宴杀,最近在c#中采用了一種優(yōu)化方案癣朗,在此記錄一下。

造成性能災難的原因

首先我們要明白c#對字符串的處理是怎么樣的:

c#對一些通常的字符串操作旺罢,是做了緩存的優(yōu)化的旷余,string作為一個引用類型,不同的變量只要內容相同扁达,實質上都是指向同一個引用正卧,我們可以用下述方法獲取變量內存地址來一探究竟:

public string getMemory(object o) // 獲取引用類型的內存地址方法
{
        GCHandle h = GCHandle.Alloc(o, GCHandleType.Pinned);

        IntPtr addr = h.AddrOfPinnedObject();

        return "0x" + addr.ToString("X");
}
string s1 = "111";
string s2 = "11" + "1";
string s3 = new string('1', 3);
Debug.Log("s1:" + s1);
Debug.Log("s2:" + s2);
Debug.Log("s3:" + s3);
Debug.Log("s1 memory:" + getMemory(s1));
Debug.Log("s2 memory:" + getMemory(s2));
Debug.Log("s3 memory:" + getMemory(s3));

觀察輸出可以發(fā)現,字符串s1,s2雖然是通過不同方式拼接的跪解,但是因為其內容一致炉旷,引用指向的內存地址都是相同的。

c#在常用的字符串操作中都采用了這樣的優(yōu)化方式,但是可以通過new string的方式來構建一個新的string引用窘行,可以看到上面的字符串s3雖然內容與s1,s2都相同饥追,但是地址卻是不一樣的。

在字符串拼接時罐盔,實質上并沒有對原有字符串做任何改變但绕,而是檢查是否全局已經緩存了相同字符串,如果緩存了就返回引用翘骂,沒有的話便重新創(chuàng)建一個新的string壁熄,直到下一次gc被回收,這樣也就解釋了為什么我們拼接不同結果字符串的時候會產生大量的內存垃圾碳竟。

string的本質

想要優(yōu)化字符串拼接的話,我們需要進一步探究string的本質:

c#中的string是一個引用類型的class狸臣,其本質是一個含有一些描述信息的char數組莹桅,但是我們對本質的探究到這里并沒有停止。

對業(yè)務層而言烛亦,我們需要的字符串到底是什么诈泼?按最終用途可以分成兩類:

最終變?yōu)槎M制流的數據

業(yè)務中一大部分的字符串,最終實際上都會轉變?yōu)閿祿髅呵荩热缇W絡消息和寫文件铐达。實際上我們并不需要產生string類,使用自己維護的char數組來記錄字符數據檬果,然后直接輸出為二進制流瓮孙,之后再對char數組進行回收重用,可以徹底解決string類產生的gc和內存壓力选脊。

需要注意的是杭抠,如果需要對字符流編碼,可以使用encoder的原生方法:

Encoder mEncoder = Encoding.UTF8.GetEncoder();
    private unsafe int GetBytes(char[] chars,byte[] bytes,int offset,int length,int outOffset)
    {
        fixed (char* c = chars)
        {
            //最后一個參數代表是否刷新encode緩存內容
            int count = mEncoder.GetByteCount(c + offset, length, false);
            //注意byte數組要開辟足夠的空間
            fixed (byte* b = bytes)
            {
                mEncoder.GetBytes(c + offset, length, b + outOffset, count, false);
                return count;
            }
        }
    }

最終需要傳遞給系統(tǒng)API的string

對于業(yè)務中需要傳遞給不可控接口的string恳啥,我們需要對接口需要的string進行判斷偏灿,然后通過修改string內容的方式,對string這一類進行重用钝的。要知道雖然c#沒有提供string內容修改的接口翁垂,但是string本質上也還是char數組,對其內容修改相當容易:

private unsafe void SetChar(string s, char c, int index)
        {
            if (index >= s.Length)
                throw new ArgumentOutOfRangeException("index");

            fixed (char *p = s) {
                // Set the character.
                p[index] = c;
            }
        }

緩存的策略類似普通的緩存池硝桩,需要新字符串時沿猜,從string池中取出一個長度足夠的(不夠就新建),從頭填充正確的數據亿柑,最后對多余的尾部按需修改填充邢疙,用完放回池子。

渲染類

對于渲染類的string一般都好處理,重用string時尾部填充‘\0'就可以疟游,例如unity中的Text組件就可以采用這樣的策略呼畸。(unity在讀到\0后就不讀了,所以實際上只要把不需要的第一個位置填上\0就好)

json類

對于json這樣的字符串標記語言來說就要麻煩一些了颁虐,如果項目中的json解析類是可以修改的蛮原,還可以用\0來表示終止,但是遇到外部庫這樣的另绩,不知道是否對\0做處理的話儒陨,只能在數據傳輸時約定一個特殊key,內容填空字符串笋籽,通過這樣的方式來補全string多余的無用位置了蹦漠。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市车海,隨后出現的幾起案子笛园,更是在濱河造成了極大的恐慌,老刑警劉巖侍芝,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件研铆,死亡現場離奇詭異,居然都是意外死亡州叠,警方通過查閱死者的電腦和手機棵红,發(fā)現死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咧栗,“玉大人逆甜,你說我怎么就攤上這事÷ハǎ” “怎么了忆绰?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長可岂。 經常有香客問我错敢,道長,這世上最難降的妖魔是什么缕粹? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任稚茅,我火速辦了婚禮,結果婚禮上平斩,老公的妹妹穿的比我還像新娘亚享。我一直安慰自己,他們只是感情好绘面,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布欺税。 她就那樣靜靜地躺著侈沪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晚凿。 梳的紋絲不亂的頭發(fā)上亭罪,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音歼秽,去河邊找鬼应役。 笑死,一個胖子當著我的面吹牛燥筷,可吹牛的內容都是我干的箩祥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼肆氓,長吁一口氣:“原來是場噩夢啊……” “哼袍祖!你這毒婦竟也來了?” 一聲冷哼從身側響起做院,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤盲泛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后键耕,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡柑营,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年屈雄,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片官套。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡酒奶,死狀恐怖,靈堂內的尸體忽然破棺而出奶赔,到底是詐尸還是另有隱情惋嚎,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布站刑,位于F島的核電站另伍,受9級特大地震影響,放射性物質發(fā)生泄漏绞旅。R本人自食惡果不足惜摆尝,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望因悲。 院中可真熱鬧堕汞,春花似錦、人聲如沸晃琳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至人灼,卻和暖如春围段,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挡毅。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工蒜撮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人跪呈。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓段磨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親耗绿。 傳聞我的和親對象是個殘疾皇子苹支,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,105評論 1 32
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,813評論 0 38
  • 轉 # https://www.cnblogs.com/easypass/archive/2010/12/ 08/...
    呂品?閱讀 9,733評論 0 44
  • __block和__weak修飾符的區(qū)別其實是挺明顯的:1.__block不管是ARC還是MRC模式下都可以使用,...
    LZM輪回閱讀 3,315評論 0 6
  • 今天想來分享一下自己的一個真實體驗误阻。 一次無意中刷朋友圈看到一位高中好友發(fā)的美體內衣宣傳债蜜,生完布丁喂完奶,瘦得很但...
    陳華潔閱讀 803評論 0 0