简体   繁体   English

Java - 什么可以使这段代码运行得更快?

[英]Java - What can make this code run faster?

I have implemented almost the same code in Objective-c, and it runs two to three times faster than it does in Java. 我在Objective-c中实现了几乎相同的代码,它的运行速度比Java快两到三倍。 I'm trying to figure out which instructions may be the most resource intensive and see if there is a better way of doing the same thing that is more efficient in Java. 我正在试图找出哪些指令可能是最耗费资源的,并且看看是否有更好的方法来做同样的事情,这在Java中更有效。

This is part of a routine that reads a large resultset from the database, and for each word that is returned, it checks to see if that word can be made from the letter tiles the player has. 这是从数据库中读取大型结果集的例程的一部分,对于返回的每个单词,它会检查该单词是否可以从播放器具有的字母区块中生成。 It includes support for blank tiles, which can be used as any letter. 它包括对空白图块的支持,可以用作任何字母。 A blank tile will be represented by an underscore character. 空白图块将由下划线字符表示。

Basically, for each word that is returned from the database, I iterate through each of the letters of the word, and look through the players array of available letters. 基本上,对于从数据库返回的每个单词,我遍历单词的每个字母,并查看可用字母的玩家数组。 If I find that letter, I remove it from the players array and keep going. 如果我找到那封信,我将其从玩家阵列中移除并继续前进。 If I don't find the letter, the word is discarded and the next word read. 如果我没有找到该字母,则该字被丢弃并且下一个字被读取。 Unless, I find an underscore character in the player's array, then, I'll use that for the letter, and remove it from the array. 除非,我在播放器的数组中找到一个下划线字符,然后,我将使用它作为字母,并将其从数组中删除。 If I get to the end of the database word's array of letters and have 'found' each one, then the word is saved in a list. 如果我到达数据库字的字母数组的末尾并且每个字母都“找到”,那么该单词将保存在列表中。

I've already timed various parts of the whole function and the database query happens pretty fast. 我已经计时了整个函数的各个部分,数据库查询发生得非常快。 It is just the processing of this cursor that is very slow. 这只是对这个游标的处理非常慢。 Any suggestions would be appreciated! 任何建议,将不胜感激!

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());
}

I don't know exactly where your code is spending most of its time. 我不知道您的代码在大部分时间内的确切位置。 You should profile for that. 你应该描述一下。 But I would replace your long switch statement with a table lookup: 但我会用表查找替换你的long switch语句:

// 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

This is likely to be much faster than a switch statement. 这可能比switch语句快得多。

EDIT: I notice that inside your j loop you are calling sWord.charAt(i) for each j value. 编辑:我注意到你的j循环中你为每个j值调用sWord.charAt(i) You can speed things up a bit more by factoring that function call out of the loop. 通过将函数调用分解出循环,可以加快速度。 If you use my code, you can just use ch in place of sWord.charAt(i) . 如果您使用我的代码,您可以使用ch代替sWord.charAt(i)

PS As a matter of style, it's nicer to code if (found) { ... instead of if (found == true) { ... . PS作为一种风格问题,编码if (found) { ...而不是if (found == true) { ... Likewise use if (!found) { instead of if (found == false) { . 同样使用if (!found) {而不是if (found == false) {

I think the switch statement will probably be turned into a jump table by the compiler, so I don't see an issue with that. 我认为switch语句可能会被编译器变成跳转表,所以我没有看到它的问题。

On the other hand, you can probably use a better data structure for your player's hand. 另一方面,您可以为玩家的手使用更好的数据结构。 Right now, you're basically using a triply nested loop: 现在,你基本上使用三重嵌套循环:

  1. Iterating through every word in the database 迭代数据库中的每个单词
  2. Iterating through every character in the word 迭代单词中的每个字符
  3. Iterating through every character in the player's tile array 迭代播放器的tile数组中的每个字符

The first two cannot be avoided. 前两个是无法避免的。 On the other hand, you can use a hash table or some kind of O(N) lookup data structure for the third. 另一方面,您可以为第三个使用哈希表或某种O(N)查找数据结构。

I'd probably represent the hand as an array of 27 ints. 我可能会将手牌代表27个整数的数组。 Each element represents a letter or "_", and its value is the number of tiles in the hand. 每个元素代表一个字母或“_”,其值是手中的瓦片数。 When you find a matching tile, you can decrement its value. 找到匹配的图块时,可以减小其值。 If the value is already zero, then you know the player doesn't have that tile. 如果该值已经为零,那么您就知道该播放器没有该磁贴。

But as Ted pointed out, your best bet is to use a profiler to find the most expensive calls. 但正如泰德所指出的,最好的办法是使用分析器找到最昂贵的电话。 Then figure out how to make as few of those calls as possible. 然后弄清楚如何尽可能少地进行这些调用。

You tend to get answers that are guesses. 你倾向于得到猜测的答案。

The thing to do is, on each platform, just squeeze the code until it's optimal . 要做的是,在每个平台上,只需挤压代码,直到它达到最佳状态 Then if there's any speed differential, at least you'll know each code is as fast as possible. 然后,如果有任何速度差异,至少你会知道每个代码尽可能快。

Profiling is what's often recommended, but here's an example of how I do it . 分析是经常推荐的,但这是我如何做的一个例子

Thanks everyone. 感谢大家。 I was expecting to get e-mail alerts, so, I didn't realize people were already responding. 我当时希望得到电子邮件警报,所以,我没有意识到人们已经在回应。

I ended up placing log printouts of system times at various places in the cursor loop to try and determine what was taking the most time. 我最终在光标循环的不同位置放置系统时间的日志打印输出,以尝试确定花费最多时间的内容。 The initial moveToFirst() took the most time, but that was to be expected, and there is really nothing I could do about it. 最初的moveToFirst()占用了大部分时间,但这是可以预料的,我真的无能为力。 I noticed that most words processed in two or three milliseconds, which should be plenty fast enough, but then, for no apparent reason, one word would take 20 or 30 ms. 我注意到大多数单词在两三毫秒内处理,这应该足够快,但是,由于没有明显的原因,一个单词需要20或30毫秒。 I reasoned that there must be some garbage collection going on in the background, so, I sought to reduce allocations/deallocations as much as possible. 我推断在后台必须进行一些垃圾收集,因此,我试图尽可能地减少分配/解除分配。 In all of the loops, rather than declare the variable like for (int i = 0... to for (i = 0, moving the variable declaration to the top of the method. This helped a little, but I'd still left one line untouched. When I changed that, it made all the difference in the world. 在所有的循环中,而不是声明变量,如(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();

I allocate aTiles above the cursor loop, and here, I'm just re-initializing it with the characters in the players hand. 我在光标循环上方分配了一个Tiles,在这里,我只是用玩家手中的角色重新初始化它。 This one clone() was what was making frequent garbage collection necessary, and killing my performance. 这一个clone()是必要的频繁垃圾收集,并杀死我的性能。

You've all provided great suggestions, and I'll further tweak the code to eek out better performance based on your advice. 你们都提供了很好的建议,我会进一步调整代码,根据你的建议,提高性能。 Thank you very much! 非常感谢你!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM