軟體的效能設計(一)介面設計對軟體效能的影響 (轉)

worldblog發表於2007-12-07
軟體的效能設計(一)介面設計對軟體效能的影響 (轉)[@more@]

  的設計(一)介面設計對軟體效能的影響 
  劉彥清·yesky

  效能方面的問題有好多種。最容易修正的一種是,在一項計算任務時使用了一個效能不好的演算法,例如,在對數目很多的資料進行排序時採用了起泡演算法,每次使用時對一個經常使用的資料項進行計算而不是將它儲存起來,這些問題一般我們都能很容易發現,而且一旦發現後,都能很方便地進行改正。然而,許多效能方面的問題都是是由一些比較深奧的、不容易修改的程式碼━━程式的介面設計引起的。

  大多數的程式都是由內部人員開發的或從外部購買的元件"組裝"而成的。即使軟體不完全依賴於原有的元件,面向的設計過程也使得應用程式在開發時採用元件形式,因為這樣可以簡化程式的設計、開發和方面的工作。儘管採用元件的好處是不可否認的,我們還應該意識到元件的介面會對使用它們的程式的效能和執行狀態產生重大的影響。

  也許會有讀者問,介面跟效能有什麼關係?一個類的介面不但定義了類可以完成的功能,而且還定義了它的物件建立行為和使用它所需要的方法的順序,一個類如何定義它的構造器和方法會影響這個物件是否可以重用,是它本身的方法建立還是要求其客戶建立中間物件,客戶要使用這個類需要呼叫多少個方法。

  所有這些因素都會影響到程式的效能。Java軟體效能管理方面的基本原理之一是:避免建立過多的物件。這並不意味著你不能建立任何物件從而不充分利用面象物件語言帶來的諸多好處,而是說在開發對效能敏感的程式碼時需要對物件的建立保持謹慎。物件建立的代價相當高昂,我們應該在對效能敏感的軟體中儘量避免建立臨時或中間物件。

  在處理字元的程式中,String類是引起物件建立的最大源。因為String類是不可變的,每當一個String類的物件被修改或構造時,都會建立一個新的物件。因此,一個具有效能意識的人員總是避免過多地使用String類物件。然而,儘管你在程式設計中儘量避免使用String物件,還是會經常發現使用的元件介面必須使用String物件,因此,你不可能不使用String類物件。

  例子:的匹配

  作為一個例子,可以假設你在編寫一個名字為Bot的。MailBot需要處理每個郵件頂部的MIME頭部━━例如傳送日期或者傳送者的郵件地址,它將透過使用一個匹配表示式的元件處理MIME頭部,以使這一處理過程會更簡單一些。它把輸入的字元放在一個字元緩衝區中,透過對緩衝區進行處理標題。由於MailBot將呼叫這一表示式匹配子程式來處理每一個標題,因此這個匹配子程式的效能將十分地重要。

  我們首先來看一個效能十分低下的表示式匹配類的介面:

  public class AwfulRegExpMatcher {

   /**建立一個給定表示式的匹配過程,它將對給定的字串進行處理*/

   public AwfulRegExpMatcher(String regExp, String inputText);

   /**找到針對輸入文字的下一個匹配,如果匹配,返回匹配的文字,否則返回一個空字元 */

   public String getNextMatch();

   }

即使這個類採用了一個很高效的匹配演算法,大量呼叫它的程式的效能也不會很好。因為匹配器物件是與輸入文字捆綁在一起的,每次呼叫它時,都需要首先生成一個新的匹配器物件。由於我們的目標是減少不必要的物件建立工作,實現對匹配過程程式碼的重用應該是一個良好的開端。

  下面的這個類定義了匹配器的另一種可能的介面,它允許匹配器重用,但效能仍然不夠好:

  public class BadRegExpMatcher {

   public BadRegExpMatcher(String regExp);

   /** 試圖針對輸入文字匹配指定的表示式,如果匹配則返回匹配的文字,否則返回一個空白字串*/

   public String match(String inputText);

   /** 得到下一個匹配的字元,否則返回一個空白字元*/

   public String getNextMatch();

  }

避開返回的匹配子表示式等敏感的表示式匹配問題不談,這個類的定義有什麼問題嗎?如果僅僅從其功能方面看,它沒有任何問題,但如果從效能方面來考慮,則它存在許多問題。首先,匹配器要求其呼叫者建立一個String類來表示被匹配的文字。MailBot應該儘量避免生成String物件,但當它發現一個需要處理的標題時,它必須建立一個String物件供BadRegExpMatcher呼叫:

  BadRegExpMatcher dateMatcher = new BadRegExpMatcher(...);

   while (...) {

    ...

    String headerLine = new String(myBuffer, thisHeaderStart,

      thisHeaderEnd-thisHeaderStart);

    String result = dateMatcher.match(headerLine);

    if (result == null) { ... }

  }

  其次,即使MailBot僅僅需要得到是否匹配的返回資訊,而無需得到匹配的文字,匹配器也會返回一個匹配的字串。這意味著為了簡單地使用BadRegExpMatcher來驗證一個特定格式的日期標題,你也必須建立二個 String物件━━供匹配器使用的輸入文字和匹配結果文字。建立二個物件似乎不會對效能產生重大影響,但如果必須為MailBot處理的每條郵件的標題建立二個物件,就可能嚴重地影響程式的效能。這一問題並不出在MailBot本身的設計上,而是出在BadRegExpMatcher的設計上。

  注意:不返回String物件而返回一個"輕量級"的Match物件也不會在效能上帶來很大的改進。儘管建立一個Match物件的代價要比建立一個String物件的代價低一些,它還是會產生一個char陣列,並複製資料,仍然建立了一個對呼叫者並非必需的臨時性的物件。

  BadRegExpMatcher只接受它需要的輸入資料型別,而不是可以接受我們方便提供的資料型別,僅就這一點,它就非常不理想。使用BadRegExpMatcher還會帶來別的危害,其中的一個潛在的危害是這樣將對MailBot的效能帶來更多的影響。儘管在處理郵件的標題時必須避免使用Strings,但又必須建立許多的Strings物件供BadRegExpMatcher使用,因此你可能放棄不使用String物件的目標,而更加不受限制地使用它。一個設計不恰當的元件會影響使用它的程式的效能,即使以後找到了一個無需使用String物件的表示式元件,整個程式仍然會受到影響。
 
一個恰當的介面

  如何定義BadRegExpMatcher才能避免上述的問題呢?首先,BadRegExpMatcher應該不指定其輸入文字的格式,它應該能夠接受其呼叫者可以高效地提供的任何一種資料型別。其次,它不應該為匹配結果自動地生成一個String物件,只需要返回足夠的資訊讓呼叫者來決定是否需要生成匹配結果字串。(也可以提供一個方法來完成這一任務,但這並非是必需的。)一個效能比較好的介面應該是這樣的:

  class BetterRegExpMatcher {

    public BetterRegExpMatcher(...);

    /** 使匹配器可以接受多種格式的輸入━━ String物件、字元陣列、字元組數的子集,如果不匹配,返回-1;如果匹配,則返回開始匹配的偏移地址。*/

    public int match(String inputText);

    public int match(char[] inputText);

    public int match(char[] inputText, int offset, int length);

    /** 如果匹配,則返回匹配的長度;如果不是完全匹配,則呼叫程式應該能夠從匹配的偏移處生成匹配的字串 */

    public int getMatchLength();

    /** 如果呼叫程式需要,就可以很方便地得到匹配字串的子程式 */

    public String getMatchText();

   }

  新的介面消除了呼叫者將輸入文字轉化為匹配子程式所要求的格式的需求。MailBot可以用如下的方式呼叫match():

  int resultOffset = dateMatcher.match(myBuffer, thisHeaderStart, thisHeaderEnd-thisHeaderStart);

  if (resultOffset < 0) { ... }

  這樣就既達到了設計目標又沒有建立任何新的物件,另外,它的介面設計也體現了Java所倡導的"多而簡單的方法"的設計思想。

  建立物件對效能的精確影響取決於match()完成的工作量。透過建立和對二個不作任何實際工作的表示式匹配程式類的執行進行計時,就會發現它們在效能上存在著巨大的差異,在Sun 1.3 中,使用BetterRegExpMatcher類的上述程式碼的執行速度比使用BadRegExpMatcher類快50倍。透過簡單地支援子串匹配,BetterRegExpMatcher的執行速度就可以比BadRegExpMatcher快5倍。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-989427/,如需轉載,請註明出處,否則將追究法律責任。

相關文章