Skip to content
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

Move MemoryAddress::copy #169

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -115,33 +115,6 @@
@Override
int hashCode();

/**
* Perform bulk copy from source address to target address. More specifically, the bytes at addresses {@code src}
* through {@code src.addOffset(bytes - 1)} are copied into addresses {@code dst} through {@code dst.addOffset(bytes - 1)}.
* If the source and address ranges overlap, then the copying is performed as if the bytes at addresses {@code src}
* through {@code src.addOffset(bytes - 1)} were first copied into a temporary segment with size {@code bytes},
* and then the contents of the temporary segment were copied into the bytes at addresses {@code dst} through
* {@code dst.addOffset(bytes - 1)}.
* <p>
* The result of a bulk copy is unspecified if, in the uncommon case, the source and target address ranges do not
* overlap, but refer to overlapping regions of the same backing storage using different addresses. For example,
* this may occur if the same file is {@link MemorySegment#mapFromPath mapped} to two segments.
*
* @param src the source address.
* @param dst the target address.
* @param bytes the number of bytes to be copied.
* @throws IndexOutOfBoundsException if {@code bytes < 0}, or if it is greater than the size of the segments
* associated with either {@code src} or {@code dst}.
* @throws IllegalStateException if either the source address or the target address belong to memory segments
* which have been already closed, or if access occurs from a thread other than the thread owning either segment.
* @throws UnsupportedOperationException if either {@code src} or {@code dst} do not feature required access modes;
* more specifically, {@code src} should be associated with a segment with {@link MemorySegment#READ} access mode,
* while {@code dst} should be associated with a segment with {@link MemorySegment#WRITE} access mode.
*/
static void copy(MemoryAddress src, MemoryAddress dst, long bytes) {
MemoryAddressImpl.copy((MemoryAddressImpl)src, (MemoryAddressImpl)dst, bytes);
}

/**
* The <em>unchecked</em> memory address instance modelling the {@code NULL} address. This address is <em>not</em> backed by
* a memory segment and hence it cannot be dereferenced.
@@ -31,6 +31,7 @@
import jdk.internal.foreign.AbstractMemorySegmentImpl;
import jdk.internal.foreign.HeapMemorySegmentImpl;
import jdk.internal.foreign.MappedMemorySegmentImpl;
import jdk.internal.foreign.MemoryAddressImpl;
import jdk.internal.foreign.NativeMemorySegmentImpl;
import jdk.internal.foreign.Utils;

@@ -308,6 +309,29 @@
*/
MemorySegment fill(byte value);

/**
* Perform bulk copy from given source segment to this segment. More specifically, the bytes at

This comment has been minimized.

@PaulSandoz

PaulSandoz May 15, 2020
Member

"Performs a bulk copy..."

* offset {@code 0} through {@code src.byteSize() - 1} in the source segment are copied into this segment
* at offset {@code 0} through {@code src.byteSize() - 1}.
* If the source segment overlaps with this segment, then the copying is performed as if the bytes at
* offset {@code 0} through {@code src.byteSize() - 1} in the source segment were first copied into a
* temporary segment with size {@code bytes}, and then the contents of the temporary segment were copied into
* this segment at offset {@code 0} through {@code src.byteSize() - 1}.
* <p>
* The result of a bulk copy is unspecified if, in the uncommon case, the source segment does not overlap with
* this segment, but it instead refers to an overlapping regions of the same backing storage using different addresses.

This comment has been minimized.

@PaulSandoz

PaulSandoz May 15, 2020
Member

"The result of a bulk copy is unspecified if, in the uncommon case, the source segment and this segment do not overlap, but refer to overlapping regions of the same backing storage using different addresses"

* For example, this may occur if the same file is {@link MemorySegment#mapFromPath mapped} to two segments.
*
* @param src the source segment.
* @throws IndexOutOfBoundsException if {src.byteSize() > this.byteSize()}.
* @throws IllegalStateException if either the source segment or this segment have been already closed,
* or if access occurs from a thread other than the thread owning either segment.
* @throws UnsupportedOperationException if either the source segment or this segment do not feature required access modes;
* more specifically, {@code src} should feature at least the {@link MemorySegment#READ} access mode,
* while this segment should feature at least the {@link MemorySegment#WRITE} access mode.
*/
void copyFrom(MemorySegment src);

/**
* Wraps this segment in a {@link ByteBuffer}. Some of the properties of the returned buffer are linked to
* the properties of this segment. For instance, if this segment is <em>immutable</em>
@@ -123,6 +123,17 @@ public final MemorySegment fill(byte value){
return this;
}

public void copyFrom(MemorySegment src) {
long size = src.byteSize();
((AbstractMemorySegmentImpl)src).checkRange(0, size, true);
checkRange(0, size, false);
long offsetSrc = ((AbstractMemorySegmentImpl) src).min();
long offsetDst = min();
Object baseSrc = ((AbstractMemorySegmentImpl) src).base();
Object baseDst = base();
UNSAFE.copyMemory(baseSrc, offsetSrc, baseDst, offsetDst, size);
}

@Override
@ForceInline
public final MemoryAddress baseAddress() {
@@ -228,7 +239,7 @@ final AbstractMemorySegmentImpl acquire() {
checkIntSize("byte[]");
byte[] arr = new byte[(int)length];
MemorySegment arrSegment = MemorySegment.ofArray(arr);
MemoryAddress.copy(baseAddress(), arrSegment.baseAddress(), length);
arrSegment.copyFrom(this);
return arr;
}

@@ -54,17 +54,6 @@ public MemoryAddressImpl(AbstractMemorySegmentImpl segment, long offset) {
this.offset = offset;
}

public static void copy(MemoryAddressImpl src, MemoryAddressImpl dst, long size) {
src.checkAccess(0, size, true);
dst.checkAccess(0, size, false);
//check disjoint
long offsetSrc = src.unsafeGetOffset();
long offsetDst = dst.unsafeGetOffset();
Object baseSrc = src.unsafeGetBase();
Object baseDst = dst.unsafeGetBase();
UNSAFE.copyMemory(baseSrc, offsetSrc, baseDst, offsetDst, size);
}

// MemoryAddress methods

@Override
@@ -429,7 +429,7 @@ public void testCopyHeapToNative(Consumer<MemoryAddress> checker, Consumer<Memor
try (MemorySegment nativeArray = MemorySegment.allocateNative(bytes);
MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) {
initializer.accept(heapArray.baseAddress());
MemoryAddress.copy(heapArray.baseAddress(), nativeArray.baseAddress(), bytes);
nativeArray.copyFrom(heapArray);
checker.accept(nativeArray.baseAddress());
}
}
@@ -441,7 +441,7 @@ public void testCopyNativeToHeap(Consumer<MemoryAddress> checker, Consumer<Memor
try (MemorySegment nativeArray = MemorySegment.allocateNative(seq);
MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) {
initializer.accept(nativeArray.baseAddress());
MemoryAddress.copy(nativeArray.baseAddress(), heapArray.baseAddress(), bytes);
heapArray.copyFrom(nativeArray);
checker.accept(heapArray.baseAddress());
}
}
@@ -57,7 +57,7 @@ public void testCopy(SegmentSlice s1, SegmentSlice s2) {
BYTE_HANDLE.set(addr1.addOffset(i), (byte) i);
}
//perform copy
MemoryAddress.copy(addr1, addr2, size);
s2.segment.copyFrom(s1.segment.asSlice(0, size));
//check that copy actually worked
for (int i = 0 ; i < size ; i++) {
assertEquals((byte)i, BYTE_HANDLE.get(addr2.addOffset(i)));
@@ -321,6 +321,7 @@ MemoryLayout make(long size) {
final static List<String> CONFINED_NAMES = List.of(
"close",
"fill",
"copyFrom",
"toByteArray",
"withOwnerThread"
);
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2020, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/

package org.openjdk.bench.jdk.incubator.foreign;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import sun.misc.Unsafe;

import jdk.incubator.foreign.MemorySegment;
import java.util.concurrent.TimeUnit;

import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT;

@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@State(org.openjdk.jmh.annotations.Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(3)
public class BulkOps {

static final Unsafe unsafe = Utils.unsafe;

static final int ELEM_SIZE = 1_000_000;
static final int CARRIER_SIZE = (int)JAVA_INT.byteSize();
static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE;

static final long unsafe_addr = unsafe.allocateMemory(ALLOC_SIZE);
static final MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE);

static final int[] bytes = new int[ELEM_SIZE];
static final MemorySegment bytesSegment = MemorySegment.ofArray(bytes);
static final int UNSAFE_INT_OFFSET = unsafe.arrayBaseOffset(int[].class);

static {
for (int i = 0 ; i < bytes.length ; i++) {
bytes[i] = i;
}
}

@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void unsafe_fill() {
unsafe.setMemory(unsafe_addr, ALLOC_SIZE, (byte)0);

This comment has been minimized.

@PaulSandoz

PaulSandoz May 15, 2020
Member

Out of an abundance of caution you might wanna choose a non-zero value just in case the JIT knows it's zeroing already zeroed memory (i suspect it does not).

}

@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void segment_fill() {
segment.fill((byte)0);
}

@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void unsafe_copy() {
unsafe.copyMemory(bytes, UNSAFE_INT_OFFSET, null, unsafe_addr, ALLOC_SIZE);
}

@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void segment_copy() {
segment.copyFrom(bytesSegment);
}
}
ProTip! Use n and p to navigate between commits in a pull request.