Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java,为什么从MappedByteBuffer读取比从BufferedReader读取慢_Java_Io_Mappedbytebuffer - Fatal编程技术网

Java,为什么从MappedByteBuffer读取比从BufferedReader读取慢

Java,为什么从MappedByteBuffer读取比从BufferedReader读取慢,java,io,mappedbytebuffer,Java,Io,Mappedbytebuffer,我试着从一个可能很大的文件中读取行 为了提高性能,我尝试使用映射文件。但是当我比较性能时,我发现映射文件方式甚至比我从BufferedReader public long chunkMappedFile(String filePath, int trunkSize) throws IOException { long begin = System.currentTimeMillis(); logger.info("Processing imei file, mapped file

我试着从一个可能很大的文件中读取行

为了提高性能,我尝试使用映射文件。但是当我比较性能时,我发现映射文件方式甚至比我从
BufferedReader

public long chunkMappedFile(String filePath, int trunkSize) throws IOException {
    long begin = System.currentTimeMillis();
    logger.info("Processing imei file, mapped file [{}], trunk size = {} ", filePath, trunkSize);

    //Create file object
    File file = new File(filePath);

    //Get file channel in readonly mode
    FileChannel fileChannel = new RandomAccessFile(file, "r").getChannel();

    long positionStart = 0;
    StringBuilder line = new StringBuilder();
    long lineCnt = 0;
    while(positionStart < fileChannel.size()) {
        long mapSize = positionStart + trunkSize < fileChannel.size() ? trunkSize : fileChannel.size()  - positionStart ;
        MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, positionStart, mapSize);//mapped read
        for (int i = 0; i < buffer.limit(); i++) {
            char c = (char) buffer.get();
            //System.out.print(c); //Print the content of file
            if ('\n' != c) {
                line.append(c);
            } else {// line ends
                processor.processLine(line.toString());
                if (++lineCnt % 100000 ==0) {
                    try {
                        logger.info("mappedfile processed {} lines already, sleep 1ms", lineCnt);
                        Thread.sleep(1);
                    } catch (InterruptedException e) {}
                }
                line = new StringBuilder();
            }
        }
        closeDirectBuffer(buffer);
        positionStart = positionStart + buffer.limit();
    }

    long end = System.currentTimeMillis();
    logger.info("chunkMappedFile {} , trunkSize: {},  cost : {}  " ,filePath, trunkSize, end - begin);

    return lineCnt;
}

public long normalFileRead(String filePath) throws IOException {
    long begin = System.currentTimeMillis();
    logger.info("Processing imei file, Normal read file [{}] ", filePath);
    long lineCnt = 0;
    try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
        String line;

        while ((line = br.readLine()) != null) {
            processor.processLine(line.toString());
            if (++lineCnt % 100000 ==0) {
                try {
                    logger.info("file processed {} lines already, sleep 1ms", lineCnt);
                    Thread.sleep(1);
                } catch (InterruptedException e) {}
            }            }
    }
    long end = System.currentTimeMillis();
    logger.info("normalFileRead {} ,   cost : {}  " ,filePath, end - begin);

    return lineCnt;
}
BufferedReader方式:

2017-09-28 14:27:03.374 [main] INFO  com.oppo.push.ts.dispatcher.imei2device.ImeiTransformerOfflineImpl - process imei file ends:/push/file/imei2device-local/20170928/imei2device-13 , lines :12758858 , cost :13001 , lines per seconds: 981375.1249903854

这就是问题所在:文件IO不是简单明了的

您必须记住,您的操作系统对将要发生的事情有着巨大的影响。从这个意义上说:没有可靠的规则适用于所有平台上的所有JVM实现

当您确实需要担心最后一点性能时,在目标平台上进行深入分析是主要的解决方案

除此之外,您还弄错了“性能”方面。意思是:内存映射IO不会神奇地提高一次性读取应用程序中单个文件的性能。其主要优势在于:

如果您有多个进程以只读方式从同一个文件访问数据,那么mmap非常好,这在我编写的服务器系统中很常见。mmap允许所有这些进程共享相同的物理内存页,从而节省大量内存

(在使用C
mmap()
system调用时引用)

换句话说:您的示例是关于读取文件内容的。最后,操作系统仍然必须转向驱动器才能从那里读取所有字节。意思:它读取光盘内容并将其放入内存。当你第一次这么做的时候。。。你在上面做一些“特别”的事情其实并不重要。相反,当你做“特殊”的事情时,内存映射方法可能会更慢,因为与“普通”读取相比,开销更大


回到我的第一条记录:即使有5个进程读取同一个文件,内存映射方法也不一定更快。正如Linux可能会想的那样:我已经将该文件读入内存,但它没有改变——因此即使没有显式的“内存映射”,Linux内核也可能缓存信息

内存映射并没有带来任何好处,因为即使您将文件批量加载到内存中,您仍然会一次处理一个字节。如果以适当大小的
byte[]
块处理缓冲区,您可能会看到性能提高。即使如此,
BufferedReader
版本的性能也可能更好,或者至少几乎相同

任务的本质是按顺序处理文件
BufferedReader
已经很好地实现了这一点,而且代码很简单,因此如果必须选择,我会选择最简单的选项


还要注意的是,除了单字节编码外,缓冲区代码不起作用。只要每个字符有多个字节,它就会非常失败。

GhostCat是正确的。除了您的操作系统选择之外,还有其他可能影响性能的因素

  • 映射文件将对物理内存提出更高的要求。如果物理内存“紧张”,可能会导致分页活动和性能下降

  • 如果使用
    read
    syscalls读取文件,而不是将文件映射到内存中,那么操作系统可以使用不同的预读策略。预读(进入缓冲区缓存)可以加快文件读取速度

  • BufferedReader
    的默认缓冲区大小和操作系统内存页大小可能不同。这可能导致磁盘读取请求的大小不同。(较大的读取通常会导致更大的吞吐量I/O。至少在某一点上如此。)

还有可能是由基准测试方式造成的“人工制品”。例如:

  • 第一次读取文件时,部分或全部文件的副本将进入缓冲区缓存(内存中)
  • 第二次读取同一文件时,部分文件可能仍在内存中,而且明显的
    读取时间将更短

但是Linux应该很好地支持映射文件,从映射缓冲区读取有什么问题吗?当然它支持!关键是内存映射文件I/O不一定更快。不管你是怎么做的。@StephenC很好的反馈。确实帮助我进一步完善了我的答案,以至于我忘了提及;-)我会更怀疑你写的
mmap
代码。
BufferedReader
方法简单明了,很难出错(如果你有基本经验的话)。映射一个文件并像现在这样处理它看起来真的没有效果。如果你对应用程序进行分析,你可能会发现热点在于你的方法,即遍历每个字节并将其转换为
字符。
。是的,我怀疑我所做的不是一个好方法,有什么建议吗?我注意到你的问题仍然是“开放的”:你没有接受答案。请看一看,然后决定是否要回答。或者让我知道是否有什么我可以做的,以加强我的投入,使其值得接受。接受帮助未来的读者确定问题是否得到解决,并对花时间回答你的人表示感谢。我没有注意到你的答案。。。但是,平等的思想,平等的思考;-)
2017-09-28 14:27:03.374 [main] INFO  com.oppo.push.ts.dispatcher.imei2device.ImeiTransformerOfflineImpl - process imei file ends:/push/file/imei2device-local/20170928/imei2device-13 , lines :12758858 , cost :13001 , lines per seconds: 981375.1249903854