【讨论】读写文件的效率

Gump09 2008-03-10 10:32:49
最近由于要分析log,但是由于log文件太大而不能用编辑器打开所以学要分割文件。
于是想了两三个方法。
1,shell命令分割。没找到。那位达人了解的给个提示啊。
2,软件分割。免费软件不能分割大文件。
3,程序分割。

关于3,我写了一段程序。
想当然的我用RandomAccessFile读文件,然后用FileOutputStream输出。
发现效率十分低下。3g文件 500m一个,文成一个就将近3分钟。
于是改成了如下形式

FileInputStream in = new FileInputStream(srcFile);
FileChannel channel = in.getChannel();

FileOutputStream out = new FileOutputStream(fileSeparateName);
FileChannel outChannel = out.getChannel();

channel.transferTo(beginPos, blockSize, outChannel);

这样一来速度有了明显提升。

但是在分割小文件(几十M)时速度又不如第一个方法。

于是产生了下面的疑惑。
1,在什么样的情况下channel要好于stream.有没有具体的临界点。
2,从这个情况来看 java 的io 在不同的情况下可能要使用不同的类组合来达到最好的效率。哪个达人可以指点一下。或者大家来讨论一下啊。
...全文
2107 69 打赏 收藏 转发到动态 举报
写回复
用AI写文章
69 条回复
切换为时间正序
请发表友善的回复…
发表回复
superzhao123 2008-03-12
  • 打赏
  • 举报
回复
学习了。。。
Gump09 2008-03-11
  • 打赏
  • 举报
回复
甘草的文章刚刚看了一篇 的确不错也挺能写的。
感情比较丰富的类型吧?

北里的羡慕啊,我就去过两次北京有一次还是在机场
哎 好想好好转转我们伟大的首都阿
healer_kx 2008-03-11
  • 打赏
  • 举报
回复
北理的,北理最能扯淡的也是我。
bootupnow 2008-03-11
  • 打赏
  • 举报
回复
没说的,直接收藏!up
zjhlht 2008-03-11
  • 打赏
  • 举报
回复
牛~~~~~~~~~~虚心学习了

此贴我收藏了~~~~
cangyue87 2008-03-11
  • 打赏
  • 举报
回复
哦,格斗我还会一个萃梦想....喵~`
cangyue87 2008-03-11
  • 打赏
  • 举报
回复
为什么都打KOF去了,我格斗只会月姬和GGXX也...
corsail 2008-03-11
  • 打赏
  • 举报
回复
学习。。。
Gump09 2008-03-11
  • 打赏
  • 举报
回复
找到了一篇文章 翻译一下 大家共同学习
如果谁看见已经有翻译好的告诉我啊 我就不写了 呵呵

原文
http://java.sun.com/developer/technicalArticles/Programming/PerfTuning/

摘要翻译

IO性能

java总是假定只有两种文件组织形式。
第一种,基于字节流(stream of bytes)
第二种,基于字符序列(character sequences)

java 中一个字符有两个字节。所以在从文件读字符时需要转换。

提高io性能的基本原则
1,尽量避免访问磁盘
2,尽量避免访问OS
3,尽量避免方法调用
4,尽量避免单独处理字节或字符

下面通过一个计算换行数的例子来说明这些原则是如何被应用的。

1,读
import java.io.*;

public class intro1 {
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("missing filename");
System.exit(1);
}
try {
FileInputStream fis =
new FileInputStream(args[0]);
int cnt = 0;
int b;
while ((b = fis.read()) != -1) {
if (b == '\n')
cnt++;
}
fis.close();
System.out.println(cnt);
}
catch (IOException e) {
System.err.println(e);
}
}
}
这段代码中频繁调用了OS 函数,也就是FileInputStream.read
一个本地方法负责读取文件的下一个字节。

2,采用大缓存

import java.io.*;

public class intro2 {
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("missing filename");
System.exit(1);
}
try {
FileInputStream fis =
new FileInputStream(args[0]);
BufferedInputStream bis =
new BufferedInputStream(fis);
int cnt = 0;
int b;
while ((b = bis.read()) != -1) {
if (b == '\n')
cnt++;
}
bis.close();
System.out.println(cnt);
}
catch (IOException e) {
System.err.println(e);
}
}
}
BufferedInputStream.read从缓存中读取字节。减少了底层api访问。

3,直接缓存
我们不使用BufferedInputStream 而是自己直接缓存。
这样可以减少方法调用。

import java.io.*;

public class intro3 {
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("missing filename");
System.exit(1);
}
try {
FileInputStream fis =
new FileInputStream(args[0]);
byte buf[] = new byte[2048];
int cnt = 0;
int n;
while ((n = fis.read(buf)) != -1) {
for (int i = 0; i < n; i++) {
if (buf[i] == '\n')
cnt++;
}
}
fis.close();
System.out.println(cnt);
}
catch (IOException e) {
System.err.println(e);
}
}
}

对于1m的文件 执行时间分别是
intro1 6.9
intro2 0.9
intro3 0.4

最慢的和最快的时间比是 17 比 1

当然这并不是说我们总是要采用第三种方式。
其实第三种方式可能产生错误,特别是在处理end-of-file事件时(要仔细处理这类事件).
并且它可读性比较差.但是作为一个方法还是应该引起我们的注意.

第二种方式可能是最普通的方式.


【缓存】
第二种和第三种方式都用到了缓存.它确实是提高IO速度的基本手段。
这很容易让我们想起一个问题。是不是缓存越大IO越快。
JAVA 的缓存默认一般是1024 或 4048 比特。
更大的缓存可能加快IO但是也就是5% 10% 左右的小幅提升。

4,整个文件读取



import java.io.*;

public class readfile {
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("missing filename");
System.exit(1);
}
try {
int len = (int)(new File(args[0]).length());
FileInputStream fis =
new FileInputStream(args[0]);
byte buf[] = new byte[len];
fis.read(buf);
fis.close();
int cnt = 0;
for (int i = 0; i < len; i++) {
if (buf[i] == '\n')
cnt++;
}
System.out.println(cnt);
}
catch (IOException e) {
System.err.println(e);
}
}
}

这样很方便但也有很明显的不足,内存是不是比文件大。

另外一个缓存的关注点是向终端的输出。SYSTEM.OUT默认是行缓存,
也就是说缓存在遇到一个换行符的时候会将内容强行输出。

5,阻止行缓存


import java.io.*;

public class bufout {
public static void main(String args[]) {
FileOutputStream fdout =
new FileOutputStream(FileDescriptor.out);
BufferedOutputStream bos =
new BufferedOutputStream(fdout, 1024);
PrintStream ps =
new PrintStream(bos, false);

System.setOut(ps);

final int N = 100000;

for (int i = 1; i <= N; i++)
System.out.println(i);

ps.close();
}
}

这个程序输出1到100000到终端他比默认情况下快三倍。

【随机读写文本文件】

import java.io.*;

public class line1 {
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("missing filename");
System.exit(1);
}
try {
FileInputStream fis =
new FileInputStream(args[0]);
BufferedInputStream bis =
new BufferedInputStream(fis);
DataInputStream dis =
new DataInputStream(bis);
int cnt = 0;
while (dis.readLine() != null)
cnt++;
dis.close();
System.out.println(cnt);
}
catch (IOException e) {
System.err.println(e);
}
}
}

这个程序用了古老的DataInputStream.readLine来读取字符。
新一点的方法会象下面这样



import java.io.*;

public class line2 {
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("missing filename");
System.exit(1);
}
try {
FileReader fr = new FileReader(args[0]);
BufferedReader br = new BufferedReader(fr);
int cnt = 0;
while (br.readLine() != null)
cnt++;
br.close();
System.out.println(cnt);
}
catch (IOException e) {
System.err.println(e);
}
}
}

这个方法会快一些。比如6m 200,000行的文件读取第二种方式可以有20%的提升。
但即使没有提升也应该注意到一点。第一个方法会有警告,因为DataInputStream.readLine
这个方法没有正确变换字符,所以不要用它来处理文本文件。它可以处理ASCII的字节流。

来看下面这段程序
import java.io.*;

public class conv1 {
public static void main(String args[]) {
try {
FileOutputStream fos =
new FileOutputStream("out1");
PrintStream ps =
new PrintStream(fos);
ps.println("\uffff\u4321\u1234");
ps.close();
}
catch (IOException e) {
System.err.println(e);
}
}
}

显然他没有写我们能看懂的字符。

☆Reader/Writer IO类是基于字符的,它可以来解决这类问题。
OutputStreamWriter 是设置编码的地方。
import java.io.*;

public class conv2 {
public static void main(String args[]) {
try {
FileOutputStream fos =
new FileOutputStream("out2");
OutputStreamWriter osw =
new OutputStreamWriter(fos, "UTF8");
PrintWriter pw =
new PrintWriter(osw);
pw.println("\uffff\u4321\u1234");
pw.close();
}
catch (IOException e) {
System.err.println(e);
}
}
}
这段程序设置编码方式为UTF8。
  • 打赏
  • 举报
回复
甘草,你真能写啊,那么多.... 你是北理的?
  • 打赏
  • 举报
回复
[Quote=引用 45 楼 healer_kx 的回复:]
我打KOF97是很牛的,估计在程序员里面,我是打KOF最厉害的,在打KOF的人里面,我写程序是最厉害的。

http://healerkx.spaces.live.com/
[/Quote]

[size=30px]哈哈,o(∩_∩)o[/size]
  • 打赏
  • 举报
回复
打KOF限于97,打星际限于1.08b,打CS限于1.5
________________

不会玩游戏,看不懂,55555~~
healer_kx 2008-03-11
  • 打赏
  • 举报
回复
我打KOF97是很牛的,估计在程序员里面,我是打KOF最厉害的,在打KOF的人里面,我写程序是最厉害的。

http://healerkx.spaces.live.com/
healer_kx 2008-03-11
  • 打赏
  • 举报
回复
写诗啊? 写诗我也喜欢。我blog上也有,基本很打油。

我喜欢写文章,写程序,写BLOG,打kof,打星际,打CS。

写文章以抨击实政,写程序赖以为生,写blog以为藉平生,打KOF限于97,打星际限于1.08b,打CS限于1.5。
  • 打赏
  • 举报
回复
楼主,你原来的帖子中不是都有首打油诗的吗?挺好玩的。
healer_kx 2008-03-11
  • 打赏
  • 举报
回复
内存不够的时候考虑BerkerlyDB,MySQL的非Win32版本也有支持。
  • 打赏
  • 举报
回复
哦,那我就没有什么更好的建议了,呵呵~~
Gump09 2008-03-11
  • 打赏
  • 举报
回复
哎 我也做不了主啊
呵呵 不过还是谢谢火龙果的热心建议阿
highkay 2008-03-11
  • 打赏
  • 举报
回复
用java的lucene,全文检索很牛,应该能符合你的要求吧,不过3g.....还是恐怖了一点。
  • 打赏
  • 举报
回复
这样文件是多了点,但是总量还是一样的啊,第二天把这 144 个文件打包保存起来就可以了。
加载更多回复(49)

62,616

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧