tunnelvisionlabs / antlr4

The highly-optimized fork of ANTLR 4 (see README)
Other
73 stars 12 forks source link

Improve the performance of failed semantic predicate #53

Closed daniellansun closed 4 years ago

daniellansun commented 5 years ago

Background

When semantic predicate fails, FailedPredicateException will be thrown to change the control flow. But as we all know, filling the stack trace is expensive especially when FailedPredicateException is thrown frequently.

Proposal

I propose to override fillInStackTrace() of FailedPredicateException, and its implementation is simplified as follows:

    public synchronized Throwable fillInStackTrace() {
        return this;
    }

its default implementation is:

    public synchronized Throwable fillInStackTrace() {
        if (stackTrace != null ||
            backtrace != null /* Out of protocol state */ ) {
            fillInStackTrace(0);
            stackTrace = UNASSIGNED_STACK;
        }
        return this;
    }

Advantages & Disadvantages

daniellansun commented 5 years ago

I completed a performance experiment, which is not very strict. The following code costs about 20ms on my local machine.

Without filling stack trace:

package stacktrace;

public class MyException extends RuntimeException {
    public MyException(String msg) {
        super(msg);
    }

    public synchronized Throwable fillInStackTrace() {
        return this;
    }

    public static void main(String[] args) {
        long b = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            try {
                throw new MyException("hello");
            } catch (MyException e) {

            }
        }
        long e = System.currentTimeMillis();

        System.out.println("## " + (e - b) + "ms");
    }
}
daniellansun commented 5 years ago

The following code costs about 80ms on my local machine.

With filling stack trace:

package stacktrace;

public class MyException extends RuntimeException {
    public MyException(String msg) {
        super(msg);
    }

//    public synchronized Throwable fillInStackTrace() {
//        return this;
//    }

    public static void main(String[] args) {
        long b = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            try {
                throw new MyException("hello");
            } catch (MyException e) {

            }
        }
        long e = System.currentTimeMillis();

        System.out.println("## " + (e - b) + "ms");
    }
}
daniellansun commented 5 years ago

As you can see, about 75% time can be saved.

daniellansun commented 5 years ago

I'll submit a PR later :-)