执行时间:迭代与实例
Execution Time: Iterativ vs Instance
我在 Java 中编码时偶然发现了一件奇怪的事情:
我将文件读入字节数组 (byte[] file_bytes
),我想要的是 hexdump 输出(就像 Linux 中的实用程序 hexdump
或 xxd
)。基本上这是可行的(请参阅 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
基准:
file_bytes.length = 102400 字节 = 100 KiB
- 通过 class:~0.7 秒
- 没有 class:~5.2 秒
file_bytes.length = 256000 字节 = 250 KiB
- 通过 class:~1.2 秒
- 没有 class:~36 秒
这两个选项之间有一个重要的区别。在慢速版本中,您将每次迭代连接到您为每个字节构建的整个十六进制字符串上。字符串连接是一个缓慢的操作,因为它需要复制整个字符串。随着你的字符串变大,这个复制需要更长的时间,你每个字节都复制整个东西。
在更快的版本中,您将单独构建每个块,并且仅将整个块与输出字符串而不是每个单独的字节连接起来。这意味着更少的昂贵连接。在构建 uyp 块时您仍在使用连接,但是因为块比整个输出小得多,所以连接速度更快。
尽管使用字符串生成器而不是字符串连接,您可以做得更好。 StringBuilder 是一个 class 设计用于有效地增量构建字符串。它避免了连接所做的每个追加的完整副本。我希望如果您重新制作它以使用 StringBuilder,两个版本的性能将大致相同,并且比您已有的任何一个版本都要快。
我在 Java 中编码时偶然发现了一件奇怪的事情:
我将文件读入字节数组 (byte[] file_bytes
),我想要的是 hexdump 输出(就像 Linux 中的实用程序 hexdump
或 xxd
)。基本上这是可行的(请参阅 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
基准:
file_bytes.length = 102400 字节 = 100 KiB
- 通过 class:~0.7 秒
- 没有 class:~5.2 秒
file_bytes.length = 256000 字节 = 250 KiB
- 通过 class:~1.2 秒
- 没有 class:~36 秒
这两个选项之间有一个重要的区别。在慢速版本中,您将每次迭代连接到您为每个字节构建的整个十六进制字符串上。字符串连接是一个缓慢的操作,因为它需要复制整个字符串。随着你的字符串变大,这个复制需要更长的时间,你每个字节都复制整个东西。
在更快的版本中,您将单独构建每个块,并且仅将整个块与输出字符串而不是每个单独的字节连接起来。这意味着更少的昂贵连接。在构建 uyp 块时您仍在使用连接,但是因为块比整个输出小得多,所以连接速度更快。
尽管使用字符串生成器而不是字符串连接,您可以做得更好。 StringBuilder 是一个 class 设计用于有效地增量构建字符串。它避免了连接所做的每个追加的完整副本。我希望如果您重新制作它以使用 StringBuilder,两个版本的性能将大致相同,并且比您已有的任何一个版本都要快。