Skip to content

Instantly share code, notes, and snippets.

@michel-kraemer
Created October 3, 2016 11:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michel-kraemer/910b68862d2d01bd8fd0c6e4f03705bf to your computer and use it in GitHub Desktop.
Save michel-kraemer/910b68862d2d01bd8fd0c6e4f03705bf to your computer and use it in GitHub Desktop.
Benchmark testing Acton's DefaultJsonFeeder vs. a FastFeeder that doesn't need to copy bytes
@Grab(group='de.undercouch', module='actson', version='1.1.0')
class Dummy {}
import de.undercouch.actson.JsonEvent
import de.undercouch.actson.JsonFeeder
import de.undercouch.actson.JsonParser
import java.nio.ByteBuffer
import java.nio.CharBuffer
import java.nio.charset.CharacterCodingException
import java.nio.charset.CharsetDecoder
import java.nio.charset.CoderResult
import java.nio.charset.MalformedInputException
import java.nio.charset.StandardCharsets
import java.nio.charset.UnmappableCharacterException
def jsonStr = new File('src/test/resources/de/undercouch/actson/pass1.txt').text + ','
def jsonBytes = jsonStr.getBytes(StandardCharsets.UTF_8)
def doBenchmark(parser, jsonBytes, n) {
def feeder = parser.getFeeder()
println("Benchmarking with feeder: " + feeder.class.name)
int pos = 0
int event = JsonEvent.NEED_MORE_INPUT
int count = n
int state = 0
long start = System.currentTimeMillis()
while (event != JsonEvent.EOF) {
while ((event = parser.nextEvent()) == JsonEvent.NEED_MORE_INPUT) {
if (state == 0) {
feeder.feed((byte)'[')
state++
} else if (state == 1) {
pos += feeder.feed(jsonBytes, pos, jsonBytes.length - pos)
if (pos == jsonBytes.length) {
state++
}
} else if (state == 2) {
count--
if (count > 0) {
pos = 0
state = 1
} else {
feeder.feed('{}]'.getBytes(StandardCharsets.UTF_8))
state++
}
} else if (state == 3) {
feeder.done()
}
}
}
long end = System.currentTimeMillis()
println("$n iterations took ${end - start} ms")
}
class FastFeeder implements JsonFeeder {
private ByteBuffer byteBuf
private final CharBuffer charBuf
private final CharsetDecoder decoder
private boolean done = false
FastFeeder() {
charBuf = CharBuffer.allocate(2048)
charBuf.limit(0)
decoder = StandardCharsets.UTF_8.newDecoder()
}
void feed(byte b) {
byteBuf = ByteBuffer.wrap(b)
byteBuf.position(1)
}
int feed(byte[] buf) {
byteBuf = ByteBuffer.wrap(buf)
byteBuf.position(buf.length)
return buf.length
}
int feed(byte[] buf, int offset, int len) {
return this.feed(buf)
}
void done() {
done = true
}
boolean isFull() {
return byteBuf != null
}
boolean hasInput() throws CharacterCodingException {
return fillBuffer()
}
boolean isDone() throws CharacterCodingException {
return done && !hasInput()
}
char nextInput() throws CharacterCodingException {
if (!hasInput()) {
throw new IllegalStateException("Not enough input data")
}
return charBuf.get()
}
private boolean fillBuffer() throws CharacterCodingException {
if (charBuf.hasRemaining()) {
return true
}
if (byteBuf == null || byteBuf.position() == 0) {
byteBuf = null
return false
}
charBuf.position(0)
charBuf.limit(charBuf.capacity())
byteBuf.flip()
CoderResult result = decoder.decode(byteBuf, charBuf, done)
if (result.isMalformed()) {
throw new MalformedInputException(result.length())
}
if (result.isUnmappable()) {
throw new UnmappableCharacterException(result.length())
}
charBuf.flip()
byteBuf.compact()
return charBuf.hasRemaining()
}
}
long start = System.currentTimeMillis()
for (int i = 0; i < 10; ++i) {
def parser = new JsonParser()
doBenchmark(parser, jsonBytes, 10000)
}
long end = System.currentTimeMillis()
println("Total: ${end - start} ms")
start = System.currentTimeMillis()
for (int i = 0; i < 10; ++i) {
def parser = new JsonParser(new FastFeeder())
doBenchmark(parser, jsonBytes, 10000)
}
end = System.currentTimeMillis()
println("Total: ${end - start} ms")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment