传统java io_java – 传统IO与内存映射

我试图用

Java来向学生说明传统IO和内存映射文件之间的性能差异.

我在互联网上的某个地方找到了一个例子,但并非一切都很清楚,我甚至认为所有步骤都不是.我在这里和那里读了很多关于它的内容,但我不相信它们都没有正确实现.

我试着理解的代码是:

public class FileCopy{

public static void main(String args[]){

if (args.length < 1){

System.out.println(" Wrong usage!");

System.out.println(" Correct usage is : java FileCopy ");

System.exit(0);

}

String inFileName = args[0];

File inFile = new File(inFileName);

if (inFile.exists() != true){

System.out.println(inFileName + " does not exist!");

System.exit(0);

}

try{

new FileCopy().memoryMappedCopy(inFileName, inFileName+".new" );

new FileCopy().customBufferedCopy(inFileName, inFileName+".new1");

}catch(FileNotFoundException fne){

fne.printStackTrace();

}catch(IOException ioe){

ioe.printStackTrace();

}catch (Exception e){

e.printStackTrace();

}

}

public void memoryMappedCopy(String fromFile, String toFile ) throws Exception{

long timeIn = new Date().getTime();

// read input file

RandomAccessFile rafIn = new RandomAccessFile(fromFile, "rw");

FileChannel fcIn = rafIn.getChannel();

ByteBuffer byteBuffIn = fcIn.map(FileChannel.MapMode.READ_WRITE, 0,(int) fcIn.size());

fcIn.read(byteBuffIn);

byteBuffIn.flip();

RandomAccessFile rafOut = new RandomAccessFile(toFile, "rw");

FileChannel fcOut = rafOut.getChannel();

ByteBuffer writeMap = fcOut.map(FileChannel.MapMode.READ_WRITE,0,(int) fcIn.size());

writeMap.put(byteBuffIn);

long timeOut = new Date().getTime();

System.out.println("Memory mapped copy Time for a file of size :" + (int) fcIn.size() +" is "+(timeOut-timeIn));

fcOut.close();

fcIn.close();

}

static final int CHUNK_SIZE = 100000;

static final char[] inChars = new char[CHUNK_SIZE];

public static void customBufferedCopy(String fromFile, String toFile) throws IOException{

long timeIn = new Date().getTime();

Reader in = new FileReader(fromFile);

Writer out = new FileWriter(toFile);

while (true) {

synchronized (inChars) {

int amountRead = in.read(inChars);

if (amountRead == -1) {

break;

}

out.write(inChars, 0, amountRead);

}

}

long timeOut = new Date().getTime();

System.out.println("Custom buffered copy Time for a file of size :" + (int) new File(fromFile).length() +" is "+(timeOut-timeIn));

in.close();

out.close();

}

}

究竟是什么时候使用RandomAccessFile呢?在这里它用于读取和写入memoryMappedCopy,它实际上只是复制一个文件nececary?或者它是记忆映射的一部分?

在customBufferedCopy中,为什么在这里使用同步?

我还发现了一个不同的例子 – 应该测试2之间的性能:

public class MappedIO {

private static int numOfInts = 4000000;

private static int numOfUbuffInts = 200000;

private abstract static class Tester {

private String name;

public Tester(String name) { this.name = name; }

public long runTest() {

System.out.print(name + ": ");

try {

long startTime = System.currentTimeMillis();

test();

long endTime = System.currentTimeMillis();

return (endTime - startTime);

} catch (IOException e) {

throw new RuntimeException(e);

}

}

public abstract void test() throws IOException;

}

private static Tester[] tests = {

new Tester("Stream Write") {

public void test() throws IOException {

DataOutputStream dos = new DataOutputStream(

new BufferedOutputStream(

new FileOutputStream(new File("temp.tmp"))));

for(int i = 0; i < numOfInts; i++)

dos.writeInt(i);

dos.close();

}

},

new Tester("Mapped Write") {

public void test() throws IOException {

FileChannel fc =

new RandomAccessFile("temp.tmp", "rw")

.getChannel();

IntBuffer ib = fc.map(

FileChannel.MapMode.READ_WRITE, 0, fc.size())

.asIntBuffer();

for(int i = 0; i < numOfInts; i++)

ib.put(i);

fc.close();

}

},

new Tester("Stream Read") {

public void test() throws IOException {

DataInputStream dis = new DataInputStream(

new BufferedInputStream(

new FileInputStream("temp.tmp")));

for(int i = 0; i < numOfInts; i++)

dis.readInt();

dis.close();

}

},

new Tester("Mapped Read") {

public void test() throws IOException {

FileChannel fc = new FileInputStream(

new File("temp.tmp")).getChannel();

IntBuffer ib = fc.map(

FileChannel.MapMode.READ_ONLY, 0, fc.size())

.asIntBuffer();

while(ib.hasRemaining())

ib.get();

fc.close();

}

},

new Tester("Stream Read/Write") {

public void test() throws IOException {

RandomAccessFile raf = new RandomAccessFile(

new File("temp.tmp"), "rw");

raf.writeInt(1);

for(int i = 0; i < numOfUbuffInts; i++) {

raf.seek(raf.length() - 4);

raf.writeInt(raf.readInt());

}

raf.close();

}

},

new Tester("Mapped Read/Write") {

public void test() throws IOException {

FileChannel fc = new RandomAccessFile(

new File("temp.tmp"), "rw").getChannel();

IntBuffer ib = fc.map(

FileChannel.MapMode.READ_WRITE, 0, fc.size())

.asIntBuffer();

ib.put(0);

for(int i = 1; i < numOfUbuffInts; i++)

ib.put(ib.get(i - 1));

fc.close();

}

}

};

public static void main(String[] args) {

for(int i = 0; i < tests.length; i++)

System.out.println(tests[i].runTest());

}

}

我或多或少看到了什么,我的输出看起来像这样:

Stream Write: 653

Mapped Write: 51

Stream Read: 651

Mapped Read: 40

Stream Read/Write: 14481

Mapped Read/Write: 6

什么使流读/写如此令人难以置信?作为读/写测试,对我来说,反复读取相同的整数看起来有点无意义(如果我很清楚在流读/写中发生了什么)从以前读取int是不是更好写入文件,只是在同一个地方读写内容?有没有更好的方式来说明它?

我一直在关注很多这些事情,但我无法全面了解……

最佳答案 我看到的一个基准“流读/写”是:

>它实际上并不是流I / O,而是寻找文件中的特定位置.这是非缓冲的,因此所有I / O必须从磁盘完成(其他流使用缓冲I / O,因此在大块中实际读/写,然后从存储区读取或写入整数).

>它正在寻找结尾 – 4个字节,因此读取最后一个int并写入一个新的int.每次迭代时,文件的长度都会延长一个int.这确实不会增加时间成本(但确实表明该基准的作者误解了某些东西或者不小心).

这解释了该特定基准的非常高的成本.

您询问:

Wouldn’t it be better to read int’s

from the previously written file and

just read and write ints on the same

place?

这是作者我认为试图用最后两个基准测试,但这不是他们得到的.使用RandomAccessFile读取和写入文件中的相同位置,您需要在读取和写入之前进行搜索:

raf.seek(raf.length() - 4);

int val = raf.readInt();

raf.seek(raf.length() - 4);

raf.writeInt(val);

这确实展示了内存映射I / O的一个优点,因为您可以使用相同的内存地址来访问文件的相同位,而不必在每次调用之前进行额外的搜索.

顺便说一句,你的第一个基准示例类也可能有问题,因为CHUNK_SIZE不是文件系统块大小的偶数倍.对于大多数应用程序而言,使用1024和8192的倍数通常被认为是一个很好的最佳点(并且Java的BufferedInputStream和BufferedOutputStream使用该值作为默认缓冲区大小的原因).操作系统需要读取一个额外的块以满足不在块边界上的读取请求.后续读取(流)将重新读取相同的块,可能是一些完整的块,然后再次读取.内存映射I / O始终以块的形式进行物理读写,因为实际的I / O由OS内存管理器处理,后者将使用其页面大小.页面大小始终经过优化,可以很好地映射到文件块.

在该示例中,内存映射测试确实将所有内容读入内存缓冲区,然后将其全部写回.这两个测试真的写不出来比较这两个案例. memmoryMappedCopy应该以与customBufferedCopy相同的块大小进行读写.

编辑:这些测试类甚至可能有更多错误.由于你对另一个答案的评论,我再次仔细看了第一堂课.

方法customBufferedCopy是静态的并使用静态缓冲区.对于这种测试,缓冲区应该在方法中定义.然后它不需要使用synchronized(虽然它在这个上下文中并不需要它,无论如何也是这些测试).这种静态方法被称为普通方法,这是一种糟糕的编程习惯(即使用FileCopy.customBufferedCopy(…)而不是新的FileCopy().customBufferedCopy(…)).

如果您确实从多个线程运行此测试,则该缓冲区的使用将是有争议的,并且基准测试不仅仅是文件I / O,因此比较两种测试方法的结果是不公平的.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值