Live data from Hacker News

How fast can a BufferedReader read lines in Java?

lemire.me

61–70 of 90 posts

Re: How fast can a BufferedReader read lines in Java?

#61
post #28

The first issue I can see with that code is it's not doing what he expects. He does this to read the file into a StringBuffer: bf.lines().forEach(s -> sb.append(s)); However, this ends up reading all the lines into one giant line, since the String's that lines() produces have the newline character stripped. This leads to the second lines() call to read a 23MB line (the file produced by gen.py). This is less than opti…

Your version still has the problem that since Java 7 the substring method allocates a new String, which in unnecessary when it is only borrowed to the parseLine function and not needed afterwards. What you probably want is a view that reuses the same underlying data. With the following I get around 2 GB/s:

    public void readString(String data) throws IOException {
        int lastIdx = 0;
        for (int idx = data.indexOf('\n'); idx > -1; idx = data.indexOf('\n', lastIdx)) {
            parseLine(subSequenceView(data, lastIdx, idx));
            lastIdx = idx + 1;
        }
        parseLine(subSequenceView(data, lastIdx, data.length()));
    }

    CharSequence subSequenceView(CharSequence base, int beginIndex, int endIndex) {
        return new StringView(base, beginIndex, endIndex - beginIndex);
    }

    static class StringView implements CharSequence {
        final CharSequence base;
        final int offset;
        final int length;

        StringView(CharSequence base, int offset, int length) {
            if (length = 0");
            this.base = base;
            this.offset = offset;
            this.length = length;
        }

        @Override
        public char charAt(int n) {
            if (n = length)
                throw new IndexOutOfBoundsException(n);
            return base.charAt(offset + n);
        }

        @Override
        public int length() {
            return length;
        }

        @Override
        public CharSequence subSequence(int beginIndex, int endIndex) {
            return new StringView(base, offset + beginIndex, endIndex - beginIndex);
        }
    }

Re: How fast can a BufferedReader read lines in Java?

#62
At least two problems with the java code. Concatenation of strings using the plus operator creates a new string and copies the content of the old, that pushes the complexity of the code from o(n) to o(n2) where n is the number of lines. Secondly order is not guaranteed with the for each operation on streams.

The correct way to do it is using collect(Collectors.joining(“\n”)) or straight forward imperative style (without streams).

I don’t think the general statement holds (that java or buffered reader is cpu bound in particular).

Re: How fast can a BufferedReader read lines in Java?

#63
post #62

At least two problems with the java code. Concatenation of strings using the plus operator creates a new string and copies the content of the old, that pushes the complexity of the code from o(n) to o(n2) where n is the number of lines. Secondly order is not guaranteed with the for each operation on streams. The correct way to do it is using collect(Collectors.joining(“\n”)) or straight forward imperative style (with…

Where do you see string concatenation with the plus operator in the posted code?

Re: How fast can a BufferedReader read lines in Java?

#64
post #63
post #62

At least two problems with the java code. Concatenation of strings using the plus operator creates a new string and copies the content of the old, that pushes the complexity of the code from o(n) to o(n2) where n is the number of lines. Secondly order is not guaranteed with the for each operation on streams. The correct way to do it is using collect(Collectors.joining(“\n”)) or straight forward imperative style (with…

Where do you see string concatenation with the plus operator in the posted code?

Haha - nowhere ... I completely misread his code.

Re: How fast can a BufferedReader read lines in Java?

#65

Earlier quoted context omitted.

If the article's point was that better alternatives exist, it should have made that point by mentioning those better alternatives and ideally benchmarking them as well. I agree with others that this particular article comes across as very lazy and not up to Lemire's usual standard.

That's not the point of the article. The point of the article is that many programs are in fact CPU bound, contrary to the often repeated claims and you do actually need to do optimization work to saturate your IO; even in lanuages considered fast, like c++ and java, the straightforward implementation is often suboptimal.

Agreed. Though if the author really has existing materials about faster alternatives, it's still strange not to mention them. The article is strictly speaking about one way of doing I/O (or rather, an abstraction often involved in I/O), but the author should know that many will read it as saying "all I/O in Java is always slow".

Re: How fast can a BufferedReader read lines in Java?

#66
post #28

The first issue I can see with that code is it's not doing what he expects. He does this to read the file into a StringBuffer: bf.lines().forEach(s -> sb.append(s)); However, this ends up reading all the lines into one giant line, since the String's that lines() produces have the newline character stripped. This leads to the second lines() call to read a 23MB line (the file produced by gen.py). This is less than opti…

That's a pretty big error if you're correct. What does it say about the language when a CS professor falls for this on a 40 line file?

About as much as binary search being wrong in many languages for 20 years: https://thebittheories.com/the-curious-case-of-binary-search...

Nothing. Mistakes happen.

Re: How fast can a BufferedReader read lines in Java?

#67
Java has many inefficient parts. For example there's no immutable array concept (or owning concept, like in Rust), so there's a lot of unnecessary array copies happens in JDK. String is not well designed. There was an attempt to abstract String concept into CharSequence, but a lot of code still uses Strings.

I made a similar benchmark. The idea is as follows: we have 2 GB byte array (because arrays in Java have 32 bit limit, LoL) filled with 32..126 values, imitating ASCII text and 13 values imitating newlines.

The first test is simply does XOR the whole array. It's the ideal result which should correspond to memory bandwidth.

The second test wraps this array into ByteArrayInputSteram, converts it into Reader using InputStreamReader with UTF-8 encoding, reads lines using BufferedReader and in the end also XORs every char value.

For 2 GB I have 516 ms as an ideal time (3,8 GB/s which is still almost order of magnitude less than theoretical 19.2 GB/s DDR4 speed) and 3566 ms as a BufferedReader, so you can have almost 7x speed improvement with better implementation.

Benchmark: https://pastebin.com/xMD4W8mn

Re: How fast can a BufferedReader read lines in Java?

#69

Eh, he didn't use NIO. BufferedReader is an ancient Java relic. Like reading from STDIN in c, it's not made to be fast, it's there for convenience and backwards compatibility. Read a file using something like Vert.X, which is optimized for speed. I'm 100% confident it will be faster than the naive c approach

Do you have example of alternatives for the BufferedReader with the NIO APIs? I do a lot of work with large GZIP that are read line by line using the standard IO (i.e. GzipInputStrem(FileInputStream)) etc) but your comment has really made me second guess my choice of doing that...

The NIO API uses channels and buffers

  Path path = ...
  var count = 0;
  try(var channel = FileChannel.open(path)) {
    var buffer = ByteBuffer.allocateDirect(8192);
    while(channel.read(buffer) != -1) {
      while(buffer.hasRemaining()) {
        if (buffer.get() == '\n') {
          count++;
        }
      }
      buffer.clear();
    }
  }
  System.out.println(count);
but in your particular case, i don't think there is a Gzip decoder that works on ByteBuffer.
Post reply on HN