Monday 21 September 2015

Chronicle-Bytes vs nio ByteBuffer: A worked example


Chronicle-Bytes is an Open Source project under the Chronicle group of technologies.

It has got some really interesting features and I would definitely recommend it if:
  • you find yourself at all frustrated with java.nio.ByteBuffer 
  • you are in low latency environment and want to avoid allocation
Some of the extensions over java.nio.ByteBuffer are explained in the README for the project.

This is a worked example comparing Chronicle-Bytes with the standard java.nio.ByteBuffer.

The main class in Chronicle-Bytes is  net.openhft.chronicle.bytes.Bytes it extends the functionality found in java.nio.ByteBuffer.

Let's see this in action with the example I introduced in this post where we saw how to prepend into Chronicle Bytes. 

This is the scenario:

You have a variable length message that needs to be written to a buffer. You need to prepend the message with its length so you know how far to read.  The length of that message needs to be written out to the buffer in text not binary (typical for FIX style message formats).

e.g.  The resulting buffer would look like this 11 hello world or 5 hello


Let's first look at the working this example through using java.nio.ByteBuffer.


Interspersed through the code we'll debug example Hello World in red.  We'll track the progress of the buffer through the program as follows:
  • hyphens - signify empty bytes, 
  • single | mark the position

//For the sake of this example we will use a StringBuilder with "Hello World"
//but assume this StringBuilder can be passed into a function and could contain
//any amount of text
StringBuilder sb = new StringBuilder("Hello World");
//For the sake of this example we have allocated a buffer of 20 bytes but in reality
//if the string buffer could be any size we would have to be careful how we sized
//the buffer.  This is one of the advantages of the elasticByteBuffer in Chronicle-Bytes
//which automatically resizes. 
ByteBuffer buffer = ByteBuffer.allocateDirect(20);
//Make sure the buffer is cleared from the last run - assuming this is called in a loop
bytes.clear();
|--------------------

//We need to convert the string into a byte array to add to the buffer later
//NOTE: This line will be a serious source of garbage
byte[] bytes = sb.toString().getBytes(StandardCharsets.UTF_8);
//Now work out the length which is the first thing to be written into the buffer
//Again in this line we will see garbage being produced.  
//Chronicle-Bytes has a direct method for writing numbers as text. 
buffer.put(Integer.toString(bytes.length).getBytes());
11|------------------
//Add a separator between the length and the message
buffer.put((byte) ' ');
11 |-----------------
//Add the bytes for the message
buffer.put(bytes);
11 Hello World|------
//Flipping is required to set into read mode.  You will see that in Chronicle-Bytes
//none of this is sort of thing is required because it has the concept of readPosition()
//and writePosition()
buffer.flip();
|11 Hello World------

//Now read the contents of the buffer
int number = 0;
//You have no option but to read the buffer byte by byte until you hit the
//space which is the end of the length.
byte b = buffer.get();
while(b >= '0' && b<='9'){
  number *= 10;
  number += b - '0';
  b = buffer.get();
}
11 |Hello World------
//We know how many bytes to read until we reach the end of the message
byte[] dst = new byte[number];
buffer.get(dst);
11 Hello World|------
//There is no way to read them directly into a String so we have no alternative
//but to create a new String for the bytes.
System.out.println(number + ":" + new String(dst, StandardCharsets.UTF_8));

This is the standard Java NIO way of working through the example.  It's slightly clunky in places but worst of all it's going to produce a lot of garbage.  As we know (and I've written about many times in this blog) garbage is the enemy of real time latencies.

Now let's work through the example with Chronicle-Bytes.

We will use the exactly the same example and follow the progress of the buffer as we step through the code. This time however we have both a readPosition and a writePosition:
  • hyphens - signify empty bytes, 
  • single | mark the write position 
  • double || mark the read position.
//For the sake of this example we will use a StringBuilder with "Hello World"
//but assume this StringBuilder can be passed into a function and could contain
//any amount of text
StringBuilder sb = new StringBuilder("Hello World");
//Using Bytes you don't need to worry about the length of your buffer - it will resize dynamically
//This really makes life easier.
Bytes bytes = Bytes.elasticByteBuffer();
|||--------------------
//Clear and pad allows you to leave room in your buffer so that you can write into the start of the
//buffer. For more information on prepending see here.
bytes.clearAndPad(8);
--------|||------------
//Bytes supports writing UTF8 from a CharSequence like a StringBuilder.  No need for
//any object creation here!
bytes.appendUtf8(sb);
--------||Hello World|-
//Effectively moves the readPosition backwards
bytes.prewriteByte((byte) ' ');
-------|| Hello World|-
//Bytes allows you write numbers directly as text!
bytes.prepend(sb.length());
-----||11 Hello World|-
//Note how there is no flip() required


//Bytes allows you to read text direct as a long - no object creation necessary
length = (int)bytes.parseLong();
-----11 ||Hello World|-
//Bytes allows you to read bytes straight into a CharSequence as UTF8
bytes.parseUTF(sb, length);
-----11 Hello World|||-
System.out.println(length + ":" + sb);


How do they Perform

I created a JMH benchmark to compare to compare Chronicle-Bytes with ByteBuffer (the full code listing for this is at the end of the post). 

As you can see from below Chronicle-Bytes runs almost twice as fast.

Benchmark                           Mode     Cnt    Score   Error  Units
BytesPerfTest.testByteBuffer      sample  451723  302.436 ± 8.260  ns/op

BytesPerfTest.testChronicleBytes  sample  418022  179.129 ± 1.307  ns/op

If you run the test with -verbosegc you will notice that whilst there are hundreds of collections triggered by testByteBuffer, testChronicleBytes produces triggers no collections at all.  This means that were you to account for coordinated omission in your tests the results would be far worse for ByteBuffer (for more details on coordinated omission see here).  

Conclusion

You should be able to see that Chronicle-Bytes is not only a great way to avoid object creation but is actually also simpler and more intuitive to use the the standard java.nio.ByteBuffer.

8 comments:

  1. But why do we have to have the length of the string as text ? it can be an int type and that is easily added to the ByteBuffer ( putInt(int) ) ? That also solves other issues you mentioned. Seems a bit convoluted example to me ! Also , the other points you mentioned namely the elasticByteBuffer resizing thats just a syntactic sugar ( which can be implemented in ByteBuffer as well ) ?

    ReplyDelete
  2. Java is one in every of the quickest growing and wide used technologies in numerous industries from play to application development. If you're not well equipped with java resources then no got to worry as you'll be able to simply select java outsourcing. Java Outsourcing not solely saves time however additionally provides compatible product and it's the simplest platform offered. however few folks are adamant in going for java outsourcing, as for them it's terribly difficult to come to a decision whether or not to use services of some third-party company or person.In today's jet age world several corporations have adopted the strategy of outsourcing java to varied corporations and professionals. if you want more information about Cheap Essay Writing Service However, before going for java outsourcing it's knowing understand the background of the chosen company as your work together with your name is on stake. this is often wherever Soft age marks its presence. Soft age is one in every of the leading and dependable corporations providing java mobile application outsourcing services

    ReplyDelete
  3. It would be incredible if iinstylejackets" these individuals who ceaselessly fire java really had a tolerable measure of involvement with it.

    ReplyDelete
  4. Hello, I would like to thank you for sharing this interesting information. What is more, the material provided here will be useful for a lot of people. I was actually looking for a place where to case brief assignment online. Nevertheless, thank s a lot.

    ReplyDelete
  5. Thanks for sharing this piece of information. I really enjoyed it. keep up the good work and all the very best of luck! B3 Bomber Aviator Sheepskin Leather Jacket

    ReplyDelete
  6. At the manufacturing scale, product quality for real world proofing and product post finishing techniques and processes is yet to be examined. The functionality in 3DP technology may even impact the strategy carried out in areas together with production planning, labor distribution, and new talent coaching. These repositories would thus assist individuals from numerous supply chain sectors, particularly the prosumer group. Previously, examine has examined the funding on family 3D printers that make the most of open supply downloads and located they'd yield a return over 100 percent of return in 5 years . Consequently, the motion of home manufacturing, localized manufacturing, and the demand in product customization would turn into extra viable in the near future (Laplume et al. 2016). The technology of 3DP has nice potential in sustainability optimization from CNC machining the environmental, financial, and social perspectives.

    ReplyDelete
  7. Chronicle-Bytes is a library developed by OpenHFT (Open High-Frequency Trading) that provides low-latency,
    what is cloud CAD high-performance memory access and serialization capabilities for Java applications

    ReplyDelete