JDK-8235844 : Non-constant memory segments are never treated as loop invariants
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 14
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2019-12-12
  • Updated: 2020-06-11
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Relates :  
Relates :  
Description
Consider the following class:

import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;

public class PanamaPoint implements AutoCloseable {
    private static final SequenceLayout LAYOUT = MemoryLayout.ofSequence(MemoryLayout.ofStruct(
            MemoryLayouts.JAVA_INT.withOrder(ByteOrder.nativeOrder()).withName("x"),
            MemoryLayouts.JAVA_INT.withOrder(ByteOrder.nativeOrder()).withName("y")));
    private static final VarHandle VH_x = LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("x"));
    private static final VarHandle VH_y = LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("y"));

    private final MemorySegment segment;
    private final MemoryAddress base;
    public PanamaPoint(int size) {
        this.segment = MemorySegment.allocateNative(LAYOUT.elementLayout().byteSize() * size);
        this.base = segment.baseAddress();
    }
    public void setX(int x, int pos) {
        VH_x.set(base, (long)pos, x);
    }
    public int getX(int pos) {
        return (int) VH_x.get(base, (long)pos);
    }
    public void setY(int y, int pos) {
        VH_y.set(base, (long)pos, y);
    }
    public int getY(int pos) {
        return (int) VH_y.get(base, (long)pos);
    }
    @Override
    public void close() {
        segment.close();
    }
}

And the following benchmark:

@Benchmark
    public int panama_get() throws Throwable {
        int res = 0;
        for (int i = 0 ; i < SIZE ; i++) {
            res+= panamaPoint.getX(i);
        }
        return res;
    }

Despite the segment being accessed stays the same across the entire loop, a quick look at the generated code reveals that no hoisting is taking place - that is, upon every access we pay full cost for ownership checks, liveness check, bounds check, alignment check, read only check.
Comments
The benchmark does not demonstrate issues with non-constant segments. The difference in scores is due to cached PanamaPoint.base vs MemorySegment.baseAddress(). @Benchmark public int panama_get_hoisted_manual() throws Throwable { int res = 0; PanamaPoint point = panamaPoint; for (int i = 0 ; i < SIZE ; i++) { res += (int)PanamaPoint.VH_x.get(point.base, (long)i); } return res; } @Benchmark public int panama_get_hoisted_manual1() throws Throwable { int res = 0; PanamaPoint point = panamaPoint; for (int i = 0 ; i < SIZE ; i++) { res += (int)PanamaPoint.VH_x.get(point.segment.baseAddress(), (long)i); } return res; } PanamaPointer.panama_get_hoisted_manual avgt 10 0.060 �� 0.019 ms/op PanamaPointer.panama_get_hoisted_manual1 avgt 10 0.029 �� 0.001 ms/op MemorySegment.baseAddress() constructs fresh address with 0 offset and it helps to eliminate a bunch of checks: (1) offset fits into int; (2) offset computation for indexed access doesn���t overflow.
11-06-2020

Manually hoisting the loop as follows: @Benchmark public int panama_get_hoisted_manual() throws Throwable { int res = 0; MemorySegment segment = panamaPoint.segment; for (int i = 0 ; i < SIZE ; i++) { res += (int)PanamaPoint.VH_x.get(segment.baseAddress(), (long)i); } return res; } Brings significant improvements as most checks (except liveness and bounds) are correctly factored out of the loop.
12-12-2019