diff truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/profiles/LoopConditionProfile.java @ 22501:a63bda98cfdb

Extract profiles into separate package. Add isProfilingEnabled in TruffleRuntime to disable profiling in the default runtime; Add low overhead profiles for primitives; Add LoopConditionProfile; Profile footprint/threadsafety improvements; Make toString implementations more consistent; Greatly enhanced javadoc documentation for profiles; Deprecate old profiles
author Christian Humer <christian.humer@oracle.com>
date Wed, 16 Dec 2015 16:38:13 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/profiles/LoopConditionProfile.java	Wed Dec 16 16:38:13 2015 +0100
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.profiles;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.Truffle;
+
+/**
+ * <p>
+ * LoopConditionProfiles are designed to profile the outcome of loop conditions. Loop profiles can
+ * be used to profile unpredictable loops as well as predictable loops.
+ * </p>
+ *
+ * <p>
+ * <b> Arbitrary loop usage example: </b>
+ *
+ * <pre>
+ * class LoopNode extends Node {
+ * 
+ *     final LoopConditionProfile loopProfile = LoopConditionProfile.createCountingProfile();
+ * 
+ *     void execute() {
+ *         // loop count cannot be predicted
+ *         while (loopProfile.profile(Math.random() &gt;= 0.9)) {
+ *             // work
+ *         }
+ *     }
+ * }
+ * </pre>
+ *
+ * </p>
+ *
+ * <p>
+ * <b> Counted loop usage example: </b>
+ *
+ * <pre>
+ * class CountedLoopNode extends Node {
+ * 
+ *     final LoopConditionProfile loopProfile = LoopConditionProfile.createCountingProfile();
+ * 
+ *     void execute(int length) {
+ *         // loop count can be predicted
+ *         loopProfile.profileCounted(length);
+ *         for (int i = 0; loopProfile.inject(i &lt; length); i++) {
+ *             // work
+ *         }
+ *     }
+ * }
+ * </pre>
+ *
+ * </p>
+ * <p>
+ * The advantage of using {@link #profileCounted(long)} to using {@link #profile(boolean)} is that
+ * it incurs less overhead in the interpreter. Using {@link LoopConditionProfile#inject(boolean)} is
+ * a no-op in the interpreter while {@link #profile(boolean)} needs to use a counter for each
+ * iteration.
+ * </p>
+ *
+ *
+ * {@inheritDoc}
+ *
+ * @see #createBinaryProfile()
+ * @see #createCountingProfile()
+ * @see LoopConditionProfile
+ */
+public abstract class LoopConditionProfile extends ConditionProfile {
+
+    LoopConditionProfile() {
+    }
+
+    @Override
+    public abstract boolean profile(boolean value);
+
+    /**
+     * Provides an alternative way to profile counted loops with less interpreter footprint. Please
+     * see {@link LoopConditionProfile} for an usage example.
+     *
+     * @see #inject(boolean)
+     */
+    public abstract void profileCounted(long length);
+
+    /**
+     * Provides an alternative way to profile counted loops with less interpreter footprint. Please
+     * see {@link LoopConditionProfile} for an usage example.
+     *
+     * @see #inject(boolean)
+     */
+    public abstract boolean inject(boolean condition);
+
+    /**
+     * Returns a {@link LoopConditionProfile} that speculates on loop conditions to be never
+     * <code>true</code>. It also captures loop probabilities for the compiler. Loop condition
+     * profiles are intended to be used for loop conditions.
+     *
+     * @see LoopConditionProfile
+     */
+    public static LoopConditionProfile createCountingProfile() {
+        if (Truffle.getRuntime().isProfilingEnabled()) {
+            return Enabled.create();
+        } else {
+            return Disabled.INSTANCE;
+        }
+    }
+
+    static final class Enabled extends LoopConditionProfile {
+
+        @CompilationFinal private long trueCount; // long for long running loops.
+        @CompilationFinal private int falseCount;
+
+        @Override
+        public boolean profile(boolean condition) {
+            if (CompilerDirectives.inInterpreter()) {
+                if (condition) {
+                    // local required to guarantee no overflow in multi-threaded environments
+                    long localTrueCount = trueCount;
+                    if (localTrueCount < Long.MAX_VALUE) {
+                        trueCount = localTrueCount + 1;
+                    }
+                } else {
+                    // local required to guarantee no overflow in multi-threaded environments
+                    int localFalseCount = falseCount;
+                    if (localFalseCount < Integer.MAX_VALUE) {
+                        falseCount = localFalseCount + 1;
+                    }
+                }
+                // no branch probability calculation in the interpreter
+                return condition;
+            } else {
+                long trueCountLocal = trueCount;
+                int falseCountLocal = falseCount;
+                if (trueCountLocal == 0) {
+                    /* Deopt for never entering the loop. */
+                    if (condition) {
+                        CompilerDirectives.transferToInterpreterAndInvalidate();
+                        trueCount = trueCountLocal = 1;
+                    }
+                }
+                /* No deopt for not entering the loop. */
+                return CompilerDirectives.injectBranchProbability(calculateProbability(trueCountLocal, falseCountLocal), condition);
+            }
+        }
+
+        @Override
+        public void profileCounted(long length) {
+            if (CompilerDirectives.inInterpreter()) {
+                long trueCountLocal = trueCount + length;
+                if (trueCountLocal >= 0) { // don't write overflow values
+                    trueCount = trueCountLocal;
+                    int falseCountLocal = falseCount;
+                    if (falseCountLocal < Integer.MAX_VALUE) {
+                        falseCount = falseCountLocal + 1;
+                    }
+                }
+            }
+        }
+
+        @Override
+        public boolean inject(boolean condition) {
+            if (CompilerDirectives.inCompiledCode()) {
+                return CompilerDirectives.injectBranchProbability(calculateProbability(trueCount, falseCount), condition);
+            } else {
+                return condition;
+            }
+        }
+
+        private static double calculateProbability(long trueCountLocal, int falseCountLocal) {
+            if (falseCountLocal == 0 && trueCountLocal == 0) {
+                /* Avoid division by zero if profile was never used. */
+                return 0.0;
+            } else {
+                return (double) trueCountLocal / (double) (trueCountLocal + falseCountLocal);
+            }
+        }
+
+        /* for testing */
+        long getTrueCount() {
+            return trueCount;
+        }
+
+        /* for testing */
+        int getFalseCount() {
+            return falseCount;
+        }
+
+        @Override
+        public String toString() {
+            return toString(LoopConditionProfile.class, falseCount == 0, false, //
+                            String.format("trueProbability=%s (trueCount=%s, falseCount=%s)", calculateProbability(trueCount, falseCount), falseCount, trueCount));
+        }
+
+        /* Needed for lazy class loading. */
+        static LoopConditionProfile create() {
+            return new Enabled();
+        }
+
+    }
+
+    static final class Disabled extends LoopConditionProfile {
+
+        static final LoopConditionProfile INSTANCE = new Disabled();
+
+        @Override
+        protected Object clone() {
+            return INSTANCE;
+        }
+
+        @Override
+        public boolean profile(boolean condition) {
+            return condition;
+        }
+
+        @Override
+        public void profileCounted(long length) {
+        }
+
+        @Override
+        public boolean inject(boolean condition) {
+            return condition;
+        }
+
+        @Override
+        public String toString() {
+            return toStringDisabled(LoopConditionProfile.class);
+        }
+
+    }
+
+}