天天看點

NET插件系統之四——提升系統搜尋插件和啟動速度的思考

一. 面臨的問題

  開發插件系統的主要優勢是擴充性,我們不需要為系統子產品的內建再多費腦筋,但這也帶來了額外的問題。通常,系統需要在每次啟動時搜尋固定目錄下的符合要求的插件。但是,當系統變得越來越龐大,所引用的dll檔案越來越多時,就會出現很嚴重的問題:開啟時間慢,性能差,使用者體驗降低,尤其是在調試程式時,會浪費大量寶貴的時間。

  我确确實實的面臨了這樣的問題,有興趣的讀者可以看看我的插件系列文章的前幾篇,這兩天痛定思痛,決心提升系統搜尋插件的性能。

  我們先看一段普通的搜尋插件的代碼:

  造成啟動慢的主要原因有:

  1. 目錄下包含大量dll檔案(這是因為項目引用了大量第三方庫),它們并不包含我們開發的元件,卻白白浪費大量搜尋時間。有些dll檔案不是托管dll,在擷取程式集時還會抛出異常,直接捕獲後,也會造成時間損失。

  2. 上述代碼僅搜尋了滿足一種接口規範的插件, (見函數的參數interfacename)。如果不止一種插件類型,那麼可能要做很多次這樣的查找,對性能的影響更大。

      3. 為了擷取插件的一些資訊(比如是否要在啟動時加載),不得不執行個體化其對象擷取字段,這種性能損失也是不能承受的。

二. 解決途徑

     找到問題,我們對症下藥:

  1.成熟的軟體系統采用了插件樹的機制,将插件存儲為樹結構,包含父子關系,這樣能盡可能的提升搜尋和加載性能,同時友善管理,比如ecilpse。 但是,這種複雜的插件管理機制可能不适用于我們開發的輕量級系統,是以我們僅僅考慮扁平化的插件結構。

  2. 雖然插件的數量是經常變化的,但通常加載的dll檔案種類很少變化。我們可以考慮把實際包含所需插件的dll檔案名清單存儲起來,進而在搜尋時僅搜尋這些固定的dll檔案,提升性能。

  3. 插件的種類可能多種多樣,是以我們希望能一次性獲得全部類型的插件。

  4. 采用.net4.0的并行庫機制實作插件的并行搜尋。

三. 插件結構表述

  我們定義兩個資料結構存儲插件名稱和插件資訊:

四. 插件搜尋的方法

  我們将插件搜尋的步驟分為兩步:

  1. 搜尋所有接口契約(即搜尋所有的接口)

    流程圖如下:

NET插件系統之四——提升系統搜尋插件和啟動速度的思考

  2. 搜尋所有實作接口契約的插件

   直接用代碼說話

 由于篇幅有限,搜尋插件的流程與搜尋插件名稱的流程基本相同,是以省略流程圖。

3. 并行化優化

  讀者可以看到,在搜尋不同dll檔案的插件時 ,使用了 parallel.foreach ,網上介紹該方法的文章很多,此處不再贅述。 同時,系統直接将所有插件一次性的搜尋完成。大幅度的提升了搜尋速度。

五. 總結和問題

  總結:

  1. 系統盡可能的減少了對插件本身的限制,通過添加attribute的方式即可标記插件,減少了對原生代碼的修改。

  2. 實測表明,檔案目錄下存在60個左右的dll檔案,其中隻有6個是作者完成的包含插件的檔案, 在i7 2600k的電腦上:(debug版本)

    原始版本的插件搜尋和執行個體化需要将近5s的啟動時間  

          通過緩存檔案目錄和插件目錄,時間減少2.7s

      通過并行化搜尋dll檔案下的插件,時間進一步減少1s

        最終,啟動時間僅僅為1.3s左右,同時還避免了多次搜尋插件

     存在的問題:

  1. 插件系統可以自動檢測出同一dll檔案中插件的變化,但在緩存了dll檔案名之後,是無法自動檢測出dll檔案的變化的。這種情況下,需要首先删除記錄插件名稱和檔案的緩存xml檔案才能檢測出來。

  2. 依然有一定的性能提升的餘地。   

  以下是我的插件搜尋器的完整代碼,歡迎有問題随時交流: