2011-10-15 54 views
2

我已經在Objective-c中實現了幾乎相同的代碼,它的運行速度比Java中快兩到三倍。我試圖找出哪些指令可能是資源密集度最高的,並且看看是否有更好的方法來做同樣的事情,這在Java中更加高效。Java - 什麼使這段代碼運行得更快?

這是從數據庫中讀取大型結果集的例程的一部分,並且對於返回的每個單詞,它會檢查該單詞是否可以由玩傢俱有的字母瓦片製成。它包括對空白瓷磚的支持,可以用作任何字母。一個空白的瓷磚將由一個下劃線字符表示。

基本上,對於從數據庫返回的每個單詞,我遍歷單詞的每個字母,並查看可用字母的播放器數組。如果我找到那封信,我將它從玩家陣列中移除並繼續前進。如果我沒有找到該字母,則丟棄該字詞並讀取下一個字詞。除非在播放器的數組中找到下劃線字符,否則我將使用該字母作爲字母,並將其從數組中移除。如果我到達數據庫單詞的字母數組的末尾並且已經「找到」每個單詞,那麼該單詞將保存在列表中。

我已經計時了整個函數的各個部分,並且數據庫查詢發生得非常快。只是這個遊標的處理非常緩慢。任何建議,將不勝感激!

if (c.moveToFirst()) { 

    do { 
     boolean found = false; 
     int aValue = 0; 
     int letterValue = 0; 

     // Word and Word's length from the database 
     String sWord = c.getString(0); 
     int wordLength = c.getInt(1); 

     // Refresh the Tile array, underscores sorted to the front 
     // sortedTiles sorted the players tiles {_,_,a,b,c} 
     char[] aTiles = sortedTiles.clone(); 

     // Calculate the value of the word 
     for (int i = 0; i < wordLength; i++) { 

      // For each character in the database word 
      switch (sWord.charAt(i)) { 
      case 97: 
       letterValue = 1; 
       break; 
      case 98: 
       letterValue = 4; 
       break; 
      case 99: 
       letterValue = 4; 
       break; 
      case 100: 
       letterValue = 2; 
       break; 
      case 101: 
       letterValue = 1; 
       break; 
      case 102: 
       letterValue = 4; 
       break; 
      case 103: 
       letterValue = 3; 
       break; 
      case 104: 
       letterValue = 3; 
       break; 
      case 105: 
       letterValue = 1; 
       break; 
      case 106: 
       letterValue = 10; 
       break; 
      case 107: 
       letterValue = 5; 
       break; 
      case 108: 
       letterValue = 2; 
       break; 
      case 109: 
       letterValue = 4; 
       break; 
      case 110: 
       letterValue = 2; 
       break; 
      case 111: 
       letterValue = 1; 
       break; 
      case 112: 
       letterValue = 4; 
       break; 
      case 113: 
       letterValue = 10; 
       break; 
      case 114: 
       letterValue = 1; 
       break; 
      case 115: 
       letterValue = 1; 
       break; 
      case 116: 
       letterValue = 1; 
       break; 
      case 117: 
       letterValue = 2; 
       break; 
      case 118: 
       letterValue = 5; 
       break; 
      case 119: 
       letterValue = 4; 
       break; 
      case 120: 
       letterValue = 8; 
       break; 
      case 121: 
       letterValue = 3; 
       break; 
      case 122: 
       letterValue = 10; 
       break; 
      default: 
       letterValue = 0; 
       break; 
      } // switch 

      found = false; 

      // Underscores will be sorted to the front of the array, 
      // so start from the back so that we give 
      // real letters the first chance to be removed. 
      for (int j = aTiles.length - 1; j > -1; j--) { 
       if (aTiles[j] == sWord.charAt(i)) { 
        found = true; 
        // Increment the value of the word 
        aValue += letterValue; 

        // Blank out the player's tile so it is not reused 
        aTiles[j] = " ".charAt(0); 

        // I was removing the element from the array 
        // but I thought that might add overhead, so 
        // I switched to just blanking that letter out 
        // so that it wont be used again 
        //aTiles = removeItem(aTiles, j); 

        break; 
       } 

       if (aTiles[j] == cUnderscore) { 
        found = true; 

        // Blank out the player's tile so it is not reused 
        aTiles[j] = " ".charAt(0); 

        // I was removing the element from the array 
        // but I thought that might add overhead, so 
        // I switched to just blanking that letter out 
        // so that it wont be used again 
        //aTiles = removeItem(aTiles, j); 
        break; 
       } 

      } // for j 

      // If a letter in the word could not be fill by a tile 
      // or underscore, the word doesn't qualify 
      if (found == false) { 
       break; 
      } 

     } // for i 

     // If all the words letters were found, save the value and add to the list. 
     if (found == true) { 

      // if all the tiles were used it's worth extra points 
      String temp = aTiles.toString().trim(); 

      if (temp.length() < 1) { 
       aValue += 35; 
      } 

      Word word = new Word(); 
      word.word = sWord; 
      word.length = wordLength; 
      word.value = aValue; 
      listOfWords.add(word); 
     } 

    } while (c.moveToNext()); 
} 
+7

'「」.charAt(0)'可以簡單寫成'''' –

+0

您是否正在iphone上運行objective-c版本以及android上的java版本?如果是這樣,是相似速度的硬件? – blizpasta

+0

在通過每個字符開始循環之前,我會將字符串轉換爲字符數組。數組訪問可能比charAt()更快,即使charAt執行相同的操作,也會從堆棧中刪除額外的方法調用。還有+1 @Banthar爲這個小小的珍聞,對於來自C代碼的東西,這段代碼令人驚訝地反對。 – gnomed

回答

8

我不知道你的代碼大部分時間花在哪裏。你應該介紹一下。但我會用表查找替換您的長開關語句:

// In the class: 
private static final int[] letterValues = { 
    1, 4, 4, 2, 1, // etc. 
} 

// In the code: 

// Calculate the value of the word 
for (int i = 0; i < wordLength; i++) { 

    // For each character in the database word 
    char ch = sWord.charAt(i); 
    if (ch >= 97 && ch <= 122) { 
     letterValue = letterValues[ch - 97]; 
    } else { 
     letterValue = 0; 
    } 

    // the rest of your code 

這可能比switch語句快得多。

編輯:我注意到,在你的j循環內,你打電話sWord.charAt(i)爲每個j值。您可以通過將該函數調用從循環中分解出來來加快速度。如果您使用我的代碼,則可以使用ch代替sWord.charAt(i)

P.S.就風格而言,代碼if (found) { ...代替if (found == true) { ...更好。同樣使用if (!found) {而不是if (found == false) {

+0

轉換數據庫單詞,sWord轉換爲char數組聽起來不錯,並且/或者在j循環之外獲取charAt肯定會有所幫助。這也是獲得letterValue的好方法。 –

+0

我不知道轉換爲char數組有多大幫助,特別是如果您只爲每個字母位置調用一次'charAt'。然而,知道的唯一方法就是嘗試兩種方式並對其進行分析(至少在時間上)。 –

+0

我剛剛嘗試列出letterValues和50,000字的結果集,與使用switch語句相比,它削減了2秒的時間。而且,它在代碼中看起來更乾淨,謝謝! –

1

我認爲switch語句可能會被編譯器轉換成跳轉表,所以我沒有看到這個問題。

另一方面,您可能可以爲您的玩家的手使用更好的數據結構。現在,你基本上採用三重嵌套循環:

  1. 通過每一個字數據庫
  2. 迭代通過每一個角色球員的tile數組中遍歷,在這個詞
  3. 迭代每一個字符

前兩項無法避免。另一方面,第三個可以使用散列表或某種O(N)查找數據結構。

我可能會把手錶示爲27個整數的數組。每個元素代表一個字母或「_」,其值是手中的瓦片數量。當您找到匹配的圖塊時,可以減少其值。如果該值已經爲零,那麼你知道玩家沒有該圖塊。

但正如特德指出的,你最好的選擇是使用探查器來尋找最昂貴的電話。然後弄清楚如何儘可能少地打這些電話。

+0

我認爲switch語句可能是Android編譯器的一個問題。 –

1

你傾向於得到猜測的答案。

要做的事情是,在每個平臺上,只需squeeze the code until it's optimal。 那麼如果有任何速度差異,至少你會知道每個代碼儘可能快。

分析是經常推薦的,但是here's an example of how I do it

+0

感謝您的示例,我將通讀它來了解如何正確配置文件。我確實在整個代碼中都安裝了系統計時器,以試圖找出最大延遲的地方......更多細節在下面! –

+0

@邁克爾:是的,如果你擅長這一點,你實際上會有一種難得的技巧。一般的看法是「測量尺度」或「使用輪廓儀」,但如果你聽過(你不知道)達到了多少加速度,那可能就是10%-40%。相當貧血。不像第一個鏈接中的43 *倍*。 –

0

謝謝大家。我期待收到電子郵件提醒,所以,我沒有意識到人們已經在迴應。

我最終在遊標循環中的各個位置放置了系統時間的日誌打印輸出,以嘗試確定哪些時間花費最多。最初的moveToFirst()花費了大部分時間,但這是可以預料的,而且我對此無能爲力。我注意到大多數單詞在兩到三毫秒內處理完畢,應該足夠快,但是由於沒有明顯的原因,一個單詞需要20或30毫秒。我推斷在後臺必須有一些垃圾回收,所以我儘可能地減少了分配/釋放。在所有的循環中,而不是聲明變量(int i = 0 ... to for(i = 0,將變量聲明移動到方法的頂部,這有所幫助,但我仍然離開一行不變。當我改變了這一切,它在世界上起了重要作用。

// Refresh the Tile array, underscores sorted to the front 

    for (i = 0; i < sortedTiles.length; i++) { 
     aTiles[i] = sortedTiles[i]; 
    } 

    // Instead of doing it this way     
    //aTiles = sortedTiles.clone(); 

我分配aTiles光標環以上,而在這裏,我只是在球員的角色重新初始化它()這是一個需要頻繁進行垃圾回收的東西,並且會導致我的性能下降。

你們都提供了很好的建議,我會根據你的建議進一步調整代碼以獲得更好的性能非常感謝!

+0

你可以用這個調用替換那個循環:'System.arraycopy(sortedTiles,0,aTiles,0,sortedTiles.length);'。它不應該跑得快得多,但它更清潔一些。 –

+0

int不會被垃圾收集。只有引用可以被垃圾回收,而不是基元。 – TofuBeer

+0

好的,但它似乎仍然通過在代碼塊中聲明任何變量,它會導致該變量進入和退出範圍,並且我認爲在幕後,這會導致額外的時間爲該變量分配資源。即使它不會影響垃圾收集。我認爲在大多數情況下重複使用變量會更好。 –