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

java:需要提高校验和计算的性能,java,file-io,checksum,Java,File Io,Checksum,我使用以下函数计算文件的校验和: public static void generateChecksums(String strInputFile, String strCSVFile) { ArrayList<String[]> outputList = new ArrayList<String[]>(); try { MessageDigest m = MessageDigest.getInstance("MD5");

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

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());
    }
}
从一个位置复制到另一个位置所需时间不到一分钟的3 GB文件,计算起来需要一个多小时。我可以做些什么来加速这个过程,还是应该尝试使用shell命令等不同的方向

更新:多亏ratchet freak的建议,我将代码改成了这个,速度快得离谱(我猜是2048X快了…):

使用缓冲区

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
的实现者很愚蠢,在发布之前没有正确地测试所有内容

您尝试过删除println吗?我想所有的字符串操作可能会消耗大部分的处理


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

问题是System.out.print使用得太频繁了。每次调用它时,都必须创建新的字符串对象,而且代价很高

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

StringBuilder sb = new StringBuilder();
每次需要添加内容时,请称之为:

sb.append("text to be added");
稍后,当您准备打印时:

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

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

  • 正如ratchet freak所说,磁盘读取必须进行缓冲,因为Java
    read()
    很可能在没有自动缓冲的情况下被转换为操作系统IOs调用,所以一个
    read()
    就是一个系统调用!!! 如果使用数组作为缓冲区或
    BufferedInputStream
    ,操作系统通常会表现得更好。更好的是,您可以使用nio将文件映射到内存中,并尽可能快地读取它

  • 您可能不相信,但是
    dCount++计数器可能使用了很多周期。我相信即使是最新的Intel Core处理器,完成64位浮点加法也需要几个时钟周期。你最好在这个柜台上用一个长的。
    如果此计数器的唯一用途是显示进度,则可以利用Java整数溢出而不会导致错误的事实,并在字符类型包装为0(即每65536次读取)时将进度显示提前

  • 下面的字符串填充也是低效的。您应该使用
    StringBuilder
    格式化程序

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

  • 尝试使用探查器在代码中查找进一步的效率问题


  • 其目的是指出标准化的进展。这是我模拟命令行ftp客户端的“散列”行为的方法。数据帐户%60000000执行打印项次与打印项次。打印项次只在一小部分时间出现。。。如果有任何影响性能的因素,则使用条件逻辑。+1:用于考虑控制台输出。在这种情况下,它不是唯一的性能增强,但许多程序员没有意识到在编写控制台时会有多少开销。它会显著降低应用程序的速度。被否决是因为“所有那些字符串操作”只会每两千万次迭代发生一次。
    DigestInputStream
    不会覆盖
    skip()
    ,因此跳过的字节将不会被Digest处理。显然,您没有阅读DigestInputStream的Javadoc,其中没有说skip会更新摘要。对愚蠢的评论投了反对票。伙计,它没有说任何关于skip的内容,他们也没有检查构造摘要上的nullpointer,这让你想清楚为什么在其他地方的包装流上会有一个NPE,因此skip()的行为是继承的,与所有三个read()方法的行为不同,记录这些信息以更新摘要。
    sb.append("text to be added");
    
    system.out.println(sb.toString());