【问题标题】:Fastest way of processing Java IO using ASCII lines使用 ASCII 行处理 Java IO 的最快方法
【发布时间】:2010-01-21 18:21:21
【问题描述】:

我正在通过 Socket 处理 ASCII 输入/输出流,速度至关重要。我听说使用正确的 Java 技术真的很重要。我有一本教科书说使用 Buffers 是最好的方法,但也建议使用 DataInputStreamReader 链接。

对于输出,我正在使用带有 OutputStreamWriter 的 BufferedOutputStream,这似乎很好。但我不确定输入流使用什么。我正在开发新产品线,那么 Scanner 会有用吗?速度很关键,我需要尽快将数据从网络中取出。

谢谢。

PH

【问题讨论】:

  • BufferedReader 有时比手动缓冲自己慢很多。您需要针对您的特定情况分析不同的解决方案,看看哪个是最快的。 BufferedReader 并不总是最快的。

标签: java stream io java.util.scanner java-io


【解决方案1】:

只是为了笑……

socket = new ServerSocket(2004, 10);
connection = socket.accept();
in = connection.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String line = null;
do {
    line = br.readLine();
} while (!"done".equals(line));

使用 LOOPBACK,即在我的机器上使用本地进程运行到本地主机,并使用适当的“愚蠢”客户端。

requestSocket = new Socket("localhost", 2004);
out = requestSocket.getOutputStream();
PrintWriter pw = new PrintWriter(out);
String line =  "...1000 characters long..."; 
for (int i = 0; i < 2000000 - 1; i++) {
    pw.println(line);
}
line = "done";
pw.println(line);
pw.flush();

您会注意到,这会发送 2M“1000 字符”行。这只是一个粗略的吞吐量测试。

在我的机器上,环回传输速率约为 190MB/秒。字节,而不是位。 190,000 行/秒。

我的观点是,使用骨干 Java 套接字的“简单”方式非常快。这将使任何常见的网络连接饱和(这意味着网络将比您的 I/O 更慢地减慢您的速度)。

可能“足够快”。

您期望什么样的流量?

【讨论】:

  • 足够快,但比Google Fiber 慢:D,好兄弟。
【解决方案2】:

如果速度绝对关键,请考虑使用 NIO。这是针对完全相同的问题发布的代码示例。

http://lists.apple.com/archives/java-dev/2004/Apr/msg00051.html

编辑:这是另一个例子

http://www.java2s.com/Code/Java/File-Input-Output/UseNIOtoreadatextfile.htm

编辑 2:我编写此微基准测试是为了让您开始测量各种方法的性能。一些人评论说 NIO 不会执行得更快,因为你需要做更多的工作来将数据“按摩”成可用的形式,所以你可以根据你想要做的任何事情来验证它。当我在我的机器上运行这段代码时,NIO 代码在 45 兆字节的文件中大约快 3 倍,在 100 兆字节的文件中快 5 倍。

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Scanner;

public class TestStuff {

    public static void main(final String[] args)
            throws IOException, InterruptedException {

        final String file_path = "c:\\test-nio.txt";
        readFileUsingNIO(file_path);
        readFileUsingScanner(file_path);

    }

    private static void readFileUsingScanner(final String path_to_file)
            throws FileNotFoundException {
        Scanner s = null;

        final StringBuilder builder = new StringBuilder();
        try {
            System.out.println("Starting to read the file using SCANNER");
            final long start_time = System.currentTimeMillis();
            s = new Scanner(new BufferedReader(new FileReader(path_to_file)));
            while (s.hasNext()) {
                builder.append(s.next());
            }
            System.out.println("Finished!  Read took " + (System.currentTimeMillis() - start_time) + " ms");
        }
        finally {
            if (s != null) {
                s.close();
            }
        }

    }

    private static void readFileUsingNIO(final String path_to_file)
            throws IOException {
        FileInputStream fIn = null;
        FileChannel fChan = null;
        long fSize;
        ByteBuffer mBuf;

        final StringBuilder builder = new StringBuilder();
        try {
            System.out.println("Starting to read the file using NIO");
            final long start_time = System.currentTimeMillis();
            fIn = new FileInputStream("c:\\test-nio.txt");
            fChan = fIn.getChannel();
            fSize = fChan.size();
            mBuf = ByteBuffer.allocate((int) fSize);
            fChan.read(mBuf);
            mBuf.rewind();
            for (int i = 0; i < fSize; i++) {
                //System.out.print((char) mBuf.get());
                builder.append((char) mBuf.get());
            }
            fChan.close();
            fIn.close();
            System.out.println("Finished!  Read took " + (System.currentTimeMillis() - start_time) + " ms");
        }
        catch (final IOException exc) {
            System.out.println(exc);
            System.exit(1);
        }
        finally {
            if (fChan != null) {
                fChan.close();
            }
            if (fIn != null) {
                fIn.close();
            }
        }

    }

【讨论】:

  • 我的教科书说,如果对不同的输入使用多个线程,我只会获得性能提升。我只有一个意见,但这个想法肯定会吸引我。
  • 如果您正在对输入行进行非平凡的处理,那么您的书就是错误的。
  • nio 对 +scalability+ 至关重要,不是 速度。对于专用线程,nio 可能会更慢。
  • @James,你能提供一些参考吗?是否有任何微基准表明 NIO 使用专用线程时速度较慢?
  • 另外,OP 正在谈论使用套接字,而不是读取文件——这是一个不同的问题。 NIO 映射对于文件操作来说更快,因为它利用操作系统的 VM 系统将文件映射到用户空间并节省复制。套接字读取不一定是这种情况。
【解决方案3】:

Scanner 用于分隔文本。你没有谈论你的数据是什么样的,所以我不能对此发表评论。

如果您只想阅读到每个换行符,请使用

BufferedReader r = new BufferedReader(new InputStreamReader(Socket.getInputStream()))

r.readLine()

当你得到一个空值时,你就会知道你已经用尽了流中的数据。

就速度而言,它们都只是从流中读取数据。因此,假设您不需要 Scanner 的额外功能,我看不出有什么特别的理由要使用它。

【讨论】:

    【解决方案4】:

    我会使用 BufferedReader 来做一些事情:

    Collection<String> lines = new ArrayList<String>();
    BufferedReader reader = new BufferedReader( new InputStreamReader( Foo.getInputStream()));
    while(reader.ready())
    {
        lines.add( reader.readLine());
    }
    
    myClass.processData(lines); //Process the data after it is off the network.
    

    根据您的情况,您可以有一个额外的线程来处理“行”中的项目,因为它正在被填充,但是您需要使用不同的结构来支持集合 - 一个可以同时使用的结构.

    【讨论】:

    • 使用 Vector 对性能来说是错误的,它是同步的。请改用 List。这也是假设整个数据输入集将驻留在内存中。更好的方法是在读取每一行时对其进行处理。
    • 将其更改为未同步的 ArrayList。
    猜你喜欢
    • 2012-11-09
    • 2011-03-22
    • 2015-07-29
    • 2011-11-25
    • 1970-01-01
    • 2023-03-28
    • 2013-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多