Skip to content

[GR-64069] Graal memory usage improvements. #11086

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -199,7 +198,7 @@ public void duringSetup(DuringSetupAccess access) {
// (see jdk.graal.compiler.graph.NodeClass.allocateInstance).
access.registerObjectReachabilityHandler(nodeClass -> {
Class<?> clazz = nodeClass.getClazz();
if (!Modifier.isAbstract(clazz.getModifiers())) {
if (!nodeClass.isAbstract()) {
/* Support for NodeClass.allocateInstance. */
beforeAnalysisAccess.registerAsUnsafeAllocated(clazz);
}
Expand Down Expand Up @@ -375,9 +374,12 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
EncodedSnippets encodedSnippets = (EncodedSnippets) libgraalObjects.get("encodedSnippets");
checkNodeClasses(encodedSnippets, (String) libgraalObjects.get("snippetNodeClasses"));

// Mark all the Node classes as allocated so they are available during graph decoding.
// Mark all non-abstract Node classes as allocated so they
// are available during graph decoding.
for (NodeClass<?> nodeClass : encodedSnippets.getSnippetNodeClasses()) {
access.registerAsInHeap(nodeClass.getClazz());
if (!nodeClass.isAbstract()) {
access.registerAsInHeap(nodeClass.getClazz());
}
}
HotSpotReplacementsImpl.setEncodedSnippets(encodedSnippets);

Expand All @@ -389,7 +391,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {

private static void checkNodeClasses(EncodedSnippets encodedSnippets, String actual) {
String expect = CompilerConfig.snippetNodeClassesToJSON(encodedSnippets);
GraalError.guarantee(actual.equals(expect), "%s != %s", actual, expect);
GraalError.guarantee(actual.equals(expect), "%n%s%n !=%n%s", actual, expect);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public static SubprocessUtil.Subprocess launchSubprocess(Predicate<List<String>>
assert testSelector != null : "must pass the name of the current unit test";
String testName = testSelector.equals(ALL_TESTS) ? testClass.getName() : testClass.getName() + "#" + testSelector;
mainClassAndArgs.add(testName);
boolean junitVerbose = getProcessCommandLine().contains("-JUnitVerbose");
boolean junitVerbose = String.valueOf(getProcessCommandLine()).contains("-JUnitVerbose");
if (junitVerbose) {
mainClassAndArgs.add("-JUnitVerbose");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import static jdk.graal.compiler.debug.StandardPathUtilitiesProvider.DIAGNOSTIC_OUTPUT_DIRECTORY_MESSAGE_FORMAT;
import static jdk.graal.compiler.debug.StandardPathUtilitiesProvider.DIAGNOSTIC_OUTPUT_DIRECTORY_MESSAGE_REGEXP;
import static jdk.graal.compiler.test.SubprocessUtil.getProcessCommandLine;
import static jdk.graal.compiler.test.SubprocessUtil.getVMCommandLine;
import static jdk.graal.compiler.test.SubprocessUtil.withoutDebuggerArguments;

Expand Down Expand Up @@ -232,7 +233,7 @@ public void testTruffleCompilation2() throws IOException, InterruptedException {
SLCompileASTTestSuite.class.getName(), "test");
}

private static final boolean VERBOSE = Boolean.getBoolean("CompilationWrapperTest.verbose");
private static final boolean VERBOSE = Boolean.getBoolean("CompilationWrapperTest.verbose") || String.valueOf(getProcessCommandLine()).contains("-JUnitVerbose");

public static void testHelper(List<Probe> initialProbes,
List<String> extraVmArgs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ static class TestObject extends BaseClass {
int intField3 = Integer.MIN_VALUE;
int intField4 = Integer.MAX_VALUE;
int intField5 = -1;
long longField1 = 0;
long longField2 = 42L;
long longField3 = Long.MIN_VALUE;
long longField4 = Long.MAX_VALUE;
long longField5 = -1L;
float floatField1 = -1.4F;
float floatField2 = Float.MIN_NORMAL;
float floatField3 = Float.MIN_VALUE;
Expand Down Expand Up @@ -120,13 +125,18 @@ public String toString() {
}
};

private static List<Object> fieldValues(Object obj) {
List<Object> values = new ArrayList<>();
@ObjectCopier.NotExternalValue(reason = "testing NotExternalValue annotation") //
static final String[] TEST_OBJECT_COPIABLE = {
"this", "value", "should", "be", "copied"
};

private static Map<Field, Object> fieldValues(Object obj) {
Map<Field, Object> values = new HashMap<>();
Class<?> c = obj.getClass();
while (c != Object.class) {
for (Field f : c.getDeclaredFields()) {
try {
values.add(f.get(obj));
values.put(f, f.get(obj));
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
Expand All @@ -138,7 +148,7 @@ private static List<Object> fieldValues(Object obj) {

@Override
public String toString() {
return fieldValues(this).stream().map(String::valueOf).collect(Collectors.joining(", "));
return fieldValues(this).values().stream().map(String::valueOf).collect(Collectors.joining(", "));
}
}

Expand All @@ -150,10 +160,16 @@ public void testIt() {
ClassLoader loader = getClass().getClassLoader();
TestObject testObject = new TestObject();

EconomicMap<String, Object> fieldMap = EconomicMap.create();
for (var e : TestObject.fieldValues(testObject).entrySet()) {
fieldMap.put(e.getKey().getName(), e.getValue());
}

List<TimeUnit> timeUnits = List.of(TimeUnit.MICROSECONDS, TimeUnit.DAYS, TimeUnit.SECONDS);
EconomicMap<Integer, Object> emap = EconomicMap.create();
emap.put(42, Map.of("1", 1, "2", 2));
emap.put(-12345, testObject);
emap.put(-6789, fieldMap);

Map<String, String> hmap = new HashMap<>(Map.of("1000", "one thousand"));
Map<Object, String> idMap = new IdentityHashMap<>(Map.of(new Object(), "some obj"));
Expand All @@ -172,9 +188,13 @@ public void testIt() {
root.put("6", new ArrayList<>(timeUnits));
root.put("singleton2", TestObject.TEST_OBJECT_SINGLETON);
root.put("singleton2_2", TestObject.TEST_OBJECT_SINGLETON);
root.put("copiable", TestObject.TEST_OBJECT_COPIABLE);

List<Field> externalValueFields = new ArrayList<>();
externalValueFields.addAll(ObjectCopier.getStaticFinalObjectFields(BaseClass.class));
externalValueFields.addAll(ObjectCopier.getStaticFinalObjectFields(TestObject.class));

List<Field> externalValueFields = List.of(ObjectCopier.getField(BaseClass.class, "BASE_SINGLETON"),
ObjectCopier.getField(TestObject.class, "TEST_OBJECT_SINGLETON"));
Assert.assertFalse(externalValueFields.contains(ObjectCopier.getField(TestObject.class, "TEST_OBJECT_COPIABLE")));

byte[] encoded = encode(externalValueFields, root, "encoded");
Object decoded = ObjectCopier.decode(encoded, loader);
Expand All @@ -187,13 +207,18 @@ public void testIt() {

Map<String, Object> root2 = (Map<String, Object>) ObjectCopier.decode(reencoded, loader);

Assert.assertSame(BaseClass.BASE_SINGLETON, root2.get("singleton1"));
Assert.assertSame(root.get("singleton1"), root2.get("singleton1"));
Assert.assertSame(root.get("singleton1_2"), root2.get("singleton1_2"));
Assert.assertSame(root2.get("singleton1"), root2.get("singleton1_2"));

Assert.assertSame(TestObject.TEST_OBJECT_SINGLETON, root.get("singleton2"));
Assert.assertSame(root.get("singleton2"), root2.get("singleton2"));
Assert.assertSame(root.get("singleton2_2"), root2.get("singleton2_2"));
Assert.assertSame(root2.get("singleton2"), root2.get("singleton2_2"));

Assert.assertNotSame(TestObject.TEST_OBJECT_COPIABLE, root2.get("copiable"));
Assert.assertNotSame(root.get("copiable"), root2.get("copiable"));
}

private static byte[] encode(List<Field> externalValueFields, Object root, String debugLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ protected T handleFailure(DebugContext initialDebug, Throwable cause) {
try (PrintStream ps = new PrintStream(baos)) {
ps.printf("%s: Compilation of %s failed: ", Thread.currentThread(), this);
cause.printStackTrace(ps);
ps.println("Options: " + initialOptions);
printCompilationFailureActionAlternatives(ps, ExceptionAction.Silent, ExceptionAction.Diagnose);
}
TTY.print(baos.toString());
Expand Down Expand Up @@ -322,6 +323,7 @@ protected T handleFailure(DebugContext initialDebug, Throwable cause) {

ps.printf("%s: Compilation of %s failed:%n", Thread.currentThread(), this);
cause.printStackTrace(ps);
ps.println("Options: " + initialOptions);
printCompilationFailureActionAlternatives(ps, ExceptionAction.Silent, ExceptionAction.Print);
if (dumpPath != null) {
ps.println("Retrying compilation of " + this);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2025, 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
Expand Down Expand Up @@ -40,8 +40,6 @@
import java.util.function.Predicate;
import java.util.function.Supplier;

import org.graalvm.collections.EconomicSet;

import jdk.graal.compiler.core.common.Fields;
import jdk.graal.compiler.core.common.type.Stamp;
import jdk.graal.compiler.core.common.util.CompilationAlarm;
Expand All @@ -58,6 +56,7 @@
import jdk.graal.compiler.nodeinfo.NodeInfo;
import jdk.graal.compiler.nodeinfo.NodeSize;
import jdk.graal.compiler.nodeinfo.Verbosity;
import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.spi.Simplifiable;
import jdk.graal.compiler.options.OptionValues;
import jdk.graal.compiler.serviceprovider.GraalServices;
Expand Down Expand Up @@ -564,7 +563,9 @@ void addUsage(Node node) {
if (length == 0) {
extraUsages = new Node[4];
} else if (extraUsagesCount == length) {
Node[] newExtraUsages = new Node[length * 2 + 1];
int growth = length >> 1;
// Grow the usages array by 1.5x
Node[] newExtraUsages = new Node[length + growth];
System.arraycopy(extraUsages, 0, newExtraUsages, 0, length);
extraUsages = newExtraUsages;
}
Expand All @@ -588,13 +589,20 @@ private void movUsageFromEndToExtraUsages(int destExtraIndex) {
Node n = extraUsages[extraUsagesCount];
extraUsages[destExtraIndex] = n;
extraUsages[extraUsagesCount] = null;
if (extraUsagesCount == 0) {
extraUsages = EMPTY_ARRAY;
}
}

private void movUsageFromEndToIndexZero() {
if (extraUsagesCount > 0) {
this.extraUsagesCount--;
usage0 = extraUsages[extraUsagesCount];
extraUsages[extraUsagesCount] = null;
if (extraUsagesCount == 0) {
extraUsages = EMPTY_ARRAY;
} else {
extraUsages[extraUsagesCount] = null;
}
} else if (usage1 != null) {
usage0 = usage1;
usage1 = null;
Expand All @@ -607,7 +615,11 @@ private void movUsageFromEndToIndexOne() {
if (extraUsagesCount > 0) {
this.extraUsagesCount--;
usage1 = extraUsages[extraUsagesCount];
extraUsages[extraUsagesCount] = null;
if (extraUsagesCount == 0) {
extraUsages = EMPTY_ARRAY;
} else {
extraUsages[extraUsagesCount] = null;
}
} else {
assert usage1 != null;
usage1 = null;
Expand Down Expand Up @@ -688,35 +700,6 @@ public int removeUsageNTimes(Node node, int limit) {
return removedUsages;
}

/**
* Removes all nodes in the provided set from {@code this} node's usages. This is significantly
* faster than repeated execution of {@link Node#removeUsage}.
*/
public void removeUsages(EconomicSet<Node> toDelete) {
if (toDelete.size() == 0) {
return;
} else if (toDelete.size() == 1) {
removeUsage(toDelete.iterator().next());
return;
}

// requires iteration from back to front to check nodes prior to being moved to the front
for (int i = extraUsagesCount - 1; i >= 0; i--) {
if (toDelete.contains(extraUsages[i])) {
movUsageFromEndToExtraUsages(i);
incUsageModCount();
}
}
if (usage1 != null && toDelete.contains(usage1)) {
movUsageFromEndToIndexOne();
incUsageModCount();
}
if (usage0 != null && toDelete.contains(usage0)) {
movUsageFromEndToIndexZero();
incUsageModCount();
}
}

/**
* Removes all dead nodes from {@code this} node's usages. This is significantly faster than
* repeated execution of {@link Node#removeUsage}.
Expand Down Expand Up @@ -1728,6 +1711,13 @@ public String toString(Verbosity verbosity) {
}
}

/**
* Note that this is not a stable identity. It's updated when a node is
* {@linkplain #markDeleted() deleted} or potentially when its graph is
* {@linkplain StructuredGraph#maybeCompress compressed}.
*
* @see NodeIdAccessor
*/
@Deprecated
public int getId() {
return id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;

import jdk.graal.compiler.nodes.NodeClassMap;
import org.graalvm.collections.EconomicMap;
import org.graalvm.collections.Equivalence;

Expand Down Expand Up @@ -149,14 +150,9 @@ public static <T> NodeClass<T> get(Class<T> clazz) {

private final int leafId;

@LibGraalSupport.HostedOnly
public NodeClass(Class<T> clazz, NodeClass<? super T> superNodeClass) {
this(clazz, superNodeClass, null, 0);
}

@SuppressWarnings("try")
@LibGraalSupport.HostedOnly
private NodeClass(Class<T> clazz, NodeClass<? super T> superNodeClass, int[] presetIterableIds, int presetIterableId) {
public NodeClass(Class<T> clazz, NodeClass<? super T> superNodeClass) {
super(clazz);
DebugContext debug = DebugContext.forCurrentThread();
this.superNodeClass = superNodeClass;
Expand Down Expand Up @@ -201,10 +197,7 @@ private NodeClass(Class<T> clazz, NodeClass<? super T> superNodeClass, int[] pre
GraalError.guarantee(!allowedUsageTypes.contains(InputType.Memory) || MemoryKillMarker.class.isAssignableFrom(clazz),
"Node of type %s with allowedUsageType of memory must inherit from MemoryKill", clazz);

if (presetIterableIds != null) {
this.iterableIds = presetIterableIds;
this.iterableId = presetIterableId;
} else if (IterableNodeType.class.isAssignableFrom(clazz)) {
if (IterableNodeType.class.isAssignableFrom(clazz)) {
ITERABLE_NODE_TYPES.increment(debug);
this.iterableId = nextIterableId.getAndIncrement();

Expand Down Expand Up @@ -332,6 +325,13 @@ public boolean valueNumberable() {
return canGVN;
}

/**
* Determines if this node type is abstract.
*/
public boolean isAbstract() {
return Modifier.isAbstract(this.getClazz().getModifiers());
}

/**
* Determines if this node type is {@link CanonicalizableMarker canonicalizable}.
*/
Expand Down Expand Up @@ -1488,4 +1488,37 @@ private static void unregisterAtInputsAsUsageHelperMany(Node node, NodeList<Node
}
list.clearWithoutUpdate();
}

/**
* The cached id for a {@link NodeClass} object in a specific {@link NodeClassMap}.
*
* @param map an object whose identity uniquely identifies a {@link NodeClassMap}
*/
record CachedId(Object map, Integer id) {
}

private CachedId cachedId;

/**
* Sets the cache for this object's {@code id} in {@code map}.
*
* @param map an object whose identity uniquely identifies a {@link NodeClassMap}
*/
public void setCachedId(Object map, Integer id) {
cachedId = new CachedId(map, id);
}

/**
* Gets the cache for this object's id in {@code map}.
*
* @param map an object whose identity uniquely identifies a {@link NodeClassMap}
* @return null if no cached id for this object in {@code map} is available
*/
public Integer getCachedId(Object map) {
var c = cachedId;
if (c != null && c.map == map) {
return c.id;
}
return null;
}
}
Loading
Loading