Панама: отступы не предотвращают ложное совместное использование

Я пытаюсь оценить влияние ложного совместного использования на производительность программы.

В этом примере: https://github.com/lexburner/JMH-samples/blob/master/src/main/java/org/openjdk/jmh/samples/JMHSample_22_FalseSharing.java , заполнение строки кэша приводит к повышению производительности за счет порядок величины.

Однако, когда я использую API доступа к внешней памяти проекта panamas, заполнение строки кэша фактически немного ухудшает производительность. Используют ли MemorySegments неявно заполнение? Что еще могло быть причиной такого поведения?

Я уже пробовал запустить тесты на другом оборудовании и отключить гиперпоточность с тем же результатом.

Сведения о тесте:

      @BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(5)
public class JMH_FalseSharing {

    @State(Scope.Group)
    public static class StateBaseline {
        @TearDown(Level.Trial)
        public void tearDown(){
            rs.close();
        }

        ResourceScope rs = ResourceScope.newSharedScope();
        static final VarHandle VH;
        final MemorySegment readSegment = MemorySegment.allocateNative(MemoryLayouts.JAVA_LONG, rs);
        final MemorySegment writeSegment = MemorySegment.allocateNative(MemoryLayouts.JAVA_LONG, rs);

        static{
            VH = MemoryLayouts.JAVA_LONG.varHandle(long.class);
        }
    }
    
    @State(Scope.Group)
    public static class StatePadded {
        @TearDown(Level.Trial)
        public void tearDown(){
            rs.close();
        }
    
        ResourceScope rs = ResourceScope.newSharedScope();
        static final VarHandle VH;
        private static final GroupLayout gl = MemoryLayout.structLayout(
            MemoryLayout.paddingLayout(448L),
            MemoryLayouts.JAVA_LONG.withName("val"),
            MemoryLayout.paddingLayout(448L)
            );
            
        final MemorySegment readSegment  = MemorySegment.allocateNative(gl, rs);
        final MemorySegment writeSegment = MemorySegment.allocateNative(gl, rs);

        static{
            VH = gl.varHandle(long.class, MemoryLayout.PathElement.groupElement("val"));
        }
    }
    
    @Group("baseline")
    @Benchmark
    public void baselineWrite(StateBaseline baselineState){
        StateBaseline.VH.setRelease(baselineState.writeSegment, (long)StateBaseline.VH.getAcquire(baselineState.writeSegment) + 1);
    }

    @Group("baseline")
    @Benchmark
    public void baselineRead(Blackhole blackhole, StateBaseline baselineState){
        blackhole.consume((long)StateBaseline.VH.getAcquire(baselineState.readSegment));
    }
    
    @Group("padded")
    @Benchmark
    public void paddedWrite(StatePadded paddedState){
        StatePadded.VH.setRelease(paddedState.writeSegment, (long)StatePadded.VH.getAcquire(paddedState.writeSegment) + 1);
    }

    @Group("padded")
    @Benchmark
    public void paddedRead(Blackhole blackhole, StatePadded paddedState){
        blackhole.consume((long)StatePadded.VH.getAcquire(paddedState.readSegment));
    }
}

1 ответ

Запоздалый ответ, теперь, когда API FFM совсем другой.


Взгляните на следующий сеанс JShell.

      jshell --add-modules jdk.incubator.foreign
|  Welcome to JShell -- Version 17.0.2
|  For an introduction type: /help intro

jshell> import jdk.incubator.foreign.*;
   ...> ResourceScope rs = ResourceScope.newSharedScope();
   ...>
rs ==> jdk.internal.foreign.SharedScope@238e0d81

jshell> final MemorySegment readSegment = MemorySegment.allocateNative(MemoryLayouts.JAVA_LONG, rs);
   ...> final MemorySegment writeSegment = MemorySegment.allocateNative(MemoryLayouts.JAVA_LONG, rs);
   ...>
readSegment ==> MemorySegment{ id=0x28758e86 limit: 8 }
writeSegment ==> MemorySegment{ id=0x286203e6 limit: 8 }

jshell> writeSegment.address().toRawLongValue() - readSegment.address().toRawLongValue()
$5 ==> 24928

jshell>

Два, которые я выделил, например, как у вас дела вStateBaseline, находятся далеко друг от друга. Так что нет ложного обмена.

Сравните это сreadOnlyиwriteOnlyполя вJMHSample_22_FalseSharing.StateBaseline. Они находятся рядом друг с другом в одном объекте, поэтому различные приемы их разделения повышают производительность, предотвращая ложное совместное использование.


Теперь о том, почему эти дваMemorySegmentsтак далеко, этот ответ может быть уместен.

Однако они могут быть близки друг к другу. На самом деле, когда я делаю
try (ResourceScope rs = ResourceScope.newSharedScope()) {или
в Java 21:try (Arena arena = Arena.ofConfined()) {,
они обычно находятся близко, а иногда и рядом друг с другом.


Чтобы проверить ложный обмен сMemorySegmentвы можете попробовать следующее (в Java 21):

      MemorySegment longArray = Arena.global().allocateArray(ValueLayout.JAVA_LONG, 0, 0);
MemorySegment readSegment = longArray.asSlice(0, ValueLayout.JAVA_LONG);
MemorySegment writeSegment = longArray.asSlice(ValueLayout.JAVA_LONG.byteSize(), ValueLayout.JAVA_LONG);

MemorySegment structArray = Arena.global().allocateArray(gl, 2);
Другие вопросы по тегам