diff options
Diffstat (limited to 'test/708-jit-cache-churn/src/JitCacheChurnTest.java')
-rw-r--r-- | test/708-jit-cache-churn/src/JitCacheChurnTest.java | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/test/708-jit-cache-churn/src/JitCacheChurnTest.java b/test/708-jit-cache-churn/src/JitCacheChurnTest.java new file mode 100644 index 0000000000..abc5f35f70 --- /dev/null +++ b/test/708-jit-cache-churn/src/JitCacheChurnTest.java @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.Callable; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * A test driver for JIT compiling methods and looking for JIT + * cache issues. + */ +public class JitCacheChurnTest { + /* The name of methods to JIT */ + private static final String JITTED_METHOD = "$noinline$Call"; + + /* The number of cores to oversubscribe load by. */ + private static final int OVERSUBSCRIBED_CORES = 1; + + /* The number of concurrent executions of methods to be JIT compiled. */ + private static final int CONCURRENCY = + Runtime.getRuntime().availableProcessors() + OVERSUBSCRIBED_CORES; + + /* The number of times the methods to be JIT compiled should be executed per thread. */ + private static final int METHOD_ITERATIONS = 10; + + /* Number of test iterations JIT methods and removing methods from JIT cache. */ + private static final int TEST_ITERATIONS = 512; + + /* Tasks to run and generate compiled code of various sizes */ + private static final BaseTask [] TASKS = { + new TaskOne(), new TaskTwo(), new TaskThree(), new TaskFour(), new TaskFive(), new TaskSix(), + new TaskSeven(), new TaskEight(), new TaskNine(), new TaskTen() + }; + private static final int TASK_BITMASK = (1 << TASKS.length) - 1; + + private final ExecutorService executorService; + private int runMask = 0; + + private JitCacheChurnTest() { + this.executorService = new ThreadPoolExecutor(CONCURRENCY, CONCURRENCY, 5000, + TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>()); + } + + private void shutdown() { + this.executorService.shutdown(); + } + + private void runTasks(Callable<Integer> task) { + // Force JIT compilation of tasks method. + ensureJitCompiled(task.getClass(), JITTED_METHOD); + + // Launch worker threads to run JIT compiled method. + try { + ArrayList<Callable<Integer>> tasks = new ArrayList<>(CONCURRENCY); + for (int i = 0; i < CONCURRENCY; ++i) { + tasks.add(i, task); + } + + List<Future<Integer>> results = executorService.invokeAll(tasks); + for (Future<?> result : results) { + result.get(); + } + } catch (InterruptedException | ExecutionException e) { + System.err.println(e); + System.exit(-1); + } + } + + private static abstract class BaseTask implements Callable<Integer> { + private static CyclicBarrier barrier = new CyclicBarrier(CONCURRENCY); + + public Integer call() throws Exception { + barrier.await(); + int iterations = METHOD_ITERATIONS + 1; + for (int i = 0; i < iterations; ++i) { + $noinline$Call(); + } + return $noinline$Call(); + } + + protected abstract Integer $noinline$Call(); + } + + private static class TaskOne extends BaseTask { + @Override + protected Integer $noinline$Call() { + return null; + } + } + + private static class TaskTwo extends BaseTask { + @Override + protected Integer $noinline$Call() { + return 0; + } + } + + private static class TaskThree extends BaseTask { + @Override + protected Integer $noinline$Call() { + int sum = 0; + for (int i = 0; i < 3; ++i) { + sum = i * (i + 1); + } + return sum; + } + } + + private static class TaskFour extends BaseTask { + @Override + protected Integer $noinline$Call() { + int sum = 0; + for (int i = 0; i < 10; ++i) { + int bits = i; + bits = ((bits >>> 1) & 0x55555555) | ((bits << 1) & 0x55555555); + bits = ((bits >>> 2) & 0x33333333) | ((bits << 2) & 0x33333333); + bits = ((bits >>> 4) & 0x0f0f0f0f) | ((bits << 4) & 0x0f0f0f0f); + bits = ((bits >>> 8) & 0x00ff00ff) | ((bits << 8) & 0x00ff00ff); + bits = (bits >>> 16) | (bits << 16); + sum += bits; + } + return sum; + } + } + + private static class TaskFive extends BaseTask { + static final AtomicInteger instances = new AtomicInteger(0); + int instance; + TaskFive() { + instance = instances.getAndIncrement(); + } + protected Integer $noinline$Call() { + return instance; + } + } + + private static class TaskSix extends TaskFive { + protected Integer $noinline$Call() { + return instance + 1; + } + } + + private static class TaskSeven extends TaskFive { + protected Integer $noinline$Call() { + return 2 * instance + 1; + } + } + + private static class TaskEight extends TaskFive { + protected Integer $noinline$Call() { + double a = Math.cosh(2.22 * instance); + double b = a / 2; + double c = b * 3; + double d = a + b + c; + if (d > 42) { + d *= Math.max(Math.sin(d), Math.sinh(d)); + d *= Math.max(1.33, 0.17 * Math.sinh(d)); + d *= Math.max(1.34, 0.21 * Math.sinh(d)); + d *= Math.max(1.35, 0.32 * Math.sinh(d)); + d *= Math.max(1.36, 0.41 * Math.sinh(d)); + d *= Math.max(1.37, 0.57 * Math.sinh(d)); + d *= Math.max(1.38, 0.61 * Math.sinh(d)); + d *= Math.max(1.39, 0.79 * Math.sinh(d)); + d += Double.parseDouble("3.711e23"); + } + + if (d > 3) { + return (int) a; + } else { + return (int) b; + } + } + } + + private static class TaskNine extends TaskFive { + private final String [] numbers = { "One", "Two", "Three", "Four", "Five", "Six" }; + + protected Integer $noinline$Call() { + String number = numbers[instance % numbers.length]; + return number.length(); + } + } + + private static class TaskTen extends TaskFive { + private final String [] numbers = { "12345", "23451", "34512", "78901", "89012" }; + + protected Integer $noinline$Call() { + int odd = 0; + String number = numbers[instance % numbers.length]; + for (int i = 0; i < number.length(); i += 2) { + odd += Integer.parseInt(numbers[i]); + } + odd *= 3; + + int even = 0; + for (int i = 1; i < number.length(); i += 2) { + even += Integer.parseInt(numbers[i]); + } + return (odd + even) % 10; + } + } + + private void runAndJitMethods(int mask) { + runMask |= mask; + for (int index = 0; mask != 0; mask >>= 1, index++) { + if ((mask & 1) == 1) { + runTasks(TASKS[index]); + } + } + } + + private static void ensureJitCompiled(Class<?> klass, String name) { + Main.ensureJitCompiled(klass, name); + } + + private void removeJittedMethod(Class<?> klass, String name) { + Method method = null; + try { + method = klass.getDeclaredMethod(name); + } catch (NoSuchMethodException e) { + System.err.println(e); + System.exit(-1); + } + removeJitCompiledMethod(method, false); + } + + private void removeJittedMethods(int mask) { + mask = mask & runMask; + runMask ^= mask; + for (int index = 0; mask != 0; mask >>= 1, index++) { + if ((mask & 1) == 1) { + removeJittedMethod(TASKS[index].getClass(), JITTED_METHOD); + } + } + } + + private static int getMethodsAsMask(Random rng) { + return rng.nextInt(TASK_BITMASK) + 1; + } + + public static void run() { + JitCacheChurnTest concurrentExecution = new JitCacheChurnTest(); + Random invokeMethodGenerator = new Random(5); + Random removeMethodGenerator = new Random(7); + try { + for (int i = 0; i < TEST_ITERATIONS; ++i) { + concurrentExecution.runAndJitMethods(getMethodsAsMask(invokeMethodGenerator)); + concurrentExecution.removeJittedMethods(getMethodsAsMask(removeMethodGenerator)); + } + } finally { + concurrentExecution.shutdown(); + } + } + + private static native void removeJitCompiledMethod(Method method, boolean releaseMemory); +} |