...

優化.NET 應用程序 CPU 和(hé / huò)内存的(de)11 個(gè)實踐

2022-02-12

前言


凡事都有其限度,對吧?汽車隻能開這(zhè)麽快,進程隻能使用這(zhè)麽多内存,程序員隻能喝這(zhè)麽多咖啡。我們的(de)生産力受到(dào)資源的(de)限制,我們有能力更好或更差地(dì / de)利用它們。盡可能接近其極限使用我們的(de)每一(yī / yì /yí)種資源是(shì)我們的(de)目标,我們希望使用我們的(de) CPU 和(hé / huò)内存的(de)每一(yī / yì /yí)點,否則我們會爲(wéi / wèi)昂貴的(de)機器多付錢。然而(ér),若是(shì)我們使用了(le/liǎo)過多的(de)資源,我們就(jiù)有可能導緻性能問題、服務不(bù)可用問題和(hé / huò)程序宕機底崩潰問題。軟件開發看似簡單,但一(yī / yì /yí)旦遇到(dào)性能問題,就(jiù)會變得非常棘手,這(zhè)就(jiù)是(shì)我們今天要(yào / yāo)讨論的(de)内容。


定義最佳基準


讓我們嘗試描述我們的(de)最佳應用程序行爲(wéi / wèi)。假設我們有許多服務器機器需要(yào / yāo)處理高吞吐量的(de)請求。爲(wéi / wèi)簡單起見,讓我們暫時(shí)忘記高峰時(shí)間或周末。我們的(de)服務器負載在(zài)一(yī / yì /yí)天中的(de)所有時(shí)間都或多或少相同。我們爲(wéi / wèi)這(zhè)些服務器機器支付了(le/liǎo)很多錢,我們希望從它們那裏獲得盡可能多的(de)價值,這(zhè)意味着處理盡可能多的(de)請求。按照我們對簡單性的(de)承諾,我們還假設服務器僅使用内存和(hé / huò) CPU 來(lái)處理所述請求,并且沒有其他(tā)瓶頸,例如慢速網絡或鎖争用。


在(zài)所描述的(de)場景中,我們的(de)最佳行爲(wéi / wèi)是(shì)在(zài)任何給定時(shí)間使用盡可能多的(de) CPU 和(hé / huò)内存,對嗎?這(zhè)樣,我們可以(yǐ)用更少的(de)機器來(lái)處理相同數量的(de)請求。但是(shì)你可能不(bù)想利用這(zhè)些資源中的(de) 99.9%,因爲(wéi / wèi)負載的(de)輕微增加可能會導緻性能問題、服務器崩潰、數據丢失和(hé / huò)其他(tā)令人(rén)頭疼的(de)問題。所以(yǐ)我們應該選擇一(yī / yì /yí)個(gè)有足夠緩沖問題的(de)數值。平均 85% 或 90% 的(de) CPU 和(hé / huò)内存利用率聽起來(lái)是(shì)正确的(de)。


我們應該首先優化什麽?


我們的(de)應用程序不(bù)是(shì)爲(wéi / wèi)平等利用 CPU 和(hé / huò)内存而(ér)構建的(de)。或者到(dào)它托管的(de)機器的(de)确切限制。因此,你首先應該查看的(de)是(shì)你的(de)服務器是(shì)CPU-bound還是(shì)Memory-bound當服務器受 CPU 限制時(shí),這(zhè)意味着服務器可以(yǐ)處理的(de)吞吐量受到(dào)其 CPU 的(de)限制。換句話說(shuō),如果你嘗試處理更多請求,CPU 将在(zài)其他(tā)資源(如内存)達到(dào)其限制之(zhī)前達到(dào) 100%。同樣的(de)邏輯也(yě)适用于(yú)Memory-bound服務器。


服務器的(de)吞吐量将受到(dào)它可以(yǐ)分配的(de)内存的(de)限制,當嘗試處理更多負載時(shí),在(zài)其他(tā)資源(如 CPU)達到(dào)其限制之(zhī)前,該内存将達到(dào) 100%。還有其他(tā)資源可以(yǐ)限制服務器,例如I/O,在(zài)這(zhè)種情況下,吞吐量會受到(dào)磁盤或網絡的(de)讀取或寫入限制。但是(shì)我們将在(zài)這(zhè)篇文章中忽略這(zhè)一(yī / yì /yí)點,樂觀地(dì / de)假設我們的(de) I/O 是(shì)快速且無限的(de)。一(yī / yì /yí)旦你知道(dào)是(shì)什麽限制了(le/liǎo)你的(de)服務器的(de)性能,你就(jiù)會知道(dào)首先要(yào / yāo)嘗試和(hé / huò)優化什麽。


如果你的(de)服務器受 CPU 限制,那麽優化内存使用沒有意義,因爲(wéi / wèi)它不(bù)會提高處理的(de)吞吐量。事實上(shàng),它可能會損害吞吐量,因爲(wéi / wèi)你可能會因爲(wéi / wèi)更多的(de) CPU 利用率而(ér)提高内存使用率。對于(yú)内存受限的(de)服務器也(yě)是(shì)如此,在(zài)這(zhè)種情況下,你應該在(zài)查看 CPU 之(zhī)前優化内存使用。


測量 .NET 服務器中的(de) CPU 和(hé / huò)内存消耗


CPU 和(hé / huò)内存的(de)實際測量最簡單的(de)是(shì)使用Performance Counters完成。CPU 使用率的(de)指标是(shì)Process | % 處理器時(shí)間内存有幾個(gè)指标,但我建議查看Process | 私有字節你可能還對**.NET CLR 内存感興趣 | # 代表托管内存的(de)所有堆中的(de)字節**(CLR 占用的(de)部分,而(ér)不(bù)是(shì)所有内存,即托管 + 本機内存)。要(yào / yāo)查看性能計數器,你可以(yǐ)在(zài) Windows 計算機上(shàng)使用Process Explorer或 PerfMon,或者在(zài) .NET Core 服務器上(shàng)使用dotnet-counters 。如果你的(de)應用程序部署在(zài)雲中,你可以(yǐ)使用像Application Insights(Azure Monitor的(de)一(yī / yì /yí)部分)這(zhè)樣的(de) APM 工具來(lái)顯示這(zhè)些信息。或者,你可以(yǐ)在(zài)代碼中獲取性能計數器值并每 10 秒左右記錄一(yī / yì /yí)次,使用Azure 數據資源管理器之(zhī)類的(de)工具在(zài)圖表中顯示數據。

圖片

提示:檢查機器級指标和(hé / huò)進程級指标。你可能會發現其他(tā)進程正在(zài)限制你的(de)性能。

一(yī / yì /yí)旦确定了(le/liǎo)哪些資源限制了(le/liǎo)你的(de) .NET 服務器,就(jiù)該優化該資源消耗了(le/liǎo)。如果你受 CPU 限制,讓我們減少 CPU 使用率。如果你受内存限制,讓我們減少内存使用量。至少如果你在(zài)雲中運行,一(yī / yì /yí)種簡單的(de)方法是(shì)更改機器規格。如果你受内存限制,請增加内存。如果你受 CPU 限制,請增加内核數量或獲得更快的(de) CPU。這(zhè)将提高成本,但在(zài)此之(zhī)前,你可以(yǐ)檢查一(yī / yì /yí)些容易實現的(de)目标,以(yǐ)優化 CPU 或内存消耗。在(zài)更改機器規格之(zhī)前嘗試進行這(zhè)些優化,因爲(wéi / wèi)優化後一(yī / yì /yí)切都會改變。你可能會優化 CPU 使用率并變得受内存限制。然後優化内存使用并再次成爲(wéi / wèi) CPU 密集型。因此,如果你想避免不(bù)得不(bù)不(bù)斷更改機器資源以(yǐ)适應最新的(de)優化,最好把它留到(dào)最後。所以(yǐ)讓我們談談一(yī / yì /yí)些内存優化。

優化内存使用

有很多方法可以(yǐ)優化 .NET 中的(de)内存使用。深入讨論它們需要(yào / yāo)一(yī / yì /yí)整本書,而(ér)且已經有好幾本了(le/liǎo)。但我會盡量給你一(yī / yì /yí)些方向和(hé / huò)想法。

1、了(le/liǎo)解什麽占用了(le/liǎo)你的(de)内存

嘗試優化内存時(shí),你應該做的(de)第一(yī / yì /yí)件事是(shì)了(le/liǎo)解全局。什麽占用了(le/liǎo)大(dà)部分内存?有哪些數據類型?它們分配在(zài)哪裏?它們會在(zài)記憶中停留多久?有幾種工具可以(yǐ)獲取此信息:•捕獲轉儲文件并使用内存分析器或WinDbg打開它。•使用新的(de)GC 轉儲(.NET Core 3.1+) 并使用 Visual Studio 進行調查。•捕獲堆快照并使用内存分析器、PerfView或Visual Studio 診斷工具對其進行探索。此分析将顯示哪些對象占用了(le/liǎo)你的(de)大(dà)部分内存。如果你發現它被采取了(le/liǎo)MyProgram.CustomerData那就(jiù)更好了(le/liǎo)。但通常,最大(dà)的(de)對象類型是(shì)stringbyte[]byte[][]。由于(yú)應用程序中的(de)幾乎所有内容都可以(yǐ)使用這(zhè)些類型,因此你需要(yào / yāo)找到(dào)引用它們的(de)人(rén)。爲(wéi / wèi)此,查看所占用的(de)包容性内存(又名保留内存)很重要(yào / yāo)。這(zhè)個(gè)指标不(bù)僅包括對象本身占用的(de)内存,還包括它引用的(de)對象占用的(de)内存。例如,你可能會發現它MyProgram.Inventory.Item本身并不(bù)占用太多内存,但它引用了(le/liǎo)一(yī / yì /yí)個(gè)byte[]它保存内存中的(de)圖像并占用高達 70% 的(de)内存。上(shàng)面描述的(de)所有工具都可以(yǐ)顯示包含最多字節的(de)對象和(hé / huò)到(dào) GC 根的(de)引用路徑(也(yě)就(jiù)是(shì)到(dào)根的(de)最短路徑)。

2、了(le/liǎo)解誰把内存放在(zài)了(le/liǎo)哪裏

找出(chū)誰引用了(le/liǎo)最大(dà)的(de)内存塊很棒,但這(zhè)可能還不(bù)夠。有時(shí)你需要(yào / yāo)知道(dào)這(zhè)些内存是(shì)如何分配的(de)。你可能從引用路徑中知道(dào),一(yī / yì /yí)些占用大(dà)部分内存的(de)對象位于(yú)緩存中,但誰将它們放在(zài)那裏?來(lái)自單個(gè)時(shí)間點的(de)内存快照無法提供該答案。爲(wéi / wèi)此,你需要(yào / yāo)分配堆棧跟蹤。分析器使你能夠記錄你的(de)應用程序并在(zài)每次分配時(shí)保存調用堆棧。例如,你可能會發現創建有問題MyProgram.Inventory.Item對象的(de)流程将它們分配到(dào)調用堆棧App.OnShowHistoryClicked | App.SeeItemHistory | App.GetItemFromDatabase中。要(yào / yāo)獲得分配堆棧,你可以(yǐ):•使用商業内存分析器來(lái)顯示分配。

圖片

•使用 PerfView 的(de) GC Heap [] Stacks 之(zhī)一(yī / yì /yí)

圖片

分配讓你全面了(le/liǎo)解占用大(dà)部分内存的(de)内容以(yǐ)及它是(shì)如何産生的(de)。一(yī / yì /yí)旦你知道(dào)了(le/liǎo)這(zhè)一(yī / yì /yí)點,你就(jiù)可以(yǐ)開始切割最大(dà)的(de)塊并優化它們以(yǐ)減少内存使用。

3、檢查内存洩漏

在(zài) .NET 中導緻内存洩漏非常容易。有了(le/liǎo)足夠多的(de)洩漏,内存消耗會随着時(shí)間的(de)推移而(ér)增加,你會遇到(dào)各種各樣的(de)問題。内存瓶頸就(jiù)是(shì)其中之(zhī)一(yī / yì /yí),但由于(yú) GC 壓力,你最終也(yě)會遇到(dào) CPU 問題。當你不(bù)再需要(yào / yāo)對象但由于(yú)某種原因它們仍然被引用并且垃圾收集器永遠不(bù)會釋放它們時(shí),就(jiù)會發生内存洩漏。發生這(zhè)種情況的(de)原因有很多。要(yào / yāo)了(le/liǎo)解你是(shì)否有嚴重的(de)内存洩漏,請查看一(yī / yì /yí)段時(shí)間内的(de)内存消耗圖表(進程 | 私有字節計數器)。如果内存一(yī / yì /yí)直在(zài)增加,而(ér)沒有偏離某個(gè)水平,則可能存在(zài)内存洩漏。

圖片

使用内存分析器調試洩漏相當簡單。

4、切換到(dào) GC 工作站模式

.NET 中有幾種垃圾收集器模式。主要(yào / yāo)的(de)兩種模式是(shì)Workstation GC和(hé / huò)Server GC。Workstation GC 針對更短的(de) GC 暫停和(hé / huò)更快的(de)交互性進行了(le/liǎo)優化,非常适合桌面應用程序。服務器 GC 具有更長的(de) GC 暫停時(shí)間,并且針對更高的(de)吞吐量進行了(le/liǎo)優化。

在(zài) Server GC 模式下,應用程序可以(yǐ)在(zài)垃圾回收之(zhī)間處理更多數據。服務器 GC 爲(wéi / wèi)每個(gè) CPU 核心創建不(bù)同的(de)托管堆。這(zhè)意味着不(bù)同的(de) X 代内存空間需要(yào / yāo)更長的(de)時(shí)間才能填滿,因此内存消耗會更高。你基本上(shàng)是(shì)在(zài)用内存換取吞吐量。從 GC 服務器模式(.NET 服務器的(de)默認模式)更改爲(wéi / wèi) GC 工作站模式将減少内存使用量。這(zhè)在(zài)請求負載不(bù)重的(de)小型應用程序中可能是(shì)合理的(de)。也(yě)許在(zài)與主應用程序一(yī / yì /yí)起運行的(de) IIS 主機中的(de)輔助進程中。Sergey Tepliakov對此有一(yī / yì /yí)篇很棒的(de)文章。

5、檢查你的(de)緩存

在(zài)第 1 步之(zhī)後,你應該能夠看到(dào)哪些對象占用了(le/liǎo)你的(de)内存,但我想特别強調緩存。每當涉及到(dào)高内存消耗時(shí),根據我的(de)經驗,它總是(shì)最終成爲(wéi / wèi)内存洩漏或緩存。緩存似乎是(shì)許多問題的(de)神奇解決方案。當你可以(yǐ)将結果保存在(zài)内存中并重新使用它時(shí),爲(wéi / wèi)什麽要(yào / yāo)執行兩次?但是(shì)緩存是(shì)有代價的(de)。一(yī / yì /yí)個(gè)簡單的(de)實現會将對象永遠保存在(zài)内存中。你應該按時(shí)間限制或以(yǐ)其他(tā)方式使緩存無效。緩存還會将臨時(shí)對象留在(zài)内存中相對較長的(de)時(shí)間,這(zhè)會導緻更多的(de) Gen 1 和(hé / huò) Gen 2 收集,進而(ér)導緻GC 壓力。以(yǐ)下是(shì)一(yī / yì /yí)些優化内存緩存的(de)想法:

•使用.NET 中的(de)現有緩存實現可以(yǐ)輕松創建失效策略。

•考慮爲(wéi / wèi)某些事情選擇不(bù)緩存。你可能會用 CPU 或 IO 換取内存,但是(shì)當你受到(dào)内存限制時(shí),你應該這(zhè)樣做。

•考慮使用内存不(bù)足緩存。這(zhè)可能是(shì)将數據保存在(zài)文件或本地(dì / de)數據庫中。或者使用像Redis這(zhè)樣的(de)分布式緩存解決方案。

6、定期調用GC.Collect()

這(zhè)條建議是(shì)違反直覺的(de),因爲(wéi / wèi)最好的(de)做法是(shì)永遠不(bù)要(yào / yāo)調用GC.Collect(). 垃圾收集器很聰明,它應該自己知道(dào)何時(shí)觸發收集。但問題是(shì)垃圾收集器隻考慮自己的(de)進程。如果它沒有足夠的(de)内存,它會小心觸發收集并騰出(chū)空間。但如果它确實有足夠的(de)内存,GC 會非常樂意忍受過多的(de)内存消耗。

因此,GC 的(de)自私本性可能是(shì)生活在(zài)同一(yī / yì /yí)台機器上(shàng)的(de)**其他(tā)進程的(de)問題,可能托管在(zài)同一(yī / yì /yí)個(gè) IIS 上(shàng)。

這(zhè)種多餘的(de)内存可能會導緻其他(tā)進程更快地(dì / de)達到(dào)它們的(de)極限,或者導緻它們各自的(de)垃圾收集器更加努力地(dì / de)工作,因爲(wéi / wèi)它們可能錯誤地(dì / de)認爲(wéi / wèi)它們即将耗盡内存。你可能會認爲(wéi / wèi),如果其他(tā)進程的(de) GC 會達到(dào)認爲(wéi / wèi)我們内存不(bù)足并因此更加努力地(dì / de)工作的(de)程度,那麽我們自己的(de)進程也(yě)會這(zhè)樣認爲(wéi / wèi)并觸發垃圾收集來(lái)解決問題。但我們不(bù)能做出(chū)這(zhè)樣的(de)假設。一(yī / yì /yí)方面,這(zhè)些進程可能運行不(bù)同的(de) GC 實現版本(因爲(wéi / wèi)不(bù)同的(de) CLR 版本)。此外,你有不(bù)同的(de)應用程序行爲(wéi / wèi)可以(yǐ)使 GC 以(yǐ)不(bù)同的(de)方式工作。例如,一(yī / yì /yí)個(gè)進程可能會以(yǐ)更高的(de)速率分配内存,因此 GC 将更快地(dì / de)開始“強調”可用内存。底線是(shì)軟件很困難,當你在(zài)一(yī / yì /yí)台機器上(shàng)有多個(gè)進程時(shí),就(jiù)像 IIS 一(yī / yì /yí)樣,你需要(yào / yāo)考慮到(dào)這(zhè)一(yī / yì /yí)點,并可能采取一(yī / yì /yí)些不(bù)尋常的(de)步驟。

優化 CPU 使用率

硬币的(de)另一(yī / yì /yí)面是(shì) CPU 使用率。一(yī / yì /yí)旦你發現 CPU 是(shì)應用程序吞吐量的(de)瓶頸,就(jiù)需要(yào / yāo)做很多事情。

1、分析你的(de)應用程序

優化 CPU 的(de)第一(yī / yì /yí)步是(shì)了(le/liǎo)解它。究竟是(shì)什麽原因造成的(de)?哪些方法負責?哪些請求是(shì)最大(dà)的(de) CPU 消耗者,哪些是(shì)流量?這(zhè)一(yī / yì /yí)切都可以(yǐ)通過分析應用程序來(lái)解決。分析允許你記錄執行範圍并顯示所有被調用的(de)方法以(yǐ)及它們在(zài)記錄期間使用了(le/liǎo)多少 CPU。分析器通常允許将這(zhè)些結果視爲(wéi / wèi)普通列表、調用樹甚至火焰圖。這(zhè)是(shì) PerfView 中的(de)簡單列表視圖:

圖片

這(zhè)是(shì)相同場景的(de)火焰圖:

圖片

你可以(yǐ)通過以(yǐ)下方式分析你的(de)應用:

•如果場景在(zài)本地(dì / de)重現,請使用性能分析器,如PerfView、dotTrace、ANTS perf profiler,或在(zài)你的(de)開發計算機上(shàng)使用 Visual Studio 。

•在(zài)生産環境中,最簡單的(de)分析方法是(shì)使用應用程序性能監控 (APM) 工具,例如Azure Application Insights profiler或RayGun。

•你可以(yǐ)通過将代理複制到(dào)生産機器并記錄快照來(lái)分析沒有 APM 的(de)生産環境。使用 PerfView,你應該複制整個(gè)程序。它結構緊湊,無需安裝。使用 dotTrace,你可以(yǐ)複制允許在(zài)生産中記錄快照的(de)輕量級代理。

•在(zài) .NET Core 3.0+ 應用程序中,你可以(yǐ)安裝 .NET Core 3.0 SDK 并使用 dotnet-trace 命令行工具記錄快照,然後使用 PerfView 将其複制到(dào)開發機器并進行分析。

2、檢查垃圾收集器的(de)使用情況

我想說(shuō)優化 .NET CPU 使用最重要(yào / yāo)的(de)一(yī / yì /yí)點是(shì)正确的(de)内存管理。在(zài)這(zhè)方面要(yào / yāo)問的(de)重要(yào / yāo)問題是(shì):“垃圾收集浪費了(le/liǎo)多少 CPU?”。GC 的(de)工作方式是(shì)在(zài)收集期間,你的(de)執行線程被凍結。這(zhè)意味着垃圾收集直接影響性能。因此,如果你受 CPU 限制,我建議你檢查的(de)第一(yī / yì /yí)件事是(shì)性能計數器。NET CLR 内存 | % GC 時(shí)間。我不(bù)能給你一(yī / yì /yí)個(gè)指示問題的(de)神奇數字,但根據經驗,當這(zhè)個(gè)值超過 20% 時(shí),你可能會遇到(dào)問題。如果超過 40%,那麽你肯定有問題。如此高的(de)百分比表明 GC 壓力,并且有辦法處理它。

3、使用數組和(hé / huò)對象池來(lái)重用内存

陣列的(de)分配和(hé / huò)不(bù)可避免的(de)解除分配可能非常昂貴。高頻率執行這(zhè)些分配會造成 GC 壓力并消耗大(dà)量 CPU 時(shí)間。解決這(zhè)個(gè)問題的(de)一(yī / yì /yí)個(gè)好方法是(shì)使用内置的(de)ArrayPoolObjectPool (僅限 .NET Core)。這(zhè)個(gè)想法很簡單。爲(wéi / wèi)數組或對象分配一(yī / yì /yí)個(gè)共享緩沖區,然後在(zài)不(bù)分配和(hé / huò)取消分配新内存的(de)情況下重複使用。這(zhè)是(shì)一(yī / yì /yí)個(gè)簡單的(de)使用示例ArrayPool

public void Foo()
{
    var pool = ArrayPool<int>.Shared;
    int[] array = pool.Rent(ArraySize);// do stuf
    pool.Return(array);
}

4、切換到(dào) GC 服務器模式

我們已經讨論過轉移到(dào)GC 工作站模式以(yǐ)節省内存。但如果你受 CPU 限制,請考慮切換到(dào)服務器模式以(yǐ)節省 CPU。權衡是(shì)服務器模式以(yǐ)更多内存爲(wéi / wèi)代價允許更高的(de)吞吐量。

因此,如果你保持相同的(de)吞吐量,你最終将節省 CPU 時(shí)間,否則垃圾收集會花費這(zhè)些時(shí)間。默認情況下,.NET 服務器很可能具有 GC 服務器模式,因此可能不(bù)需要(yào / yāo)此更改。但是(shì)可能有人(rén)之(zhī)前将其更改爲(wéi / wèi)工作站模式,在(zài)這(zhè)種情況下,你應該小心将其更改回來(lái),因爲(wéi / wèi)他(tā)們可能有充分的(de)理由。

更改時(shí),請務必監控内存消耗和(hé / huò) GC 中的(de) % Time。你可能想查看第 2 代回收率,但如果這(zhè)個(gè)數字很高,它将反映在(zài)更高的(de) GC 時(shí)間百分比中。

5、檢查其他(tā)進

當試圖将你的(de)服務器發揮到(dào)最佳極限時(shí),你可能想要(yào / yāo)徹底了(le/liǎo)解它,這(zhè)意味着不(bù)要(yào / yāo)放棄存在(zài)于(yú)你的(de)進程之(zhī)外的(de)問題。很有可能其他(tā)進程不(bù)時(shí)消耗一(yī / yì /yí)堆CPU,并導緻一(yī / yì /yí)段時(shí)間的(de)性能下降。這(zhè)些可能是(shì)你在(zài) IIS 上(shàng)部署的(de)其他(tā)應用程序、定期 Web 作業、由操作系統觸發的(de)東西、防病毒程序或其他(tā)一(yī / yì /yí)千種東西。對此進行分析的(de)一(yī / yì /yí)種方法是(shì)使用 PerfView 記錄整個(gè)系統中的(de) ETW 事件。PerfView 從所有進程中捕獲 CPU 堆棧。你可以(yǐ)以(yǐ)很小的(de)性能開銷運行它很長時(shí)間。你可以(yǐ)在(zài)達到(dào)某個(gè) CPU 峰值時(shí)自動停止收集并進行挖掘。你可能會對結果感到(dào)驚訝。

總結

在(zài)我看來(lái),從自上(shàng)而(ér)下的(de)層面處理大(dà)規模的(de)性能問題是(shì)令人(rén)着迷的(de)。你可能有一(yī / yì /yí)個(gè)團隊花費數月時(shí)間優化一(yī / yì /yí)段代碼,相比之(zhī)下,資源分配的(de)簡單更改将産生更大(dà)的(de)影響。而(ér)且,如果你的(de)業務足夠大(dà),那麽這(zhè)個(gè)微小的(de)變化就(jiù)會轉化爲(wéi / wèi)一(yī / yì /yí)大(dà)筆錢。你記得在(zài)你的(de)合同中要(yào / yāo)求一(yī / yì /yí)個(gè)傭金條款嗎?無論如何,我希望這(zhè)篇文章對你有用。


來(lái)源:DotNet