Время выполнения: Итератив против Экземпляра - PullRequest
0 голосов
/ 24 марта 2019

Я просто наткнулся на странную вещь во время кодирования на Java:

Я читаю файл в bytearray (byte[] file_bytes), и мне нужен вывод hexdump (например, утилиты hexdump или xxd в Linux). В основном это работает (см. For-loop-code, который не закомментирован), но для больших файлов (> 100 КиБ) требуется немного, чтобы пройти через байтовые массивы, сделать правильное форматирование, и так далее.

Но если я поменяю код цикла for на код, который является закомментированным (используя класс с тем же кодом цикла for для вычисления!), Он будет работать очень быстро.

В чем причина такого поведения?

Codesnippet:

    [...]

    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++;
        }
    }

    [...]

класс 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 КиБ

    • через класс: ~ 0,7 с
    • без класса: ~ 5,2 сек
  2. file_bytes.length = 256000 байт = 250 КиБ

    • через класс: ~ 1,2 сек
    • без класса: ~ 36 с

1 Ответ

2 голосов
/ 24 марта 2019

Есть важное различие между двумя вариантами.В медленной версии вы объединяете каждую итерацию со всей шестнадцатеричной строкой, которую вы создали для каждого байта.Конкатенация строк - это медленная операция, поскольку она требует копирования всей строки.Когда ваша строка становится больше, это копирование занимает больше времени, и вы копируете все это каждый байт.

В более быстрой версии вы создаете каждый блок по отдельности и объединяете только целые блоки с выходной строкой, а не с отдельными байтами.Это означает, что гораздо меньше дорогих объединений.Вы все еще используете конкатенацию при построении uyp чанка, но поскольку чанк намного меньше, чем весь вывод, эти конкатенации быстрее.

Вы могли бы добиться гораздо большего успеха, если бы использовали строитель строк вместо конкатенации строк.StringBuilder - это класс, разработанный для эффективного наращивания строк.Это позволяет избежать полной копии каждого добавления, которое делает конкатенация.Я ожидаю, что если вы переделаете это для использования StringBuilder, обе версии будут работать примерно одинаково и будут быстрее, чем любая из версий, которые у вас уже есть.

...