Created
October 3, 2016 11:44
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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