Effizient ein großes Array mit Methodenhandeln bauen
Posted: 06 Apr 2025, 23:04
Ich muss einen MethodHandle mit Signature (Internalcontext)-> Objekt [] Aus einer Liste mit der Signatur (Internalcontext)-> Objekt .
Wenn die Liste jedoch größer ist, fehl
, das im Grunde genommen ein Array explizit konstruiert und dann jedes Element mit ordargumenten kombiniert
Dies funktioniert, aber der resultierende MethodeHandle erzeugt StackoverFlowerror Bei der Erstellung großer Arrays (sayer 100K -Elemente). Aus den Dokumenten auf Ordnungen ist dies sinnvoll, da jeder Aufruf eine Struktur des Schwanzaufrufs einstellt. könnte dazu beitragen, die Rekursion zu verringern? Oder sollte ich mich nur auf die VM verlassen, um dies durch das Inline -Inline zu tun? /> Wäre es besser, kleine Arrays zu konstruieren und sie mit System zusammenzusetzen.>
Code: Select all
static MethodHandle makeArrayUsingCollector(List elements) {
// (Object[]) -> Object[]
var handle = MethodHandles.identity(Object[].class);
// (Object,Object...Object) -> Object[]
handle = handle.asCollector(Object[].class, elements.size());
// (InternalContext,InternalContext..InternalContext) -> Object[]
handle = MethodHandles.filterArguments(handle, 0, elements.toArray(new MethodHandle[0]));
// (InternalContext) -> Object[]
handle = MethodHandles.permuteArguments(
handle, methodType(Object[].class, InternalContext.class), new int[elements.size()]);
return handle;
}
Code: Select all
private static final int MAX_ARITY = 255;
static MethodHandle buildLargeArrayNaive(List elementFactories) {
if (elementFactories.size() < MAX_ARITY) {
return makeArrayUsingCollector(elementFactories);
}
var setter = MethodHandles.arrayElementSetter(Object[].class);
// (Object[], InternalContext) -> void
MethodHandle handle = null;
for (int i = 0; i < elementFactories.size(); i++) {
// (Object[], InternalContext) -> void
var setElement =
MethodHandles.filterArguments(
MethodHandles.insertArguments(setter, 1, i), 1, elementFactories.get(i));
if (handle == null) {
handle = setElement;
} else {
handle = MethodHandles.foldArguments(setElement, handle);
}
}
// (Object[], InternalContext) -> Object[]
handle =
MethodHandles.foldArguments(
MethodHandles.dropArguments(
MethodHandles.identity(Object[].class), 1, InternalContext.class),
handle);
return MethodHandles.foldArguments(
handle,
MethodHandles.insertArguments(
MethodHandles.arrayConstructor(Object[].class), 0, elementFactories.size()));
}
Dies funktioniert, aber der resultierende MethodeHandle erzeugt StackoverFlowerror Bei der Erstellung großer Arrays (sayer 100K -Elemente). Aus den Dokumenten auf Ordnungen ist dies sinnvoll, da jeder Aufruf eine Struktur des Schwanzaufrufs einstellt.
Code: Select all
static makeArray(InternalContext ctx) {
var a = new Object[
Ich habe festgestellt, dass ich dies mit Rekursion lösen kann < /p>
static MethodHandle buildLargeArrayRecursive(List elementFactories) {
// (Object[], InternalContext) -> void
var handle = doBuildLargeArrayRecursive(0, elementFactories);
// (Object[], InternalContext) -> Object[]
handle =
MethodHandles.foldArguments(
MethodHandles.dropArguments(
MethodHandles.identity(Object[].class), 1, InternalContext.class),
handle);
;
return MethodHandles.foldArguments(
handle,
MethodHandles.insertArguments(
MethodHandles.arrayConstructor(Object[].class), 0, elementFactories.size()));
}
static final MethodHandle ARRAY_SETTER = MethodHandles.arrayElementSetter(Object[].class);
static MethodHandle doBuildLargeArrayRecursive(int offset, List elementFactories) {
int size = elementFactories.size();
if (size == 0) {
return MethodHandles.empty(methodType(void.class, Object[].class, InternalContext.class));
}
if (size == 1) {
return MethodHandles.filterArguments(
MethodHandles.insertArguments(ARRAY_SETTER, 1, offset), 1, elementFactories.get(0));
}
int half = size / 2;
var left = elementFactories.subList(0, half);
var right = elementFactories.subList(half, size);
return MethodHandles.foldArguments(
doBuildLargeArrayRecursive(offset + half, right), doBuildLargeArrayRecursive(offset, left));
}
< /code>
, was auf mindestens 100.000 Elemente zu skalieren scheint. Aber dies ist eindeutig, dass dies O (logn) Stapelraum verbraucht. Das ist wahrscheinlich in Ordnung, aber dies sind auch viele Methodenaufrufe ... < /p>
Ein paar Dinge sind aufgetreten, während ich darauf starrte: < /p>
Sollte ich Helfer-Bulk-Set-Methoden erstellen, um mehr Basisfälle zu behandeln? z.B. static void set4 (Objekt [], int offset, Objekt O1 ... Objekt O3) {...}