执行时间:迭代与实例

Execution Time: Iterativ vs Instance

我在 Java 中编码时偶然发现了一件奇怪的事情:

我将文件读入字节数组 (byte[] file_bytes),我想要的是 hexdump 输出(就像 Linux 中的实用程序 hexdumpxxd)。基本上这是可行的(请参阅 not 注释掉的 for 循环代码),但对于较大的文件(>100 KiB),需要一点时间才能通过字节数组块,做适当的格式化等等。

但是,如果我将 for-loop-code 与 is 注释掉的代码交换(使用具有相同 for-loop-code 的 class 进行计算!), 它工作得非常快。

这种行为的原因是什么?

代码段:

    [...]

    long counter = 1;
    int chunk_size = 512;
    int chunk_count = (int) Math.ceil((double) file_bytes.length / chunk_size);
    for (int i = 0; i < chunk_count; i++) {
        byte[] chunk = Arrays.copyOfRange(file_bytes, i * chunk_size, (i + 1) * chunk_size);

       // this commented two lines calculate way more faster than the for loop below, even though the calculation algorithm is the same!
       /* 
        * String test = new BytesToHexstring(chunk).getHexstring();
        * hex_string = hex_string.concat(test);
        */ 

        for (byte b : chunk) {
            if( (counter % 4) != 0 ){
                hex_string = hex_string.concat(String.format("%02X ", b));
            } else{
                hex_string = hex_string.concat(String.format("%02X\n", b)); 
            }
            counter++;
        }
    }

    [...]

class BytesToHexstring:

class BytesToHexstring {
    private String m_hexstring;

    public BytesToHexstringTask(byte[] ba) {
        m_hexstring = "";
        m_hexstring = bytes_to_hex_string(ba);
    }

    private String bytes_to_hex_string(byte[] ba) {
        String hexstring = "";
        int counter = 1;

        // same calculation algorithm like in the codesnippet above!
        for (byte b : ba) {
            if ((counter % 4) != 0) {
                hexstring = hexstring.concat(String.format("%02X ", b));
            } else {
                hexstring = hexstring.concat(String.format("%02X\n", b));
            }
            counter++;
        }
        return hexstring;
    }

    public String getHexstring() {
        return m_hexstring;
    }

}

字符串hex_string:

00 11 22 33
44 55 66 77
88 99 AA BB
CC DD EE FF

基准:

  1. file_bytes.length = 102400 字节 = 100 KiB

    • 通过 class:~0.7 秒
    • 没有 class:~5.2 秒
  2. file_bytes.length = 256000 字节 = 250 KiB

    • 通过 class:~1.2 秒
    • 没有 class:~36 秒

这两个选项之间有一个重要的区别。在慢速版本中,您将每次迭代连接到您为每个字节构建的整个十六进制字符串上。字符串连接是一个缓慢的操作,因为它需要复制整个字符串。随着你的字符串变大,这个复制需要更长的时间,你每个字节都复制整个东西。

在更快的版本中,您将单独构建每个块,并且仅将整个块与输出字符串而不是每个单独的字节连接起来。这意味着更少的昂贵连接。在构建 uyp 块时您仍在使用连接,但是因为块比整个输出小得多,所以连接速度更快。

尽管使用字符串生成器而不是字符串连接,您可以做得更好。 StringBuilder 是一个 class 设计用于有效地增量构建字符串。它避免了连接所做的每个追加的完整副本。我希望如果您重新制作它以使用 StringBuilder,两个版本的性能将大致相同,并且比您已有的任何一个版本都要快。