国产精品天干天干,亚洲毛片在线,日韩gay小鲜肉啪啪18禁,女同Gay自慰喷水

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

字符串和文本

2020-05-22 09:15 作者:unity_某某師_高錦錦  | 我要投稿

字符串和文本的處理不當(dāng)是 Unity 項目中性能問題的常見原因。在 C# 中,所有字符串均不可變。對字符串的任何操作均會導(dǎo)致分配一個完整的新字符串。這種操作的代價相對比較高,而且在大型字符串上、大型數(shù)據(jù)集上或緊湊循環(huán)中執(zhí)行時,接連不斷的重復(fù)的字符串可能發(fā)展成性能問題。

此外,由于 N 個字符串連接需要分配 N–1 個中間字符串,串行連接也可能成為托管內(nèi)存壓力的主要原因。

如果必須在緊湊循環(huán)中或每幀期間對字符串進(jìn)行連接,請使用 StringBuilder 執(zhí)行實際連接操作。為最大限度減少不必要的內(nèi)存分配,可重復(fù)使用 StringBuilder 實例。

Microsoft 整理了一份處理 C# 中的字符串的最佳做法清單,可在這里的 MSDN 網(wǎng)站上找到該清單:msdn.microsoft.com。

區(qū)域約束與序數(shù)比對

在與字符串相關(guān)的代碼中經(jīng)常出現(xiàn)的核心性能問題之一是無意間使用了緩慢的默認(rèn)字符串 API。這些 API 是為商業(yè)應(yīng)用程序構(gòu)建的,可根據(jù)與文本字符有關(guān)的多種不同區(qū)域性和語言規(guī)則來處理字符串。

例如,在美國英語區(qū)域設(shè)置下運行時,以下示例代碼將返回 true,但在許多歐洲區(qū)域設(shè)置下,將返回 false (1)

注意: 從 Unity 5.3 和 5.4 開始,Unity 的腳本運行時始終在美國英語 (en-US) 區(qū)域設(shè)置下運行:

? ?String.Equals("encyclopedia", "encyclop?dia");

對于大多數(shù) Unity 項目,上述代碼完全沒有必要。使用序數(shù)比對可將速度提高大約十倍,這種比較類型以 C 和 C++ 工程師熟悉的方式比較字符串:簡單地比較字符串的每個連續(xù)字節(jié),不考慮該字節(jié)所表示的字符。

切換至序數(shù)比對的方式非常簡單,只需將?StringComparison.Ordinal?作為最終參數(shù)提供給?String.Equals

myString.Equals(otherString, StringComparison.Ordinal);

低效的內(nèi)置字符串 API

除了切換至序數(shù)比對以外,目前已知某些 C#?String?API 的效率極低,其中包括?String.FormatString.StartsWith?和?String.EndsWith。盡管?String.Format?難以替換,但低效率字符串比較方法很容易優(yōu)化掉。

盡管 Microsoft 建議將?StringComparison.Ordinal?傳遞給任何不需要為本地化做調(diào)整的字符串比較,但 Unity 基準(zhǔn)測試表明,相比自定義實現(xiàn),該方法對性能的提升效果有限。

方法????????????????????????????????????????????????????????100k 短字符串的時間(毫秒)

String.StartsWith,默認(rèn)區(qū)域性????????????????????????????????137

String.EndsWith,默認(rèn)區(qū)域性????????????????????????????????????542

String.StartsWith,序數(shù)???????????????????????????????????????????115

String.EndsWith,序數(shù)????????????????????????????????????????????? ? 34

自定義?StartsWith?替換????????????????????????????????????????????????4.5

自定義?EndsWith?替換????????????????????????????????????????????????????4.5

String.StartsWith?和?String.EndsWith?均可以替換為類似于以下示例的簡單的手工編碼版本。

public static bool CustomEndsWith(string a, string b){

int ap = a.Length - 1;

int bp = b.Length - 1;

while (ap >= 0 && bp >= 0 && a [ap] == b [bp]){

ap--;

bp--;

}

return (bp < 0 && a.Length >= b.Length) ||(ap < 0 && b.Length >= a.Length); ? ? }

public static bool CustomStartsWith(string a, string b){

int aLen = a.Length;

int bLen = b.Length;

int ap = 0; int bp = 0;

while (ap < aLen && bp < bLen && a [ap] == b [bp]){

ap++;

bp++;

}

return (bp == bLen && aLen >= bLen) ||(ap == aLen && bLen >= aLen);

}

正則表達(dá)式

盡管正則表達(dá)式是匹配和操作字符串的強(qiáng)大方法,但它們可能對性能的影響極大。此外,由于 C# 庫的正則表達(dá)式實現(xiàn)方式,即使簡單的布爾值?IsMatch?查詢也需要在底層分配大型瞬態(tài)數(shù)據(jù)結(jié)構(gòu)。除非在初始化期間,否則這種瞬態(tài)托管內(nèi)存波動都是不可接受的。

如果必須使用正則表達(dá)式,強(qiáng)烈建議不要使用靜態(tài)?Regex.Match?或?Regex.Replace?方法,這些方法會將正則表達(dá)式視為字符串參數(shù)。這些方法即時編譯正則表達(dá)式,并且不緩存生成的對象。

以下示例代碼為無害的單行代碼。

Regex.Match(myString, "foo");

但是,該代碼每次執(zhí)行時會產(chǎn)生 5 KB 的垃圾。通過簡單的重構(gòu)即可消除其中的大部分垃圾:

var myRegExp = new Regex("foo");?

myRegExp.Match(myString);

在本示例中,每次調(diào)用?myRegExp.Match“只”產(chǎn)生 320 字節(jié)的垃圾。盡管這對于簡單的匹配操作仍然代價高昂,但比前面的示例有了相當(dāng)大的改進(jìn)。

因此,如果正則表達(dá)式是不變的字符串字面值,通過將正則表達(dá)式傳遞為正則表達(dá)式對象構(gòu)造函數(shù)的第一個參數(shù)來預(yù)編譯它們,可顯著提高效率。這些預(yù)編譯的正則表達(dá)式之后會被重用。

XML、JSON 和其他長格式文本解析

解析文本通常是加載期間所發(fā)生的最繁重的操作之一。在某些情況下,解析文本所花費的時間可能超過加載和實例化資源所花費的時間。

此問題背后的原因取決于所使用的具體解析器。C# 的內(nèi)置 XML 解析器極為靈活,但因此無法針對具體數(shù)據(jù)布局進(jìn)行優(yōu)化。

許多第三方解析器都是基于反射構(gòu)建的。盡管反射在開發(fā)過程中是絕佳選擇(因為它能讓解析器快速適應(yīng)不斷變化的數(shù)據(jù)布局),但眾所周知,它的速度非常慢。

Unity 引入了采用其內(nèi)置?JSONUtility?API 的部分解決方案,該解決方案提供了讀取/發(fā)出 JSON 的 Unity 序列化系統(tǒng)接口。在大多數(shù)基準(zhǔn)測試中,它比純 C# JSON 解析器快,但它與 Unity 序列化系統(tǒng)的其他接口具有相同的限制:沒有額外代碼的情況下,無法對許多復(fù)雜的數(shù)據(jù)類型(如字典)進(jìn)行序列化(2)(注意: 請參閱?ISerializationCallbackReceiver?接口,了解如何通過一種方法輕松添加必要的額外處理以便在 Unity 序列化過程中來回轉(zhuǎn)換復(fù)雜數(shù)據(jù)類型)。

當(dāng)遇到文本數(shù)據(jù)解析所引起的性能問題時,請考慮三種替代解決方案。

方案 1:在構(gòu)建時解析

避免文本解析成本的最佳方法是完全取消運行時文本解析。通常,這意味著通過某種構(gòu)建步驟將文本數(shù)據(jù)“烘焙”成二進(jìn)制格式。

大多數(shù)選擇使用該方法的開發(fā)者會將其數(shù)據(jù)移動到某種 ScriptableObject 衍生的類層級視圖中,然后通過 AssetBundle 分配數(shù)據(jù)。有關(guān)使用 ScriptableObjects 的精彩討論,請參閱 youtube 上?Richard Fine 的 Unite 2016 講座。

該策略可實現(xiàn)盡可能高的性能,但只適用于不需要動態(tài)生成的數(shù)據(jù)。它適用于游戲設(shè)計參數(shù)和其他內(nèi)容。

方案 2:拆分和延遲加載

第二種可行的方法是將必須解析的數(shù)據(jù)拆分為較小的數(shù)據(jù)塊。拆分后,解析數(shù)據(jù)的成本可分?jǐn)偟蕉鄠€幀。在理想的情況下,可識別出為用戶提供所需體驗而需要的特定數(shù)據(jù)部分,然后只加載這些部分。

舉一個簡單的例子:如果項目為平臺游戲,則沒必要將所有關(guān)卡的數(shù)據(jù)一起序列。如果將數(shù)據(jù)拆分為每個關(guān)卡的獨立資源,并且將關(guān)卡劃分到區(qū)域中,則可以在玩家闖關(guān)到相應(yīng)位置時再解析數(shù)據(jù)。

雖然這聽起來不難,但實際上需要在工具編碼方面投入大量精力,并可能需要重組數(shù)據(jù)結(jié)構(gòu)。

方案 3:線程

如果數(shù)據(jù)完全解析成純 C# 對象,并且不需要與 Unity API 進(jìn)行任何交互,則可以將解析操作移至工作線程。

該方案在具有大量核心的平臺上非常強(qiáng)大(3)(注意: iOS 設(shè)備最多有 2 個核心。大多數(shù) Android 設(shè)備具有 2–4 個核心。該技術(shù)適用于針對電腦平臺和游戲主機(jī)發(fā)布的項目。)但是,該方案需要仔細(xì)編程,以免產(chǎn)生死鎖和競態(tài)條件。

選擇實現(xiàn)線程的項目通常使用內(nèi)置的 C#?Thread?和?ThreadPool?類(請參閱?msdn.microsoft.com)來管理其工作線程以及標(biāo)準(zhǔn) C# 同步類。

腳注

  • (1) 請注意,從 Unity 5.3 和 5.4 開始,Unity 的腳本運行時始終在美國英語 (en-US) 區(qū)域設(shè)置下運行。

  • (2) 請參閱?ISerializationCallbackReceiver?接口,了解如何通過一種方法輕松添加必要的額外處理以便在 Unity 序列化過程中來回轉(zhuǎn)換復(fù)雜數(shù)據(jù)類型。

  • (3) 請注意,iOS 設(shè)備最多有 2 個核心。大多數(shù) Android 設(shè)備具有 2–4 個核心。該技術(shù)適用于針對電腦平臺和游戲主機(jī)發(fā)布的項目。

字符串和文本的評論 (共 條)

分享到微博請遵守國家法律
成安县| 定南县| 建昌县| 灵武市| 泸水县| 广南县| 湖南省| 崇义县| 水城县| 应城市| 乐业县| 岳西县| 乐安县| 沁源县| 六枝特区| 祁东县| 江山市| 西青区| 南华县| 鹰潭市| 澎湖县| 瑞金市| 巴东县| 晋城| 双牌县| 米泉市| 金溪县| 巫溪县| 岳阳市| 乌鲁木齐县| 镇安县| 清水河县| 昌宁县| 廊坊市| 清流县| 兴业县| 蓬莱市| 旬阳县| 黔西| 汾阳市| 金湖县|