java:需要提高校验和计算的性能

时间:2011-05-22 23:34:46

标签: java file-io checksum

我正在使用以下函数来计算文件的校验和:

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(". ");
    }
}

从一个位置复制到另一个位置需要不到一分钟的3 GB文件需要一个多小时来计算。我有什么办法可以加快速度,或者我应该像使用shell命令那样尝试不同的方向吗?

更新:感谢棘轮怪物的建议我将代码更改为更快得多(我猜想2048X更快......):

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 个答案:

答案 0 :(得分:4)

使用缓冲区

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(". ");
     }
     */
}

编辑:或者如果您不需要值

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

nvm显然DigestInputStream的实施者是愚蠢的,并且在发布前没有正确测试所有内容

答案 1 :(得分:2)

您是否尝试过删除println?我想所有字符串操作都可能消耗大部分处理!

编辑:我没有清楚地阅读它,我现在意识到它们不经常输出,我会收回我的答案,但我想这不是完全无价的:-p(对不起!)

答案 2 :(得分:0)

问题是System.out.print经常使用。每次调用它时都必须创建新的String对象并且它很昂贵。

使用StringBuilder类或其线程安全的模拟StringBuffer。

StringBuilder sb = new StringBuilder();

每次你需要添加一些东西时都要这样说:

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

稍后,当您准备好打印它时:

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

答案 3 :(得分:0)

坦率地说,您的代码存在一些问题,导致速度变慢:

  1. 像棘轮怪一样说,磁盘读取必须缓冲,因为Java read()可能转换为操作系统IO调用而不自动缓冲,因此一个read()是1个系统调用!! ! 如果使用数组作为缓冲区或BufferedInputStream,操作系统通常会运行得更好。更好的是,您可以使用nio将文件映射到内存中,并以操作系统可以处理的速度快速读取。

  2. 您可能不相信,但dCount++;计数器可能使用了很多周期。我相信即使是最新的英特尔酷睿处理器,也需要几个时钟周期来完成64位浮点数添加。你可以更好地使用这个计数器。 如果此计数器的唯一目的是显示进度,则可以利用Java整数溢出而不会导致错误的事实,并在char类型换行为0时(每65536次读取时)提前显示进度。

  3. 以下字符串填充效率也很低。您应该使用StringBuilderFormatter

    while(hashtext.length()&lt; 32){   hashtext =“0”+ hashtext; }

  4. 尝试使用分析器在代码中查找更多效率问题