Java: необходимо увеличить производительность вычисления контрольной суммы - PullRequest
7 голосов
/ 23 мая 2011

Я использую следующую функцию для вычисления контрольных сумм в файлах:

public static void generateChecksums(String strInputFile, String strCSVFile) {
    ArrayList<String[]> outputList = new ArrayList<String[]>();
    try {
        MessageDigest m = MessageDigest.getInstance("MD5");
        File aFile = new File(strInputFile);
        InputStream is = new FileInputStream(aFile);

        System.out.println(Calendar.getInstance().getTime().toString() + 
                    " Processing Checksum: " + strInputFile);

        double dLength = aFile.length();
        try {
            is = new DigestInputStream(is, m);
            // read stream to EOF as normal...
            int nTmp;
            double dCount = 0;
            String returned_content="";
            while ((nTmp = is.read()) != -1) {
                dCount++;
                if (dCount % 600000000 == 0) {
                    System.out.println(". ");
                } else if (dCount % 20000000 == 0) {
                    System.out.print(". ");
                }
            }
            System.out.println();
        } finally {
            is.close();
        }
        byte[] digest = m.digest();
        m.reset();
        BigInteger bigInt = new BigInteger(1,digest);
        String hashtext = bigInt.toString(16);
        // Now we need to zero pad it if you actually / want the full 32 chars.
        while(hashtext.length() < 32 ){
            hashtext = "0" + hashtext;
        }
        String[] arrayTmp = new String[2];
        arrayTmp[0] = aFile.getName();
        arrayTmp[1] = hashtext;
        outputList.add(arrayTmp);
        System.out.println("Hash Code: " + hashtext);
        UtilityFunctions.createCSV(outputList, strCSVFile, true);
    } catch (NoSuchAlgorithmException nsae) {
        System.out.println(nsae.getMessage());
    } catch (FileNotFoundException fnfe) {
        System.out.println(fnfe.getMessage());
    } catch (IOException ioe) {
        System.out.println(ioe.getMessage());
    }
}

Проблема в том, что цикл чтения в файле действительно медленный:

while ((nTmp = is.read()) != -1) {
    dCount++;
    if (dCount % 600000000 == 0) {
        System.out.println(". ");
    } else if (dCount % 20000000 == 0) {
        System.out.print(". ");
    }
}

A3 ГБ файла, который занимает менее минуты для копирования из одного места в другое, для расчета требуется более часа.Есть ли что-то, что я могу сделать, чтобы ускорить это, или я должен попытаться пойти в другом направлении, например, с помощью команды оболочки?

Обновление: благодаря предложению Ratchet Freak я изменил код на это, что смехотворно быстрее (Я бы предположил, что в 2048 раз быстрее ...):

byte[] buff = new byte[2048];
while ((nTmp = is.read(buff)) != -1) {
    dCount += 2048;
    if (dCount % 614400000 == 0) {
        System.out.println(". ");
    } else if (dCount % 20480000 == 0) {
        System.out.print(". ");
    }
}

Ответы [ 4 ]

4 голосов
/ 23 мая 2011

используйте буфер

byte[] buff = new byte[2048];
while ((nTmp = is.read(buff)) != -1)
{
     dCount+=ntmp;
     //this logic won't work anymore though
     /*
     if (dCount % 600000000 == 0)
     {
         System.out.println(". ");
     }
     else if (dCount % 20000000 == 0)
     {
         System.out.print(". ");
     }
     */
}

edit: или если вам не нужны значения, сделайте

while(is.read(buff)!=-1)is.skip(600000000);

nvm, по-видимому, реализацииDigestInputStream были глупы и не проверяли все должным образом перед выпуском

2 голосов
/ 23 мая 2011

Вы пытались удалить отпечатки? Я полагаю, что все эти манипуляции со строками могут занимать большую часть обработки!

Редактировать: Я не читал это ясно, теперь я понимаю, как редко они будут выводиться, я бы отказался от своего ответа, но я думаю, что он не был абсолютно бесценным :-p (Извините!)

0 голосов
/ 23 мая 2011

Честно говоря, есть несколько проблем с вашим кодом, которые замедляют его:

  1. Как и сказал храповик, чтение с диска должно буферизоваться, потому что Java read(), вероятно, транслируется в вызовы ввода-вывода операционной системы без автоматической буферизации, поэтому один read() равен 1 системному вызову !!! Операционная система обычно работает намного лучше, если вы используете массив в качестве буфера или BufferedInputStream. Более того, вы можете использовать nio, чтобы отобразить файл в память и прочитать его так быстро, как ОС сможет его обработать.

  2. Вы можете не верить этому, но счетчик dCount++; мог использовать много циклов. Я полагаю, что даже для новейшего процессора Intel Core требуется 64 тактных такта для добавления 64-разрядных операций с плавающей запятой. Вам будет гораздо лучше использовать длинную для этого счетчика. Если единственная цель этого счетчика состоит в том, чтобы отображать прогресс, вы можете использовать тот факт, что целые числа Java переполняются, не вызывая ошибки, и просто улучшить отображение прогресса, когда тип char оборачивается до 0 (это на 65536 операций чтения).

  3. Следующее заполнение строки также неэффективно. Вы должны использовать StringBuilder или Formatter.

    while (hashtext.length () <32) { hashtext = "0" + hashtext; } </p>

  4. Попробуйте использовать профилировщик, чтобы найти другие проблемы с эффективностью в вашем коде

0 голосов
/ 23 мая 2011

Проблема в том, что System.out.print используется слишком часто.Каждый раз, когда он вызывается, нужно создавать новые объекты String, и это дорого.

Вместо этого используйте класс StringBuilder или его потокобезопасный аналог StringBuffer.

StringBuilder sb = new StringBuilder();

И каждый раз, когда вам нужно добавитьчто-то назовите это:

sb.append("text to be added");

Позже, когда вы будете готовы напечатать это:

system.out.println(sb.toString());
...