【问题标题】:Reduce execution time减少执行时间
【发布时间】:2012-12-31 22:28:57
【问题描述】:

我有一个程序,它使用 HashSet 简单地删除字符数组的重复元素。

这是我的程序:

import java.util.Arrays;
import java.util.HashSet;

import java.util.Set;

public class MainClass {
    public static void main(String[] arg) {
        double sT = System.nanoTime();
        Character[] data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
                'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
                'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
                'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f',
                'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
                's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd',
                'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
                'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b',
                'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
                'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
                'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
                'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
                'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
                'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
                'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
                'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                'u', 'v', 'w', 'x', 'y', 'z' };

        Set<Character > uniqueSet = new HashSet<Character>(Arrays.asList(data));

         Character[] strArr = new Character[uniqueSet.size()];
         uniqueSet.toArray(strArr);

            for(Character str:strArr){
                System.out.println(str);
            }


        System.out.println(System.nanoTime() - sT);

    }

}

它给出了所需的输出。但问题是执行时间。有什么方法可以在我的程序中实现以减少其执行时间?

【问题讨论】:

  • 怎么这么慢?它看起来非常合理......(除了也许你想在第一个.nanoTime()之前分配你的char数组之前
  • ..不要在你的时间里包括打印...
  • @fge 是的。但它所花费的时间是 1319709.0 纳秒。平均。但是,我需要它不超过 90000 纳秒。
  • 定时 one 执行也无济于事。请注意,有 JIT 启动等分析您的代码等。不要计时第一次执行!
  • 您的代码对我来说似乎很好。您可以通过 data[] 数组,填充 uniqueSet 并将结果写入一个组合 for 循环中的屏幕。但它只会节省最少的时间。

标签: java performance optimization hashset


【解决方案1】:

由于您可以拥有的不同类型的元素非常小,您可以轻松地使用简单的数组而不是哈希集(一种类似于集合或计数排序的方法)。如果您只关心非大写英文字母,请声明数组boolean met[26];,如果您需要能够支持所有字符,请使用boolean met[256];

遍历数组并仅在其 met 值为 false 时将字符添加到结果中。将字符添加到结果中时,不要忘记将其标记为已使用。

不涉及散列,因此 - 更好的性能。

编辑:我的意思似乎有些混乱,我将尝试添加代码示例

boolean met[] = new boolean[256]; // Replace 256 with the size of alphabet you are using
List<Character> res = new ArrayList<Character>();
for(Character c:data){
  int int_val = (int)c.charValue();
  if (!met[int_val]) {
     met[int_val] = true;
     res.add(c);
  }
}

// res holds the answer.

【讨论】:

  • 字符至少需要 16 位。并且输入数据数组中只有 200 项。所以我打赌你的方法会比散列集慢,因为当你想把输出写到屏幕上时,你需要通过那些 65536 个项目。但是,如果 OP 只要求 26 个英文字母,那将会发生巨大的变化。
  • @AlKepp 很抱歉,但我认为我不理解您的评论。我建议使用大小为 256 的数组而不是哈希集。此外,我的方法不需要散列 - 只需将具有与字符对应的索引的元素标记为 true。 16 位是什么意思?
  • @AlKepp 他只使用布尔数组作为标记,并且可以通过字符值索引数组元素
  • @AlKepp 在编辑您的评论后 - 您永远不会检查数组。如果数组中的相应单元格仍然为假,则仅将元素添加到结果中。所以你只迭代不可避免的输入。
  • @IvayloStrandjev 我觉得你的回答很有帮助。你能解释清楚一点吗?
【解决方案2】:

首先要意识到,写入控制台非常昂贵。如果你删除

System.out.println(str);

这将大大加快代码速度。

另外需要注意的是,您的代码运行时间不足以预热代码(它不会被编译)您应该运行测试大约 2 到 10 秒,看看它需要多长时间热身代码。

花费的时间

  • 打印 => 2,498,085 ns
  • 不打印 => 1,509,978 ns
  • 如果您先警告代码 => 43,347 ns
  • 如果您预热代码并平均运行 10,000 次 => 18,922 ns
  • 进一步优化代码并运行一百万次 ~2secs => 1,287 ns

端到端性能提升了 2000 倍 ;)

最终代码看起来像

StringBuilder strArr = null;
long sT = 0;
int runs = 1000000;
for (int i = -40000; i < runs; i++) {
    if (i == 0)
        sT = System.nanoTime();

    String text = "qwertyuiopasdfghjklzxcvbnm" +
            "qwertyuiopasdfghjklzxcvbnm" +
            "qwertyuiopasdfghjklzxcvbnm" +
            "qwertyuiopasdfghjklzxcvbnm" +
            "qwertyuiopasdfghjklzxcvbnm" +
            "qwertyuiopasdfghjklzxcvbnm" +
            "qwertyuiopasdfghjklzxcvbnm" +
            "qwertyuiopasdfghjklzxcvbnm" +
            "qwertyuiopasdfghjklzxcvbnm";
    BitSet bs = new BitSet(256);
    for (int j = 0, len = text.length(); j < len; j++)
        bs.set(text.charAt(j));
    strArr = new StringBuilder();
    for (int j = -1; (j = bs.nextSetBit(j+1)) >= 0; )
        strArr.append((char) j);
}

System.out.printf("Took an average of %,d ns%n", (System.nanoTime() - sT) / runs);

System.out.print(strArr);

打印

Took an average of 1,287 ns
abcdefghijklmnopqrstuvwxyz

【讨论】:

  • 1.3 纳秒不可能是正确的,这里有些东西正在编译不存在。
  • @MarkoTopolnik 1.3 ns 是错误的(大约 4 个时钟周期)。它是 1287 ns(大约 4000 个时钟周期)。 ;)
  • 是的,我的 Caliper 基准测试证实了这一点。顺便说一句,我修改了您的代码以使用new String(...),因为我认为每次都为创建字符串付费更为现实(OP 可能不会在常量上执行此操作,否则运行时将无关紧要)。我也坚持创建最终的char[]
【解决方案3】:

让 Google Caliper 负责微基准测试:

 0% Scenario{vm=java, trial=0, benchmark=Array} 12868.77 ns; σ=523.07 ns @ 10 trials

  us
12.9

vm: java
trial: 0
benchmark: Array

12.9 微秒。和你的 1319 微秒不太一样,是吗:)

作为参考,这是确切的代码:

import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;

public class Performance extends SimpleBenchmark {
  public int timeArray(int reps) {
    int sum = 0;
    for (int rep = 0; rep < reps; rep++) {
      Character[] data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
          'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
          'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b',
          'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
          'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
          'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
          'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
          'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
          't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
          'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e',
          'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
          'y', 'z' };
      sum += new HashSet<>(Arrays.asList(data))
          .toArray(new Character[new HashSet<Character>
                   (Arrays.asList(data)).size()]).length;
    }
    return sum;
  }

  public static void main(String... args) {
    Runner.main(Performance.class, args);
  }
}

【讨论】:

    【解决方案4】:

    您可以使用 Google Guava 为您完成这项工作。有一个关于它的Stackoverflow discussion

    【讨论】:

    • 虽然 Guava 可以提高可读性,但出于性能考虑,我不推荐它。
    猜你喜欢
    • 1970-01-01
    • 2018-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多