Skip to content

Commit

Permalink
Experiment in nuking the JIT counter in methods if the threshold has …
Browse files Browse the repository at this point in the history
…been

reached BUT it has taken longer than -Xjit.time.delta ns since the last time
saved when the counter was set to 0.

This is totally untuned in that I made up a number and it I suspect is too
dramatically not JITting methods.  In running an oj bench and gem list (with
and without --dev) and starting up rails the times do not appear to be much
different.

After tuning the value (which I guess would be for this machine -- a follow
up to this work will be whether one magic constant can work here) which would
be figuring out proper time delta and also determing whether any important
methods which should have JITted had not there is a followup change I want to
try: change threshold to be more than just a method call counter.  I would
like to also add threadpolls as an increment value.  Then larger methods which
are not called as much will end up as JIT candidates.
  • Loading branch information
enebo committed Sep 10, 2019
1 parent 59c1bac commit 24632b3
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 3 deletions.
2 changes: 1 addition & 1 deletion core/pom.xml
Expand Up @@ -634,7 +634,7 @@ DO NOT MODIFIY - GENERATED CODE
</manifestEntries>
</transformer>
</transformers>
<Createsourcesjar>${create.sources.jar}</Createsourcesjar>
<createSourcesJar>${create.sources.jar}</createSourcesJar>
</configuration>
</execution>
</executions>
Expand Down
Expand Up @@ -30,12 +30,17 @@ public class InterpretedIRMethod extends AbstractIRMethod implements Compilable<

protected InterpreterContext interpreterContext = null;
protected int callCount = 0;
protected long time;

public InterpretedIRMethod(IRScope method, Visibility visibility, RubyModule implementationClass) {
super(method, visibility, implementationClass);

// -1 jit.threshold is way of having interpreter not promote full builds.
if (Options.JIT_THRESHOLD.load() == -1) callCount = -1;
if (Options.JIT_THRESHOLD.load() == -1) {
callCount = -1;
} else {
time = System.nanoTime();
}

// If we are printing, do the build right at creation time so we can see it
if (IRRuntimeHelpers.shouldPrintIR(implementationClass.getRuntime())) {
Expand Down Expand Up @@ -296,7 +301,17 @@ private void promoteToFullBuild(ThreadContext context) {

if (runtime.isBooting() && !Options.JIT_KERNEL.load()) return; // don't Promote to full build during runtime boot

if (callCount++ >= Options.JIT_THRESHOLD.load()) runtime.getJITCompiler().buildThresholdReached(context, this);
if (callCount++ >= Options.JIT_THRESHOLD.load()) {
long newTime = System.nanoTime();

if ((newTime - time) >= Options.JIT_TIME_DELTA.load()) {
callCount = 0;
time = newTime;
return;
}

runtime.getJITCompiler().buildThresholdReached(context, this);
}

if (IRRuntimeHelpers.shouldPrintIR(implementationClass.getRuntime())) {
ByteArrayOutputStream baos = IRDumper.printIR(method, true, true);
Expand Down
Expand Up @@ -26,6 +26,7 @@ public class MixedModeIRMethod extends AbstractIRMethod implements Compilable<Dy
private boolean displayedCFG = false; // FIXME: Remove when we find nicer way of logging CFG

private volatile int callCount = 0;
protected volatile long time;
private volatile DynamicMethod actualMethod;

public MixedModeIRMethod(IRScope method, Visibility visibility, RubyModule implementationClass) {
Expand All @@ -35,6 +36,8 @@ public MixedModeIRMethod(IRScope method, Visibility visibility, RubyModule imple
if (!implementationClass.getRuntime().getInstanceConfig().getCompileMode().shouldJIT() ||
Options.JIT_THRESHOLD.load() < 0) {
callCount = -1;
} else {
time = System.nanoTime();
}

// This is so profiled callsite can access the sites original method (callsites has IRScope in it).
Expand Down Expand Up @@ -271,6 +274,14 @@ private void tryJit(ThreadContext context) {

synchronized (this) {
if (callCount >= 0 && callCount++ >= Options.JIT_THRESHOLD.load()) {
long newTime = System.nanoTime();

if ((newTime - time) >= Options.JIT_TIME_DELTA.load()) {
callCount = 0;
time = newTime;
return;
}

context.runtime.getJITCompiler().buildThresholdReached(context, this);
}
}
Expand Down
238 changes: 238 additions & 0 deletions core/src/main/java/org/jruby/parser/#skeleton.parser#
@@ -0,0 +1,238 @@
# jay skeleton for Java

# character in column 1 determines outcome...
# # is a comment
# . is copied
# t is copied as //t if -t is set
# other lines are interpreted to call jay procedures

version Java 1.0 (c) 2002 ats@cs.rit.edu
.
prolog ## %{ ... %} prior to the first %%

. // %token constants
tokens public static final int
.
. /** number of final state.
. */
yyFinal protected static final int yyFinal =
.
. /** parser tables.
. Order is mandated by <i>jay</i>.
. */
. protected static final short[] yyLhs = {
yyLhs
. }, yyLen = {
yyLen
. }, yyDefRed = {
yyDefRed
. }, yyDgoto = {
yyDgoto
. }, yySindex = {
yySindex
. }, yyRindex = {
yyRindex
. }, yyGindex = {
yyGindex
. };
. protected static final short[] yyTable = {
yyTable
. };
. protected static final short[] yyCheck = {
yyCheck
. };
.
. /** maps symbol value to printable name.
. @see #yyExpecting
. */
. protected static final String[] yyNames = {
yyNames-strings
. };
.
t /** printable rules for debugging.
t */
t protected static final String [] yyRule = {
yyRule-strings
t };
t
t /** index-checked interface to {@link #yyNames}.
t @param token single character or <tt>%token</tt> value.
t @return token name or <tt>[illegal]</tt> or <tt>[unknown]</tt>.
t */
t public static String yyName (int token) {
t if (token < 0 || token > yyNames.length) return "[illegal]";
t String name;
t if ((name = yyNames[token]) != null) return name;
t return "[unknown]";
t }
t
.
. /** computes list of expected tokens on error by tracing the tables.
. @param state for which to compute the list.
. @return list of token names.
. */
. protected String[] yyExpecting (int state) {
. int token, n, len = 0;
. boolean[] ok = new boolean[yyNames.length];
.
. if ((n = yySindex[state]) != 0)
. for (token = n < 0 ? -n : 0;
. token < yyNames.length && n+token < yyTable.length; ++ token)
. if (yyCheck[n+token] == token && !ok[token] && yyNames[token] != null) {
. ++ len;
. ok[token] = true;
. }
. if ((n = yyRindex[state]) != 0)
. for (token = n < 0 ? -n : 0;
. token < yyNames.length && n+token < yyTable.length; ++ token)
. if (yyCheck[n+token] == token && !ok[token] && yyNames[token] != null) {
. ++ len;
. ok[token] = true;
. }
.
. String result[] = new String[len];
. for (n = token = 0; n < len; ++ token)
. if (ok[token]) result[n++] = yyNames[token];
. return result;
. }
.
. /** the generated parser, with debugging messages.
. Maintains a dynamic state and value stack.
. @param yyLex scanner.
. @param ayydebug debug message writer implementing <tt>yyDebug</tt>, or <tt>null</tt>.
. @return result of the last reduction, if any.
. */
. public Object yyparse (RubyLexer yyLex, Object ayydebug)
. throws java.io.IOException {
. return yyparse(yyLex);
. }
.
. /** initial size and increment of the state/value stack [default 256].
. This is not final so that it can be overwritten outside of invocations
. of {@link #yyparse}.
. */
. protected int yyMax;
.
. /** executed at the beginning of a reduce action.
. Used as <tt>$$ = yyDefault($1)</tt>, prior to the user-specified action, if any.
. Can be overwritten to provide deep copy, etc.
. @param first value for <tt>$1</tt>, or <tt>null</tt>.
. @return first.
. */
. protected Object yyDefault (Object first) {
. return first;
. }
.
. /** the generated parser.
. Maintains a dynamic state and value stack.
. @param yyLex scanner.
. @return result of the last reduction, if any.
. */
. public Object yyparse (RubyLexer yyLex) throws java.io.IOException {
. if (yyMax <= 0) yyMax = 256; // initial size
. int yyState = 0, yyStates[] = new int[yyMax]; // state stack
. Object yyVal = null, yyVals[] = new Object[yyMax]; // value stack
. int yyToken = -1; // current input
. int yyErrorFlag = 0; // #tokens to shift
.

local ## %{ ... %} after the first %%

. yyLoop: for (int yyTop = 0;; ++ yyTop) {
. if (yyTop >= yyStates.length) { // dynamically increase
. System.out.println("HERE!!!! " + yyTop);
. int[] i = new int[yyStates.length+yyMax];
. System.arraycopy(yyStates, 0, i, 0, yyStates.length);
. yyStates = i;
. Object[] o = new Object[yyVals.length+yyMax];
. System.arraycopy(yyVals, 0, o, 0, yyVals.length);
. yyVals = o;
. }
. yyStates[yyTop] = yyState;
. yyVals[yyTop] = yyVal;
.
. yyDiscarded: for (;;) { // discarding a token does not change stack
. int yyN;
. if ((yyN = yyDefRed[yyState]) == 0) { // else [default] reduce (yyN)
. if (yyToken < 0) {
.// yyToken = yyLex.advance() ? yyLex.token() : 0;
. yyToken = yyLex.nextToken();
. }
. if ((yyN = yySindex[yyState]) != 0 && (yyN += yyToken) >= 0
. && yyN < yyTable.length && yyCheck[yyN] == yyToken) {
. yyState = yyTable[yyN]; // shift to yyN
. yyVal = yyLex.yaccValue;
. yyToken = -1;
. if (yyErrorFlag > 0) -- yyErrorFlag;
. continue yyLoop;
. }
. if ((yyN = yyRindex[yyState]) != 0 && (yyN += yyToken) >= 0
. && yyN < yyTable.length && yyCheck[yyN] == yyToken)
. yyN = yyTable[yyN]; // reduce (yyN)
. else
. switch (yyErrorFlag) {
.
. case 0:
. support.yyerror("syntax error", yyExpecting(yyState), yyNames[yyToken]);
.
. case 1: case 2:
. yyErrorFlag = 3;
. do {
. if ((yyN = yySindex[yyStates[yyTop]]) != 0
. && (yyN += yyErrorCode) >= 0 && yyN < yyTable.length
. && yyCheck[yyN] == yyErrorCode) {
. yyState = yyTable[yyN];
. yyVal = yyLex.yaccValue;
. continue yyLoop;
. }
. } while (-- yyTop >= 0);
. support.yyerror("irrecoverable syntax error");
.
. case 3:
. if (yyToken == 0) {
. support.yyerror("irrecoverable syntax error at end-of-file");
. }
. yyToken = -1;
. continue yyDiscarded; // leave stack alone
. }
. }
. int yyV = yyTop + 1-yyLen[yyN];
. ParserState state = states[yyN];
. if (state == null) {
. yyVal = yyDefault(yyV > yyTop ? null : yyVals[yyV]);
. } else {
. yyVal = state.execute(support, lexer, yyVal, yyVals, yyTop);
. }
.// switch (yyN) {
.// ACTIONS_BEGIN

actions ## code from the actions within the grammar

.// ACTIONS_END
.// }
. yyTop -= yyLen[yyN];
. yyState = yyStates[yyTop];
. int yyM = yyLhs[yyN];
. if (yyState == 0 && yyM == 0) {
. yyState = yyFinal;
. if (yyToken < 0) {
. yyToken = yyLex.nextToken();
.// yyToken = yyLex.advance() ? yyLex.token() : 0;
. }
. if (yyToken == 0) {
. return yyVal;
. }
. continue yyLoop;
. }
. if ((yyN = yyGindex[yyM]) != 0 && (yyN += yyState) >= 0
. && yyN < yyTable.length && yyCheck[yyN] == yyState)
. yyState = yyTable[yyN];
. else
. yyState = yyDgoto[yyM];
. continue yyLoop;
. }
. }
. }
.
.// ACTION_BODIES
epilog ## text following second %%
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/parser/.#skeleton.parser
11 changes: 11 additions & 0 deletions core/src/main/java/org/jruby/runtime/InterpretedIRBlockBody.java
Expand Up @@ -20,6 +20,7 @@ public class InterpretedIRBlockBody extends IRBlockBody implements Compilable<In
protected boolean reuseParentScope;
private boolean displayedCFG = false; // FIXME: Remove when we find nicer way of logging CFG
private int callCount = 0;
private long time;
private InterpreterContext interpreterContext;
private InterpreterContext fullInterpreterContext;

Expand All @@ -32,6 +33,8 @@ public InterpretedIRBlockBody(IRClosure closure, Signature signature) {
// promote to full build here if we are -X-C.
if (closure.getManager().getInstanceConfig().getCompileMode().shouldJIT() || Options.JIT_THRESHOLD.load() == -1) {
callCount = -1;
} else {
time = System.nanoTime();
}
}

Expand Down Expand Up @@ -147,6 +150,14 @@ private void promoteToFullBuild(ThreadContext context) {
if (context.runtime.isBooting() && !Options.JIT_KERNEL.load()) return; // don't Promote to full build during runtime boot

if (callCount++ >= Options.JIT_THRESHOLD.load()) {
long newTime = System.nanoTime();

if ((newTime - time) >= Options.JIT_TIME_DELTA.load()) {
callCount = 0;
time = newTime;
return;
}

context.runtime.getJITCompiler().buildThresholdReached(context, this);
}
}
Expand Down
11 changes: 11 additions & 0 deletions core/src/main/java/org/jruby/runtime/MixedModeIRBlockBody.java
Expand Up @@ -24,6 +24,7 @@ public class MixedModeIRBlockBody extends IRBlockBody implements Compilable<Comp
protected boolean reuseParentScope;
private boolean displayedCFG = false; // FIXME: Remove when we find nicer way of logging CFG
private volatile int callCount = 0;
private volatile long time;
private InterpreterContext interpreterContext;
private volatile CompiledIRBlockBody jittedBody;

Expand All @@ -37,6 +38,8 @@ public MixedModeIRBlockBody(IRClosure closure, Signature signature) {
if (!closure.getManager().getInstanceConfig().getCompileMode().shouldJIT() ||
Options.JIT_THRESHOLD.load() < 0) {
callCount = -1;
} else {
time = System.nanoTime();
}
}

Expand Down Expand Up @@ -164,6 +167,14 @@ private void promoteToFullBuild(ThreadContext context) {
if (callCount < 0) return;

if (callCount++ >= Options.JIT_THRESHOLD.load()) {
long newTime = System.nanoTime();

if ((newTime - time) >= Options.JIT_TIME_DELTA.load()) {
callCount = 0;
time = newTime;
return;
}

callCount = -1;

// ensure we've got code ready for JIT
Expand Down
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/util/cli/Options.java
Expand Up @@ -104,6 +104,7 @@ public class Options {
public static final Option<Boolean> INVOKEDYNAMIC_YIELD = bool(INVOKEDYNAMIC, "invokedynamic.yield", false, "Bind yields directly using invokedynamic.");

public static final Option<Integer> JIT_THRESHOLD = integer(JIT, "jit.threshold", Constants.JIT_THRESHOLD, "Set the JIT threshold to the specified method invocation count.");
public static final Option<Integer> JIT_TIME_DELTA = integer(JIT, "jit.time.delta", 100000, "Set the JIT hotness time delta in ns to consider JITting");
public static final Option<Integer> JIT_MAX = integer(JIT, "jit.max", Constants.JIT_MAX_METHODS_LIMIT, "Set the max count of active methods eligible for JIT-compilation.");
public static final Option<Integer> JIT_MAXSIZE = integer(JIT, "jit.maxsize", Constants.JIT_MAX_SIZE_LIMIT, "Set the max size (in IR instructions) for a method to be eligible to JIT.");
public static final Option<Boolean> JIT_LOGGING = bool(JIT, "jit.logging", false, "Enable JIT logging (reports successful compilation).");
Expand Down

0 comments on commit 24632b3

Please sign in to comment.