2011-05-22 40 views
7

我使用下面的函数来对文件计算校验:的java:需要增加校验和计算的性能

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

的想法是表示进度到标准输出。这是我模仿命令行ftp客户端的“哈希”行为的方式。 dcount%60000000可以打印一张println文件。 – opike 2011-05-22 23:43:53

回答

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实施者是愚蠢的,并没有释放

+0

'DigestInputStream'不会覆盖'skip()',所以跳过的字节不会被消化器处理。 – McDowell 2011-05-22 23:57:52

+1

显然你没有阅读DigestInputStream的Javadoc,它*不*表示跳过更新摘要。低估了愚蠢的评论。 – EJP 2011-05-23 05:42:31

+0

哥们没有说_anything_关于跳过,他们也不检查在建设摘要上的空指针,让你找出为什么你在其他地方的包装流上得到NPE – 2011-05-23 11:40:46

2

你有没有试图消除的println的前正确地测试一切?我想象所有的字符串操作可能会消耗大部分的处理!

编辑:我没有读清楚,我现在意识到他们会怎么不常输出,我收回我的答案,但我想这是不完全的宝贵:-P

+2

println只发生在很小的时间内......如果有什么影响其性能的条件逻辑。 – opike 2011-05-22 23:45:01

+0

+1:考虑控制台输出。在这种情况下,它不会是唯一的性能增强,但许多程序员在写入控制台时并不知道有多少开销。它会显着减慢应用程序的速度。 – 2011-05-22 23:46:44

+0

Downvoted是因为“所有那些字符串操作”只发生一次每二千万次迭代一次。 – QuantumMechanic 2011-05-22 23:53:20

0
(对不起!)

问题是System.out.print过于频繁。每次它被称为新的字符串对象必须被创建并且它是昂贵的。

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

StringBuilder sb = new StringBuilder(); 

每一次你需要添加一些通话时间是:

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

以后,当您准备打印:

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

坦率地说有几个问题与您的代码这使它变慢:

  1. 像棘轮怪胎说,磁盘读取必须缓冲,因为Java read()的可能转换为操作系统IO调用没有自动缓冲,所以一个read()是1系统调用! 如果您使用阵列作为缓冲区或BufferedInputStream,操作系统通常会执行得更好。更好的是,您可以使用nio将文件映射到内存中,并且可以像操作系统一样快速地读取它。

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

  3. 下面的字符串填充也是低效的。您应该使用StringBuilderFormatter

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

  4. 尝试使用Profiler来找到代码中进一步提高效率的问题