Skip to content

Commit 7b2664d

Browse files
committed
[Java] Improve performance of field access order checking.
This commit switches the generated code to using an int to represent the state rather than a reference to an enum case. The change improves performance significantly, which I suspect is due to avoiding a write barrier that updates the card table. Relevant benchmark runs are below. Perhaps different JVM versions and GC options will yield different relative improvements. All benchmark runs use: - `-Dsbe.generate.access.order.checks=true` at build time - `-Dsbe.enable.access.order.checks=true` at runtime Using an enum: ``` $ java -jar -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=true ./sbe-benchmarks/build/libs/sbe-benchmarks.jar ".*Car.*" -bm thrpt -f 2 -i 3 -wi 2 \# JMH version: 1.36 \# VM version: JDK 1.8.0_302, OpenJDK 64-Bit Server VM, 25.302-b08 \# VM invoker: /home/zach/.asdf/installs/java/zulu-8.56.0.21/jre/bin/java \# VM options: -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=true ... Benchmark Mode Cnt Score Error Units CarBenchmark.testDecode thrpt 6 8075519.641 ± 363346.224 ops/s CarBenchmark.testEncode thrpt 6 7211523.145 ± 504174.913 ops/s ``` Using an int: ``` $ java -jar -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=true ./sbe-benchmarks/build/libs/sbe-benchmarks.jar ".*Car.*" -bm thrpt -f 2 -i 3 -wi 2 \# JMH version: 1.36 \# VM version: JDK 1.8.0_302, OpenJDK 64-Bit Server VM, 25.302-b08 \# VM invoker: /home/zach/.asdf/installs/java/zulu-8.56.0.21/jre/bin/java \# VM options: -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=true ... Benchmark Mode Cnt Score Error Units CarBenchmark.testDecode thrpt 6 13030684.127 ± 155782.171 ops/s CarBenchmark.testEncode thrpt 6 9781612.505 ± 1342559.378 ops/s ```
1 parent b2899c4 commit 7b2664d

File tree

5 files changed

+303
-211
lines changed

5 files changed

+303
-211
lines changed

sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,12 @@ private void generateEncoder(
270270

271271
private static CharSequence qualifiedStateCase(final FieldOrderModel.State state)
272272
{
273-
return "CodecState." + state.name();
273+
return "CodecStates." + state.name();
274+
}
275+
276+
private static CharSequence stateCaseForSwitchCase(final FieldOrderModel.State state)
277+
{
278+
return qualifiedStateCase(state);
274279
}
275280

276281
private static CharSequence unqualifiedStateCase(final FieldOrderModel.State state)
@@ -304,23 +309,38 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field
304309
fieldOrderModel.generateGraph(sb, " * ");
305310
sb.append(" * }</pre>\n");
306311
sb.append(" */\n");
307-
sb.append(" private enum CodecState\n")
312+
sb.append(" private static class CodecStates\n")
308313
.append(" {\n");
309314
fieldOrderModel.forEachState(state ->
310-
sb.append(" ").append(unqualifiedStateCase(state)).append(",\n"));
311-
315+
{
316+
sb.append(" private static final int ")
317+
.append(unqualifiedStateCase(state))
318+
.append(" = ").append(state.number())
319+
.append(";\n");
320+
});
321+
sb.append("\n").append(" private static final String[] STATE_NAME_LOOKUP =\n")
322+
.append(" {\n");
323+
fieldOrderModel.forEachState(state ->
324+
{
325+
sb.append(" \"").append(state.name()).append("\",\n");
326+
});
327+
sb.append(" };\n\n");
328+
sb.append(" private static String name(final int state)\n")
329+
.append(" {\n")
330+
.append(" return STATE_NAME_LOOKUP[state];\n")
331+
.append(" }\n");
312332
sb.append(" }\n\n");
313333

314-
sb.append(" private CodecState codecState = ")
334+
sb.append(" private int codecState = ")
315335
.append(qualifiedStateCase(fieldOrderModel.notWrappedState()))
316336
.append(";\n\n");
317337

318-
sb.append(" private CodecState codecState()\n")
338+
sb.append(" private int codecState()\n")
319339
.append(" {\n")
320340
.append(" return codecState;\n")
321341
.append(" }\n\n");
322342

323-
sb.append(" private void codecState(CodecState newState)\n")
343+
sb.append(" private void codecState(int newState)\n")
324344
.append(" {\n")
325345
.append(" codecState = newState;\n")
326346
.append(" }\n\n");
@@ -457,7 +477,7 @@ private static void generateAccessOrderListener(
457477
.append(indent).append("{\n")
458478
.append(indent).append(" throw new IllegalStateException(")
459479
.append("\"Cannot access field \\\"").append(token.name())
460-
.append("\\\" in state: \" + codecState());\n")
480+
.append("\\\" in state: \" + CodecStates.name(codecState()));\n")
461481
.append(indent).append("}\n");
462482
}
463483
else
@@ -470,7 +490,7 @@ private static void generateAccessOrderListener(
470490
transitions.forEach(transition ->
471491
{
472492
transition.forEachStartState(startState ->
473-
sb.append(indent).append(" case ").append(unqualifiedStateCase(startState)).append(":\n"));
493+
sb.append(indent).append(" case ").append(stateCaseForSwitchCase(startState)).append(":\n"));
474494
sb.append(indent).append(" codecState(")
475495
.append(qualifiedStateCase(transition.endState())).append(");\n")
476496
.append(indent).append(" break;\n");
@@ -479,7 +499,7 @@ private static void generateAccessOrderListener(
479499
sb.append(indent).append(" default:\n")
480500
.append(indent).append(" throw new IllegalStateException(")
481501
.append("\"Cannot access field \\\"").append(token.name())
482-
.append("\\\" in state: \" + codecState());\n")
502+
.append("\\\" in state: \" + CodecStates.name(codecState()));\n")
483503
.append(indent).append("}\n");
484504
}
485505
}
@@ -835,13 +855,13 @@ private void generateGroupDecoderClassHeader(
835855
if (null != fieldOrderModel)
836856
{
837857
sb.append("\n")
838-
.append(indent).append(" private CodecState codecState()\n")
858+
.append(indent).append(" private int codecState()\n")
839859
.append(indent).append(" {\n")
840860
.append(indent).append(" return parentMessage.codecState();\n")
841861
.append(indent).append(" }\n");
842862

843863
sb.append("\n")
844-
.append(indent).append(" private void codecState(final CodecState newState)\n")
864+
.append(indent).append(" private void codecState(final int newState)\n")
845865
.append(indent).append(" {\n")
846866
.append(indent).append(" parentMessage.codecState(newState);\n")
847867
.append(indent).append(" }\n");
@@ -965,13 +985,13 @@ private void generateGroupEncoderClassHeader(
965985
if (null != fieldOrderModel)
966986
{
967987
sb.append("\n")
968-
.append(ind).append(" private CodecState codecState()\n")
988+
.append(ind).append(" private int codecState()\n")
969989
.append(ind).append(" {\n")
970990
.append(ind).append(" return parentMessage.codecState();\n")
971991
.append(ind).append(" }\n");
972992

973993
sb.append("\n")
974-
.append(ind).append(" private void codecState(final CodecState newState)\n")
994+
.append(ind).append(" private void codecState(final int newState)\n")
975995
.append(ind).append(" {\n")
976996
.append(ind).append(" parentMessage.codecState(newState);\n")
977997
.append(ind).append(" }\n");
@@ -3193,7 +3213,7 @@ private CharSequence generateDecoderFlyweightCode(
31933213

31943214
if (null != fieldOrderModel)
31953215
{
3196-
methods.append(" final CodecState currentCodecState = codecState();\n");
3216+
methods.append(" final int currentCodecState = codecState();\n");
31973217
}
31983218

31993219
methods

sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,37 @@ public final class FrameCodecDecoder
3535
* }
3636
* }</pre>
3737
*/
38-
private enum CodecState
38+
private static class CodecStates
3939
{
40-
NOT_WRAPPED,
41-
V0_BLOCK,
42-
V0_PACKAGENAME_DONE,
43-
V0_NAMESPACENAME_DONE,
44-
V0_SEMANTICVERSION_DONE,
40+
private static final int NOT_WRAPPED = 0;
41+
private static final int V0_BLOCK = 1;
42+
private static final int V0_PACKAGENAME_DONE = 2;
43+
private static final int V0_NAMESPACENAME_DONE = 3;
44+
private static final int V0_SEMANTICVERSION_DONE = 4;
45+
46+
private static final String[] STATE_NAME_LOOKUP =
47+
{
48+
"NOT_WRAPPED",
49+
"V0_BLOCK",
50+
"V0_PACKAGENAME_DONE",
51+
"V0_NAMESPACENAME_DONE",
52+
"V0_SEMANTICVERSION_DONE",
53+
};
54+
55+
private static String name(final int state)
56+
{
57+
return STATE_NAME_LOOKUP[state];
58+
}
4559
}
4660

47-
private CodecState codecState = CodecState.NOT_WRAPPED;
61+
private int codecState = CodecStates.NOT_WRAPPED;
4862

49-
private CodecState codecState()
63+
private int codecState()
5064
{
5165
return codecState;
5266
}
5367

54-
private void codecState(CodecState newState)
68+
private void codecState(int newState)
5569
{
5670
codecState = newState;
5771
}
@@ -116,10 +130,10 @@ private void onWrap(final int actingVersion)
116130
switch(actingVersion)
117131
{
118132
case 0:
119-
codecState(CodecState.V0_BLOCK);
133+
codecState(CodecStates.V0_BLOCK);
120134
break;
121135
default:
122-
codecState(CodecState.V0_BLOCK);
136+
codecState(CodecStates.V0_BLOCK);
123137
break;
124138
}
125139
}
@@ -176,7 +190,7 @@ public FrameCodecDecoder sbeRewind()
176190
public int sbeDecodedLength()
177191
{
178192
final int currentLimit = limit();
179-
final CodecState currentCodecState = codecState();
193+
final int currentCodecState = codecState();
180194
sbeSkip();
181195
final int decodedLength = encodedLength();
182196
limit(currentLimit);
@@ -236,9 +250,9 @@ public static String irIdMetaAttribute(final MetaAttribute metaAttribute)
236250

237251
private void onIrIdAccessed()
238252
{
239-
if (codecState() == CodecState.NOT_WRAPPED)
253+
if (codecState() == CodecStates.NOT_WRAPPED)
240254
{
241-
throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState());
255+
throw new IllegalStateException("Cannot access field \"irId\" in state: " + CodecStates.name(codecState()));
242256
}
243257
}
244258

@@ -300,9 +314,9 @@ public static String irVersionMetaAttribute(final MetaAttribute metaAttribute)
300314

301315
private void onIrVersionAccessed()
302316
{
303-
if (codecState() == CodecState.NOT_WRAPPED)
317+
if (codecState() == CodecStates.NOT_WRAPPED)
304318
{
305-
throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState());
319+
throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState()));
306320
}
307321
}
308322

@@ -364,9 +378,9 @@ public static String schemaVersionMetaAttribute(final MetaAttribute metaAttribut
364378

365379
private void onSchemaVersionAccessed()
366380
{
367-
if (codecState() == CodecState.NOT_WRAPPED)
381+
if (codecState() == CodecStates.NOT_WRAPPED)
368382
{
369-
throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState());
383+
throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState()));
370384
}
371385
}
372386

@@ -430,11 +444,11 @@ private void onPackageNameAccessed()
430444
{
431445
switch (codecState())
432446
{
433-
case V0_BLOCK:
434-
codecState(CodecState.V0_PACKAGENAME_DONE);
447+
case CodecStates.V0_BLOCK:
448+
codecState(CodecStates.V0_PACKAGENAME_DONE);
435449
break;
436450
default:
437-
throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState());
451+
throw new IllegalStateException("Cannot access field \"packageName\" in state: " + CodecStates.name(codecState()));
438452
}
439453
}
440454

@@ -570,11 +584,11 @@ private void onNamespaceNameAccessed()
570584
{
571585
switch (codecState())
572586
{
573-
case V0_PACKAGENAME_DONE:
574-
codecState(CodecState.V0_NAMESPACENAME_DONE);
587+
case CodecStates.V0_PACKAGENAME_DONE:
588+
codecState(CodecStates.V0_NAMESPACENAME_DONE);
575589
break;
576590
default:
577-
throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState());
591+
throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState()));
578592
}
579593
}
580594

@@ -710,11 +724,11 @@ private void onSemanticVersionAccessed()
710724
{
711725
switch (codecState())
712726
{
713-
case V0_NAMESPACENAME_DONE:
714-
codecState(CodecState.V0_SEMANTICVERSION_DONE);
727+
case CodecStates.V0_NAMESPACENAME_DONE:
728+
codecState(CodecStates.V0_SEMANTICVERSION_DONE);
715729
break;
716730
default:
717-
throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState());
731+
throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState()));
718732
}
719733
}
720734

0 commit comments

Comments
 (0)