- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我试图向学生说明传统 IO 和 java 中的内存映射文件之间的性能差异。我在互联网上的某个地方找到了一个例子,但我并不是很清楚所有的事情,我什至不认为所有的步骤都是必要的。我在这里和那里阅读了很多关于它的内容,但我不相信它们都可以正确实现。
我尝试理解的代码是:
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 <large file with full path>");
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
中读写的,真的只需要拷贝一个文件吗?或者它是内存映射的一部分?
在customBufferedCopy
中,这里为什么要用synchronized
?
我还找到了一个不同的例子,它应该测试 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
是什么让 Stream Read/Write 如此之长?作为读/写测试,对我来说,一遍又一遍地读取相同的整数看起来有点毫无意义(如果我理解 Stream Read/Write
中发生的事情)不是吗最好从以前写入的文件中读取 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
不是文件系统 block 大小的偶数倍。通常最好使用 1024 的倍数,而 8192 已被证明是大多数应用程序的最佳选择(也是 Java 的 BufferedInputStream
和 BufferedOutputStream
使用该值作为默认值的原因缓冲区大小)。操作系统将需要读取额外的 block 以满足不在 block 边界上的读取请求。 (流的)后续读取将重新读取同一个 block ,可能是一些完整的 block ,然后再读取一个额外的 block 。内存映射 I/O 总是以 block 为单位进行物理读写,因为实际的 I/O 由操作系统内存管理器处理,该管理器将使用其页面大小。页面大小始终经过优化以很好地映射到文件 block 。
在该示例中,内存映射测试确实将所有内容读入内存缓冲区,然后将其全部写回。这两个测试真的没有写好来比较这两种情况。 memmoryMappedCopy
应该以与 customBufferedCopy
相同的 block 大小读取和写入。
编辑:这些测试类可能还有更多问题。由于您对其他答案的评论,我再次更仔细地看了第一个类。
方法 customBufferedCopy
是静态的,使用静态缓冲区。对于这种测试,缓冲区应该在方法中定义。那么它就不需要使用 synchronized
(尽管它在这种情况下和这些测试中都不需要它)。此静态方法被称为普通方法,这是糟糕的编程习惯(即使用 FileCopy.customBufferedCopy(...)
而不是 new FileCopy().customBufferedCopy(...)
)。
如果您确实从多个线程运行此测试,那么该缓冲区的使用将引起争议,并且基准测试将不仅仅是关于文件 I/O,因此比较两种测试方法的结果是不公平的。
关于java - 传统 IO 与内存映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2593531/
IO 设备如何知道属于它的内存中的值在memory mapped IO 中发生了变化? ? 例如,假设内存地址 0 专用于保存 VGA 设备的背景颜色。当我们更改 memory[0] 中的值时,VGA
我目前正在开发一个使用Facebook sdk登录(通过FBLoginView)的iOS应用。 一切正常,除了那些拥有较旧版本的facebook的人。 当他们按下“使用Facebook登录”按钮时,他
假设我有: this - is an - example - with some - dashesNSRange将使用`rangeOfString:@“-”拾取“-”的第一个实例,但是如果我只想要最后
Card.io SDK提供以下详细信息: 卡号,有效期,月份,年份,CVV和邮政编码。 如何从此SDK获取国家名称。 - (void)userDidProvideCreditCardInfo:(Car
iOS 应用程序如何从网络服务下载图片并在安装过程中将它们安装到用户的 iOS 设备上?可能吗? 最佳答案 您无法控制应用在用户设备上的安装,因此无法在安装过程中下载其他数据。 只需在安装后首次启动应
我曾经开发过一款企业版 iOS 产品,我们公司曾将其出售给大型企业,供他们的员工使用。 该应用程序通过 AppStore 提供,企业用户获得了公司特定的配置文件(包含应用程序配置文件)以启用他们有权使
我正在尝试将 Card.io SDK 集成到我的 iOS 应用程序中。我想为 CardIO ui 做一个简单的本地化,如更改取消按钮标题或“在此保留信用卡”提示文本。 我在 github 上找到了这个
我正在使用 CardIOView 和 CardIOViewDelegate 类,没有可以设置为 YES 的 BOOL 来扫描 collectCardholderName。我可以看到它在 CardIOP
我有一个集成了通话工具包的 voip 应用程序。每次我从我的 voip 应用程序调用时,都会在 native 电话应用程序中创建一个新的最近通话记录。我在 voip 应用程序中也有自定义联系人(电话应
iOS 应用程序如何知道应用程序打开时屏幕上是否已经有键盘?应用程序运行后,它可以接收键盘显示/隐藏通知。但是,如果应用程序在分屏模式下作为辅助应用程序打开,而主应用程序已经显示键盘,则辅助应用程序不
我在模拟器中收到以下错误: ImageIO: CGImageReadSessionGetCachedImageBlockData *** CGImageReadSessionGetCachedIm
如 Apple 文档所示,可以通过 EAAccessory Framework 与经过认证的配件(由 Apple 认证)进行通信。但是我有点困惑,因为一些帖子告诉我它也可以通过 CoreBluetoo
尽管现在的调试器已经很不错了,但有时找出应用程序中正在发生的事情的最好方法仍然是古老的 NSLog。当您连接到计算机时,这样做很容易; Xcode 会帮助弹出日志查看器面板,然后就可以了。当您不在办公
在我的 iOS 应用程序中,我定义了一些兴趣点。其中一些有一个 Kontakt.io 信标的名称,它绑定(bind)到一个特定的 PoI(我的意思是通常贴在信标标签上的名称)。现在我想在附近发现信标,
我正在为警报提示创建一个 trigger.io 插件。尝试从警报提示返回数据。这是我的代码: // Prompt + (void)show_prompt:(ForgeTask*)task{
您好,我是 Apple iOS 的新手。我阅读并搜索了很多关于推送通知的文章,但我没有发现任何关于 APNS 从 io4 到 ios 6 的新更新的信息。任何人都可以向我提供 APNS 如何在 ios
UITabBar 的高度似乎在 iOS 7 和 8/9/10/11 之间发生了变化。我发布这个问题是为了让其他人轻松找到答案。 那么:在 iPhone 和 iPad 上的 iOS 8/9/10/11
我想我可以针对不同的 iOS 版本使用不同的 Storyboard。 由于 UI 的差异,我将创建下一个 Storyboard: Main_iPhone.storyboard Main_iPad.st
我正在写一些东西,我将使用设备的 iTunes 库中的一部分音轨来覆盖 2 个视频的组合,例如: AVMutableComposition* mixComposition = [[AVMutableC
我创建了一个简单的 iOS 程序,可以顺利编译并在 iPad 模拟器上运行良好。当我告诉 XCode 4 使用我连接的 iPad 设备时,无法编译相同的程序。问题似乎是当我尝试使用附加的 iPad 时
我是一名优秀的程序员,十分优秀!