1/*
2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.hotspot.test;
24
25import static org.graalvm.compiler.debug.MemUseTrackerKey.getCurrentThreadAllocatedBytes;
26
27import org.graalvm.compiler.api.test.Graal;
28import org.graalvm.compiler.core.test.AllocSpy;
29import org.graalvm.compiler.hotspot.CompilationTask;
30import org.graalvm.compiler.hotspot.HotSpotGraalCompiler;
31import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
32import org.graalvm.compiler.options.OptionValues;
33
34import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
35import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
36import jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider;
37import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
38import jdk.vm.ci.runtime.JVMCICompiler;
39
40/**
41 * Used to benchmark memory usage during Graal compilation.
42 *
43 * To benchmark:
44 *
45 * <pre>
46 *     mx vm -XX:-UseJVMCIClassLoader -cp @org.graalvm.compiler.hotspot.test org.graalvm.compiler.hotspot.test.MemoryUsageBenchmark
47 * </pre>
48 *
49 * Memory analysis for a {@link CompileTheWorld} execution can also be performed. For example:
50 *
51 * <pre>
52 *     mx vm -XX:-UseJVMCIClassLoader -DCompileTheWorld.Classpath=$HOME/SPECjvm2008/SPECjvm2008.jar -cp @org.graalvm.compiler.hotspot.test org.graalvm.compiler.hotspot.test.MemoryUsageBenchmark
53 * </pre>
54 */
55public class MemoryUsageBenchmark extends HotSpotGraalCompilerTest {
56
57    public static int simple(int a, int b) {
58        return a + b;
59    }
60
61    public static synchronized int complex(CharSequence cs) {
62        if (cs instanceof String) {
63            return cs.hashCode();
64        }
65
66        if (cs instanceof StringBuilder) {
67            int[] hash = {0};
68            cs.chars().forEach(c -> hash[0] += c);
69            return hash[0];
70        }
71
72        int res = 0;
73
74        // Exercise lock elimination
75        synchronized (cs) {
76            res = cs.length();
77        }
78        synchronized (cs) {
79            res = cs.hashCode() ^ 31;
80        }
81
82        for (int i = 0; i < cs.length(); i++) {
83            res *= cs.charAt(i);
84        }
85
86        // A fixed length loop with some canonicalizable arithmetics will
87        // activate loop unrolling and more canonicalization
88        int sum = 0;
89        for (int i = 0; i < 5; i++) {
90            sum += i * 2;
91        }
92        res += sum;
93
94        // Activates escape-analysis
95        res += new String("asdf").length();
96
97        return res;
98    }
99
100    static class MemoryUsageCloseable implements AutoCloseable {
101
102        private final long start;
103        private final String name;
104
105        MemoryUsageCloseable(String name) {
106            this.name = name;
107            this.start = getCurrentThreadAllocatedBytes();
108        }
109
110        @Override
111        public void close() {
112            long end = getCurrentThreadAllocatedBytes();
113            long allocated = end - start;
114            System.out.println(name + ": " + allocated);
115        }
116    }
117
118    public static void main(String[] args) {
119        // Ensure a Graal runtime is initialized prior to Debug being initialized as the former
120        // may include processing command line options used by the latter.
121        Graal.getRuntime();
122
123        new MemoryUsageBenchmark().run();
124    }
125
126    @SuppressWarnings("try")
127    private void doCompilation(String methodName, String label) {
128        HotSpotResolvedJavaMethod method = (HotSpotResolvedJavaMethod) getResolvedJavaMethod(methodName);
129
130        // invalidate any existing compiled code
131        method.reprofile();
132
133        long jvmciEnv = 0L;
134
135        try (MemoryUsageCloseable c = label == null ? null : new MemoryUsageCloseable(label)) {
136            HotSpotJVMCIRuntimeProvider runtime = HotSpotJVMCIRuntime.runtime();
137            int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
138            HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, entryBCI, jvmciEnv);
139            CompilationTask task = new CompilationTask(runtime, (HotSpotGraalCompiler) runtime.getCompiler(), request, true, false, getInitialOptions());
140            task.runCompilation();
141        }
142    }
143
144    @SuppressWarnings("try")
145    private void allocSpyCompilation(String methodName) {
146        if (AllocSpy.isEnabled()) {
147            HotSpotResolvedJavaMethod method = (HotSpotResolvedJavaMethod) getResolvedJavaMethod(methodName);
148
149            // invalidate any existing compiled code
150            method.reprofile();
151
152            long jvmciEnv = 0L;
153            try (AllocSpy as = AllocSpy.open(methodName)) {
154                HotSpotJVMCIRuntimeProvider runtime = HotSpotJVMCIRuntime.runtime();
155                HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, JVMCICompiler.INVOCATION_ENTRY_BCI, jvmciEnv);
156                HotSpotGraalCompiler compiler = (HotSpotGraalCompiler) runtime.getCompiler();
157                OptionValues options = getInitialOptions();
158                CompilationTask task = new CompilationTask(runtime, compiler, request, true, false, options);
159                task.runCompilation();
160            }
161        }
162    }
163
164    private static final boolean verbose = Boolean.getBoolean("verbose");
165
166    private void compileAndTime(String methodName) {
167
168        // Parse in eager mode to resolve methods/fields/classes
169        parseEager(methodName, AllowAssumptions.YES);
170
171        // Warm up and initialize compiler phases used by this compilation
172        for (int i = 0; i < 10; i++) {
173            doCompilation(methodName, verbose ? methodName + "[warmup-" + i + "]" : null);
174        }
175
176        doCompilation(methodName, methodName);
177    }
178
179    public void run() {
180        compileAndTime("simple");
181        compileAndTime("complex");
182        OptionValues options = CompileTheWorld.loadOptions(getInitialOptions());
183        if (CompileTheWorld.Options.Classpath.getValue(options) != CompileTheWorld.SUN_BOOT_CLASS_PATH) {
184            HotSpotJVMCIRuntimeProvider runtime = HotSpotJVMCIRuntime.runtime();
185            CompileTheWorld ctw = new CompileTheWorld(runtime, (HotSpotGraalCompiler) runtime.getCompiler(), options);
186            try {
187                ctw.compile();
188            } catch (Throwable e) {
189                e.printStackTrace();
190            }
191        }
192        allocSpyCompilation("simple");
193        allocSpyCompilation("complex");
194    }
195}
196