我正在使用以下函数来计算文件的校验和:
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 :(得分: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)
坦率地说,您的代码存在一些问题,导致速度变慢:
像棘轮怪一样说,磁盘读取必须缓冲,因为Java read()
可能转换为操作系统IO调用而不自动缓冲,因此一个read()
是1个系统调用!! !
如果使用数组作为缓冲区或BufferedInputStream
,操作系统通常会运行得更好。更好的是,您可以使用nio将文件映射到内存中,并以操作系统可以处理的速度快速读取。
您可能不相信,但dCount++;
计数器可能使用了很多周期。我相信即使是最新的英特尔酷睿处理器,也需要几个时钟周期来完成64位浮点数添加。你可以更好地使用这个计数器。
如果此计数器的唯一目的是显示进度,则可以利用Java整数溢出而不会导致错误的事实,并在char类型换行为0时(每65536次读取时)提前显示进度。
以下字符串填充效率也很低。您应该使用StringBuilder
或Formatter
。
while(hashtext.length()&lt; 32){ hashtext =“0”+ hashtext; }
尝试使用分析器在代码中查找更多效率问题