Live data from Hacker News

The sorry state of Java deserialization

marginalia.nu

31–40 of 54 posts

Re: The sorry state of Java deserialization

#31
post #29

I noticed: 1. Small read buffers. No reason to sequentially read and parse gigabytes only 4kb at a time. 2. parseDelimitedFrom created a new CodedInputStream on every message, which has its own internal buffer; that's why you don't see a buffered stream wrapper in the examples. Every iteration of the loop is allocating fresh 4kb byte[]s. 3. The nio protobuf code creates wrappers for the allocated ByteBuffer on every…

> Making a header with the city string mapped to an integer would dramatically shrink the file and speed up parsing. Indeed, Parquet will do this for you if you let it. I also wonder how good Java is at optimizing closure creation in a loop, as in: k -> new ResultObserver() The vast majority of those closures are created and never called. C++ might optimize this well if calling a template but has basically no chance…

The closure itself is only being created once, it's essentially a singleton. Only if it would capture variables it would have to be recreated every iteration.

Re: The sorry state of Java deserialization

#32

Earlier quoted context omitted.

Tried a refresh, a force-refresh... no change at all (Firefox and Chrome)

Interesting. Android or iOS?

Desktop - that's my complaint... you wrote that the code samples "barely fit on desktop", which is only true because your CSS wastes over 2/3rds of the screen width I have provided to my user agent.

(it's fine though - I use Reader Mode on such user-hostile sites)

Re: The sorry state of Java deserialization

#33
post #26

“I admit I don’t understand these results. There’s clearly nothing in the runtime itself that prevents these types of speeds.” Oh, there is. The default Java serialization is sort of like “pickle” module in Python - if you are familiar. It will deal with pretty much anything you throw at it, figuring the data structures and offsets to serialize or parse at runtime. More efficient methods trade universality for speed,…

>With Java, you have to go out of your way to maintain good cache locality because you give up control over memory layout for automatic memory management. There is a shadowy cult in a hidden corner of the Java community, an heresy to many, only followed by a handful of obnoxious zealots inspired by the dark ages of Ada 83, C, or even assembly, who take pride in creating Java programs that only allocate a finite amoun…

Ah, so you're one of the six Epsilon GC users :-)

Re: The sorry state of Java deserialization

#34

Earlier quoted context omitted.

Author here, I'm away from my computer atm, but I can cook up a repo with each test in a few hours when I get home. I designed the tests as a drag race because that mimics my real world usage.

That's nice - I'd encourage you to play around with attaching e.g. JMC [1] to the process to better understand why things are as they are. I tried recreating your DataInputStream + BufferedInputStream (wrote the 1brc data to separate output files, read using your code - I had to guess at ResultObserver implementation though). On my machine it roughly in the same time frame as yours - ~1min. According to Flight Record…

JMC is indeed a valuable tool, though what you see in any java profiler is to be taken with a grain of salt. The string parsing and hash lookups are present in most of the implementations, yet some of them are up to 10 times faster than the DataInputStream + BufferedInputStream code.

It doesn't seem like it can be true that 90% of the time is spent in string parsing and hash lookups if the same operation takes 10% of the time when reading from a filechannel and bytebuffer.

Re: The sorry state of Java deserialization

#35

Earlier quoted context omitted.

That's nice - I'd encourage you to play around with attaching e.g. JMC [1] to the process to better understand why things are as they are. I tried recreating your DataInputStream + BufferedInputStream (wrote the 1brc data to separate output files, read using your code - I had to guess at ResultObserver implementation though). On my machine it roughly in the same time frame as yours - ~1min. According to Flight Record…

JMC is indeed a valuable tool, though what you see in any java profiler is to be taken with a grain of salt. The string parsing and hash lookups are present in most of the implementations, yet some of them are up to 10 times faster than the DataInputStream + BufferedInputStream code. It doesn't seem like it can be true that 90% of the time is spent in string parsing and hash lookups if the same operation takes 10% of…

Aren't the versions that take 10% of the time only reading each city name once, and then doing an array lookup rather than a hashmap lookup?

Re: The sorry state of Java deserialization

#36

Earlier quoted context omitted.

JMC is indeed a valuable tool, though what you see in any java profiler is to be taken with a grain of salt. The string parsing and hash lookups are present in most of the implementations, yet some of them are up to 10 times faster than the DataInputStream + BufferedInputStream code. It doesn't seem like it can be true that 90% of the time is spent in string parsing and hash lookups if the same operation takes 10% of…

Aren't the versions that take 10% of the time only reading each city name once, and then doing an array lookup rather than a hashmap lookup?

Nope, see for example "Custom 1":

  var buffer = ByteBuffer.allocate(4096);
  try (var fc = (FileChannel) Files.newByteChannel(tempFile, 
                        StandardOpenOption.READ)) 
  {

    buffer.flip();

    for (int i = 0; i  new ResultsObserver())
             .observe(temperature / 100.);
    }
  }

Re: The sorry state of Java deserialization

#37

Earlier quoted context omitted.

Interesting. Android or iOS?

Desktop - that's my complaint... you wrote that the code samples "barely fit on desktop", which is only true because your CSS wastes over 2/3rds of the screen width I have provided to my user agent. (it's fine though - I use Reader Mode on such user-hostile sites)

Oh yeah, now I understand.

I'm struggling to find a width for the layout that makes sense for text (where narrow columns are preferable), but also for code snippets (where you want wider columns).

Re: The sorry state of Java deserialization

#39

I noticed: 1. Small read buffers. No reason to sequentially read and parse gigabytes only 4kb at a time. 2. parseDelimitedFrom created a new CodedInputStream on every message, which has its own internal buffer; that's why you don't see a buffered stream wrapper in the examples. Every iteration of the loop is allocating fresh 4kb byte[]s. 3. The nio protobuf code creates wrappers for the allocated ByteBuffer on every…

So what does the performant way of doing this look like? Your final suggestion brought the nio version down to about 32s.

I was looking for ways of re-using the wrappers in the NIO code, but couldn't figure out how to do that.

Re: The sorry state of Java deserialization

#40

Earlier quoted context omitted.

Aren't the versions that take 10% of the time only reading each city name once, and then doing an array lookup rather than a hashmap lookup?

Nope, see for example "Custom 1": var buffer = ByteBuffer.allocate(4096); try (var fc = (FileChannel) Files.newByteChannel(tempFile, StandardOpenOption.READ)) { buffer.flip(); for (int i = 0; i new ResultsObserver()) .observe(temperature / 100.); } }

My bad - I got confused as the original DIS+BIS took ~60s on my machine. I reproducing the Custom 1 implementation locally (before seeing your repo) and it took ~48s on the same machine. JFR (which you honestly can trust most of the time) says that the HashMap lookup now is ~50% of the time and the String constructor call being ~35%.
Post reply on HN