共用方式為


本文章是由機器翻譯。

為 SQL 提速

通過層交互分析優化資料庫調用

Mark Friedman

許多應用程式專為使用多個層而設計。在這樣的應用程式中,就應用程式的整體回應而言,對資料訪問層的調用的性能至關重要。使用多個層可以提高應用程式的靈活性。n 層方法也可以説明實現關鍵元件的隔離,這可用於提高可靠性和可伸縮性。將元件隔離到不同層中後,可以更輕鬆地在可用計算資源間分佈,因此使用多個層可以提高可伸縮性。

層交互分析 (TIP) 旨在説明您瞭解應用程式所依賴的資料層的性能。TIP 是 Visual Studio 分析工具提供的一種新功能,用於測量和報告 .NET Framework 應用程式在等待對 ADO.NET 相容資料庫的同步調用完成時,所經歷的資料層延遲持續時間。對於經常調用資料庫而又注重回應時間的應用程式,TIP 可以説明您瞭解哪些資料請求是造成回應延遲的主要原因。

本文將介紹 TIP 並演示其報告功能。另外還將討論 TIP 所依賴的檢測技術,並提供一些有效使用 TIP 診斷與資料庫活動相關的性能問題的最佳實踐。本文將逐步介紹在以下示例環境中使用 TIP:資料密集型的雙層 ASP.NET Web 應用程式,並且使用 LINQ to SQL 技術從 Microsoft SQL Server 訪問資料。最後將討論如何使用標準 SQL 管理員性能工具來增加 TIP 性能資料,以便加深對資料層性能的理解。

TIP 入門

TIP 會動態添加檢測代碼,用於在分析運行期間測量應用程式的資料層調用的持續時間。Visual Studio 分析工具在 Visual Studio 2010 Premium Edition 和 Visual Studio 2010 Ultimate Edition 中提供。

若要啟動分析會話,可以從“Analyze”(分析)功能表按一下“Launch Performance Wizard”(啟動性能嚮導),也可以從“Debug”(調試)功能表按一下“Start Performance Analysis”(啟動性能分析),或者使用 Alt+F2 快速鍵。在性能嚮導的第一頁上,將要求您選擇一種分析方法。

TIP 適用于任何一種分析方法(採樣、檢測、記憶體或併發),但預設情況下未啟用。若要啟用 TIP,您需要在性能嚮導的第三頁上取消選中“在嚮導完成後啟動分析”核取方塊。(由於 TIP 尚未啟用,您現在無法啟動應用程式並開始分析。)為了得到最佳結果,建議在一開始選擇採樣分析方法,這尤其適用于您最注重的是資料層交互資料的情況。這主要是因為採樣對應用程式性能的影響最小。

若要啟用 TIP 資料收集,請在性能嚮導中訪問剛剛在“Performance Explorer”(性能資源管理器)視窗中創建的性能會話,然後按右鍵以查看其屬性。在屬性對話方塊中,選擇“Tier Interactions”(層交互)選項卡,然後選中“Enable tier interaction profiling”(啟用層交互分析)核取方塊。按一下“OK”(確定)按鈕可關閉該對話方塊。此時 TIP 已經啟用,您已準備好開始對應用程式進行分析。

若要開始運行實際分析,請按一下“Performance Explorer”(性能資源管理器)視窗中的“Launch with Profiling”(啟動並分析)工具列按鈕。

有關在 Visual Studio 中啟用 TIP 的完整說明,請參見 Habib Heydarian 的博客文章“Walkthrough:Using the Tier Interaction Profiler in Visual Studio Team System 2010”,位址為 https://blogs.msdn.com/b/habibh/archive/2009/06/30/walkthrough-using-the-tier-interaction-profiler-in-visual-studio-team-system-2010.aspx

TIP 測量性能的方法

TIP 在分析運行期間將代碼插入應用程式,用於測量對應用程式所使用的 ADO.NET 資料層的調用。當層交互分析活動時,Visual Studio 分析器檢查解決方案中的 Microsoft 中間語言 (MSIL),查找對 ADO.NET 函數的引用。調用即時 (JIT) 編譯器以生成由應用程式運行的本機代碼之前,分析器插入向關鍵 ADO.NET 方法添加檢測代碼的指令。此檢測代碼跟蹤每次 ADO.NET 調用期間所用的時間量。

隨著應用程式執行,TIP 將捕獲和記錄計時資料。對應用程式進行分析時,此檢測代碼將記錄執行的所有 ADO.NET 調用的持續時間,同時還捕獲資料庫調用中使用的命令文本的副本。在應用程式運行期間,TIP 會收集使用 ADO.NET 類同步方法(包括 SQL、OLE DB、開放資料庫連接 (ODBC) 和 SQL Server Compact (SQL CE) API)執行的所有資料庫訪問的計時資料。如果應用程式使用 LINQ to SQL 或實體框架 API 訪問 SQL 資料庫,則 TIP 還將捕獲計時資料。

計時資料與分析會話期間收集的所有其他資料一起存儲到 Visual Studio 分析器檔 (.vsp) 中。由於調用外部資料庫的應用程式執行的是進程外調用,因此 TIP 添加用於檢測應用程式的指令對應用程式整體性能產生的影響非常小。

性能優化

在常見設計模式中,將 Web 應用程式劃分為展示層、業務邏輯層和資料層。此設計模式會導致將應用程式劃分為各個元件,以實現可靠性、可擴展性和可伸縮性。多層應用程式使用業務邏輯元件訪問其資料層。這些元件將資料實體在邏輯上組織為一組相關表中的行和列,以此引用資料實體。根據設計,SQL Server 等資料庫維護與資料庫表關聯的物理資料的方式對應用程式是透明的。

出於可靠性和可伸縮性考慮,大型 Web 應用程式通常在池中配置多台電腦,負責與應用程式的各層關聯的邏輯處理。使用多台電腦支援多個層在性能分析方面造成了特殊的挑戰,這是因為監視任意一台電腦都只能提供不完整的應用程式資訊。

例如,使用 SQL Server 之類的資料庫系統管理和仲裁對應用程式資料存儲的訪問,會造成資料層與應用程式邏輯的隔離。駐留在諸如 SQL Server 之類的資料庫中的應用程式資料在獨立的進程位址空間中維護,具有自己的資料存儲。包含應用程式資料的資料庫可以與應用程式駐留在同一台物理電腦上,但更傾向于使用網路通訊協定從不同電腦進行訪問。

應用程式傳遞到外部資料庫的 SQL 命令以及在資料庫上操作的命令為進程外調用。採樣分析在等待這些進程外調用完成時,將應用程式視為休眠,但無法確定應用程式等待的原因或者這些延遲的持續時間。檢測和併發分析會測量這些延遲的持續時間,但無法確定所發佈的 SQL 命令以及這些命令花費如此長的時間來完成的原因。

在與外部資料庫通信的多層應用程式中,資料庫元件通常在整體應用程式回應時間中佔據主要比例。Visual Studio 分析工具包括多種分析方法,其中有採樣、檢測、記憶體分配、和併發,但如果沒有 TIP 資料,這些方法對於確定與訪問外部資料庫相關的性能問題都沒有多少説明。

使用 TIP 資料可以深入剖析與資料庫相關的延遲,並瞭解發生延遲的原因。將此資料與資料庫供應商提供的其他性能工具一起使用時,您還可以瞭解採取哪些操作來提高很大程度上依賴于資料庫性能的應用程式的性能。

由於 TIP 將檢測代碼添加到應用程式碼中,因此可以收集與資料庫命令相關的計時資料而不受所訪問資料庫實例的物理位置的影響。例如,對於與應用程式駐留在相同物理電腦上的 SQL Server 實例(這是單元測試中的一種常見情況),可以收集該實例的進程外調用的計時資料。當同一個應用程式準備好針對其他物理電腦上的 SQL Server 實例執行集成或負載測試時,TIP 可以繼續收集該配置的測量資料。實際上,使用 TIP 測量可以比較這兩個不同配置的性能。

您可以通過 TIP 比較和對照多個外部資料庫性能和可用優化選項的影響,包括快取記憶體配置、物理資料存放裝置、資料庫分區、資料庫索引和資料庫表設計。此外,您可以直接測量在虛擬機器上運行 SQL Server 的性能影響。

TIP 報告基本資訊

當啟動了 TIP 的分析會話完成後,將在“層交互”視圖中匯總應用程式與其 ADO.NET 資料層的任意交互的相關計時資料。图 1 顯示啟動了 TIP 資料收集並且在分析運行期間存在 ADO.NET 活動的分析器示例。

圖 1 Visual Studio 分析器層交互報告

報告的上半部分是收集的分析資料的摘要。對於 ASP.NET 應用程式,該視圖按 URL 組織。報告按照 URL 對 Web 應用程式 GET 請求的伺服器端回應時間分組。

在應用程式層下,報告顯示與資料庫層(在本例中為 AdventureWorks 示例資料庫)的各個連接。其中測量和報告的是 ASP.NET 請求的伺服器端處理時間部分,這一部分與使用 ADO.NET 的同步資料庫調用相關。在本例中顯示了三行摘要,每一行匯總了與所分析網站中三個不同 ASP.NET 頁面相關的資料庫活動。對於在分析期間標識的每個 ASP.NET 頁面,將報告分析運行期間處理的 ASP.NET 請求數以及生成的每個回應訊息的伺服器端回應時間。

額外的摘要行顯示其他 GET 請求的回應時間資料,包括對樣式表、Javascript 代碼和頁面中連結的圖像的請求。分析器無法與特定 ASP.NET 請求關聯的任何資料庫調用都分組在“Other Requests”(其他請求)類別下。

在分析使用資料層的 Windows 桌面或主控台應用程式時,該報告將按進程名稱對 ADO.NET 活動進行劃分。

在每個網頁摘要行下列出單獨一行摘要,其中報告在 ASP.NET 處理期間發出的同步資料庫請求數,這些請求按照資料庫連接進行組織。在本例中,您可以看到在單個資料庫連接中處理了六個對 CustomerQuery.aspx 的 ASP.NET 請求。這六個請求在伺服器上的總處理用時為 0.959 秒,平均回應時間為 160 毫秒。這些請求發佈了 12 個 SQL 查詢,用時約 45 毫秒完成。與為此網頁生成回應訊息相關的用時中,對資料庫請求的等待時間僅占約 5%。

如果突出顯示其中一個資料庫連接摘要行,則“層交互”視圖的下半部分將詳細列出應用程式發出的特定 SQL 命令。SQL 命令按照發佈的命令文本分組,並按照在該頁面組中的用時排序。

在本示例中,一條 SQL 命令發佈了三次,另一條命令發佈了六次,第三個查詢發佈了三次。對於詳細視圖,在摘要報告中累計到單行中的各個特定查詢的用時將分別報告。您可以查看總用時、該命令在所有實例上的平均用時以及對於每個查詢所觀察到的最短和最長延遲。

如果按兩下 SQL 命令詳細資訊行,則將在“Database Command Text”(資料庫命令文本)視窗中顯示所發佈的 SQL 命令的完整文本。這是應用程式在執行期間通過 ADO.NET 介面傳遞到資料庫的實際命令。如果請求針對的是存儲過程的執行,則將顯示對存儲過程的特定調用。

LINQ to SQL 示例

現在介紹一個使用 TIP 的簡單示例,通過它可以瞭解很大程度上依賴于從資料庫訪問資訊的 ASP.NET 應用程式。

對於使用 LINQ to SQL 訪問存儲在外部 SQL Server 資料庫中的資料的應用程式,TIP 尤為有用,這是因為 LINQ 的目的是讓開發人員無需深入瞭解物理資料庫及其性能特徵。使用 LINQ to SQL,在物件關係設計器中創建的“實體:關係”(E:R) 圖表會生成隨後由 Visual Studio 用作範本的類,用於自動構建語法正確的 SQL 命令。

由於使用 LINQ to SQL 時無需考慮大部分 SQL 語言編碼方面的注意事項,因此 LINQ to SQL 也常常會掩蓋與資料庫設計、配置和優化相關的重要性能注意事項。如本例所述,使用 LINQ,您可以方便地創建聯接多個表的複雜查詢,而無需考慮這樣做的性能影響。

使用 TIP,您可以查看 LINQ to SQL 生成的實際 SQL 命令文本,並收集這些 SQL 查詢的運行時執行的度量。然後,您可以使用手頭的 SQL 命令文本訪問其他資料庫優化工具,説明您更好地瞭解任意特定 LINQ to SQL 操作對性能的影響。

本文中的示例是一個 Web 表單應用程式,該應用程式使用特定客戶 ID 查詢 AdventureWorks Sales.Customer 表來檢索該客戶的訂單歷史記錄。此查詢中涉及的 AdventureWorks 表包括 Customer、SalesOrderHeader、SalesOrderDetail 和 StateProvince 表,如圖 2 中的物件關係設計器視圖所示。

圖 2 查詢 Sales.Customer 資訊所用的 AdventureWorks 表

如果您希望隨訂單歷史記錄顯示客戶的郵寄位址和電子郵寄地址資訊,則需要訪問 CustomerAddress、Address 和 Contact 表。如物件關係設計器中所示,AdventureWorks 表包含 CustomerID、SalesOrder 和 ContactID 等主鍵和外鍵,使得這些表可以按照邏輯方式聯接起來。

图 3 中顯示了使用 LINQ to SQL 創建 AdventureWorks 客戶查詢的 C# 代碼。在本例中,custid 是請求的特定 CustomerID 值。此查詢返回一個 customeryquery 集合,其中包含單獨的一行資料,提供在 select new 子句中列出的資料欄位。

圖 3 LINQ to SQL 客戶查詢

var customerquery = 
  from customers in db.Customers
  from custaddrs in db.CustomerAddresses
  from addrs in db.Addresses
  where (customers.CustomerID == custid &&
         customers.CustomerID == custaddrs.CustomerID &&
         custaddrs.AddressID == addrs.AddressID)

  select new {
    customers.CustomerID,
    customers.CustomerType,
    addrs.AddressLine1,
    addrs.AddressLine2,
    addrs.City,
    addrs.StateProvince,
    addrs.PostalCode,
    customers.TerritoryID
  };

然後,可以將 customeryquery 綁定到 ASP.NET 網頁上的控制項:

DetailsView1.DataSource = customerquery;
DetailsView1.DataBind();

現在,可以創建查詢以檢索此客戶的訂單歷史記錄:

var orderquery = 
  from orderhdr in db.SalesOrderHeaders
  where (orderhdr.CustomerID == custid)
  orderby orderhdr.OrderDate
  select new {
    Date = orderhdr.OrderDate.ToShortDateString(),
    orderhdr.AccountNumber,
    InvoiceNo = orderhdr.SalesOrderID,
    orderhdr.TotalDue
  };

執行此 LINQ to SQL 操作時,orderquery 將包含與特定客戶 ID 關聯的 OrderHdr 表中每一行相對應的行。 如果客戶歷史記錄指示有多個銷售交易,則 orderquery 集合將包含多行。

這些查詢表面上非常簡明。 但是,使用 TIP 便可瞭解這些看上去簡單的 LINQ to SQL 操作的性能影響。

使用 TIP 資料進行優化

現在,讓我們進一步瞭解 customerquery。 在運行時,LINQ to SQL 使用 LINQ 語句中隱含的邏輯資料庫 SELECT 操作,並使用它生成聯接以下四個 AdventureWorks 表中的資料的有效 SQL 命令:Customers、CustomerAddresses、Addresses 和靜態 StateProvince 表。 在此處的 LINQ to SQL 代碼中看不到這一點。

在 Visual Studio 分析器下運行此代碼時,TIP 檢測會報告此查詢執行的次數,並測量網頁等待執行的延遲時間。 實際上,這是在分析運行期間執行了六次的操作,如圖 1 中所示。

此外,如上文所述,LINQ to SQL 代碼生成的 SQL 命令在分析應用程式時也可用。 图 4 顯示了此操作的實際 SQL 命令。

圖 4 customerquery 的 SQL 命令

SELECT [t0].[CustomerID], [t0].[CustomerType], [t2].[AddressLine1], [t2].[AddressLine2], [t2].[City], [t3].[StateProvinceID], [t3].[StateProvinceCode], [t3].[CountryRegionCode], [t3].[IsOnlyStateProvinceFlag], [t3].[Name], [t3].[TerritoryID], [t3].[rowguid], [t3].[ModifiedDate], [t2].[PostalCode], [t0].[TerritoryID] AS [TerritoryID2]
FROM [Sales].[Customer] AS [t0]
CROSS JOIN [Sales].[CustomerAddress] AS [t1]
CROSS JOIN [Person].[Address] AS [t2]
INNER JOIN [Person].[StateProvince] AS [t3] ON [t3].[StateProvinceID] = [t2].[StateProvinceID]
WHERE ([t0].[CustomerID] = @p0) AND ([t0].[CustomerID] = [t1].[CustomerID]) AND ([t1].[AddressID] = [t2].[AddressID])

請注意,SQL 命令文本中包括一個權杖(在此處指定為“@p0”),用於表示 LINQ 提供給查詢中的客戶 ID 參數。

現在,已經得到了由 LINQ 生成的實際 SQL 命令文本,可以瞭解資料庫設計如何影響查詢的性能。

此時可以執行的操作是在 SQL Server Management Studio 中執行此 SQL 命令,並檢查其執行計畫,如圖 5 中所示。 若要訪問此查詢的執行計畫,需要添加命令以指向合適的資料庫:

USE AdventureWorks ;
GO

接下來,從 TIP 報告中複製 SQL 命令文本,請記住使用資料庫中的有效 CustomerID 替換“@p0”權杖。然後,在 SQL Server Management Studio 中執行此示例查詢並訪問執行計畫,該執行計畫顯示查詢最佳化工具如何將邏輯請求轉換為物理執行計畫。

圖 5 示例 LINQ to SQL 操作的執行計畫

在本例中,查詢的執行計畫顯示 SELECT 語句使用 CustomerID 欄位上的聚集索引訪問 Customer 表,該語句返回並且只返回表中的一行。在 SQL Server Management Studio 中,您可以將滑鼠懸停在某個操作上以查看其屬性,或者突出顯示該操作並按右鍵以查看“Properties”(屬性)視窗。使用這種方式,您可以迴圈逐個查看該命令請求的其餘幾個操作。接下來的三個 JOIN 中,每個都會增加初始的 Customer SELECT,還將使用聚集索引訪問表並返回單獨一行。

通過此調查您可以看到,處理此查詢總共需要訪問四行,每一行來自 AdventureWorks 資料庫中的一個不同表。每次訪問均使用表的唯一主鍵有效執行。

與此類似,您可以使用 TIP 查看 orderquery 代碼的 SQL 命令並將其提供給 SQL Server Management Studio 以查看其執行計畫(參見圖 6)。此查詢使用 CustomerID 作為外鍵訪問一個名為 OrderHdr 的表,因此需要訪問 SalesOrderHeaderID 上的普通非聚集索引以及聚集索引。

圖 6 orderquery 的執行計畫

這個特殊的查詢實例返回九行。LINQ to SQL 代碼中的 orderby 子句轉換為 SQL ORDER BY 子句,使得在 SELECT 的結果集上執行一個額外的排序操作。根據 SQL Server 計畫優化程式的估算,此操作佔用了請求總執行成本的 40%。

選擇分析上下文

TIP 旨在為現有 Visual Studio 分析方法提供補充,用於收集資料層交互上的特定度量。TIP 是輔助的資料收集工具,必須指定主要分析方法才能收集資料。對於任何使用 ADO.NET 與資料層通信的應用程式,可以在採樣、檢測和併發分析運行期間收集 TIP 資料。

假設您需要為將收集 TIP 資料的應用程式選擇一種主要分析方法,您將使用哪種方法?現在讓我們瞭解選擇主要分析方法的一些注意事項。

在性能研究中是否主要關注與資料層交互相關的延遲?如果是,則建議使用採樣分析作為主要方法,因為此方法通常是干擾最少的一種分析形式。

如果在性能研究中主要關注的不是資料層延遲,請根據在當前上下文中哪種分析方法能夠提供最合適的測量資料來做出選擇。例如,如果要研究與多執行緒併發執行相關的問題,則收集併發資料。如果要研究與 CPU 密集型應用程式相關的問題,則收集採樣資料。有關如何選擇主要收集方法的更多指南,請參見文章“如何:選擇收集方法”。

如果您尚不熟悉資料層代碼,則可能需要從主要分析資料中獲取説明,以找到發起這些 ADO.NET 調用的準確代碼。TIP 在收集同步 ADO.NET 進程外調用的計時資訊時不捕獲調用堆疊。如果需要瞭解在應用程式中對 ADO.NET 方法進行調用的位置,則檢測分析最為有用。採樣資料也會有所説明,但其精度不如檢測分析。

您可以選擇同時收集資源爭用資料和層交互度量,但收集爭用資料相比採樣往往是較高開銷的函數,並且爭用資料對於確定發起特定 ADO.NET 調用的位置一般沒有任何説明。對於需要 .NET 記憶體分配分析(通常具有很大的影響)的調查,通常也不會從收集層交互度量中獲益。

採樣分析

一般而言,在性能研究中資料層交互本身是主要關注物件。在本例中,選擇採樣作為主要分析方法通常會獲得最佳結果。在本例中,由於這通常是對應用程式性能影響最小的分析方法,因此首選採樣方法。在定位到發起對性能影響最大的 ADO.NET 調用的原始程式碼時,採樣分析也會非常有用。

對於在採樣分析期間收集的指令執行樣本,在使用在進程外運行的資料層函數時,通常不反映應用程式等待通過 ADO.NET 介面進行的同步調用完成所花費的任何時間。在應用程式的執行執行緒等待這些進程外調用完成期間,將阻止應用程式執行緒並且不對其記錄任何執行樣本。使用採樣時,瞭解哪些應用程式延遲是由於對資料層的同步調用而造成的最佳方法是收集 TIP 資料。

TIP 使用的檢測在收集計時資料時不捕獲任何調用堆疊。因此,如果您對分層應用程式進行分析並且不完全熟悉代碼,則可能難於準確確定發起資料層調用的位置。採樣分析同樣也有助於確定應用程式碼中對這些 ADO.NET 介面執行調用的位置。如果應用程式頻繁調用 ADO.NET 介面,則很有可能會收集一些顯示在 ADO.NET 模組(包括 System.Data.dll 和 System.Data.Linq.dll)中用時的樣本。

在檢查採樣資料並將其與層交互度量進行比較時,請記住,在等待同步資料庫調用完成而阻止應用程式執行緒時,將不收集執行緒的任何採樣資料。樣本在執行期間會累積,但不包括 TIP 明確測量的進程外延遲。不過,您可以預計在 ADO.NET 方法中收集的執行樣本與 TIP 觀察和測量的 ADO.NET 命令數之間存在大致的關聯。在這些情況下,採樣分析有助於您定位到發出 TIP 測量和報告的 ADO.NET 調用的原始程式碼。

請注意,如果應用程式的 SQL 查詢返回很大的結果集,隨後這些結果集綁定到表單上的資料繫結控制項,則您會在控制項的 DataBind 方法中發現非常高的執行採樣數。查看採樣分析中出現哪些 DataBind 方法也有助於定位到發起 ADO.NET 調用的原始程式碼。

檢測分析

收集檢測分析時,檢測記錄的方法的計時資料已經包括了方法中等待進程外調用完成的任意用時。對於選定進行檢測的應用程式方法,將測量其各個方法進入和退出的時間,以此來收集在檢測分析中記錄的計時資料。對於應用程式中使用 ADO.NET 調用與資料層交互的方法,其計時資料中已隱式包含了執行任何進程外調用的延遲。

從 TIP 收集的計時資料單獨明確地確定和測量進程外延遲。通過層交互分析測量的延遲,應該為在檢測分析運行期間測量的方法內部總用時的子集。瞭解這一點以後,您應該能夠將層交互分析的計時資料與在檢測分析的方法級別上收集的計時資料進行匹配,以查明發起資料層調用的方法。

如果使用方法級別的檢測足以確定應用程式中發起任意 ADO.NET 調用的位置,則可以毫不猶豫地使用檢測分析。但是,檢測分析相比採樣分析的干擾通常會大得多,這會帶來較大的開銷,並往往會生成非常大的 .vsp 收集檔。此外,如果應用程式使用多次調用 ADO.NET 函數的方法,則檢測收集的資料僅能説明您定位到方法級別,而無法區分內嵌到單個方法內部的多個 ADO.NET 調用。

獲取更多資料

構建多層應用程式這種設計模式有助於提高可靠性和可伸縮性,但當應用程式元件在不同電腦上執行時會帶來性能監視上的困難。

對於多層應用程式而言,一個未涵蓋其相互關聯的各層的簡單視圖不能提供完整的性能資訊。正如您所看到的,TIP 可以提供其他方法所無法提供的關鍵計時資料。如本文中的示例建議,此計時資料與通過其他標準資料庫管理工具得到的性能資料一起使用時可以發揮更大的作用。

Mark Friedman 在 Microsoft 的 Visual Studio Ultimate 團隊擔任架構師。他已出版了兩本關於 Windows 性能的書籍,並且定期發佈有關性能問題的博客文章,位址為 blogs.msdn.com/ddperf/

衷心感謝以下技術專家參與本文的審閱:Daryush LaqabChris Schmich