UnsafeBuffer implementation, based on sun.misc.Unsafe

Motivation:
When Unsafe is available, we can supposedly do certain things faster than when it is not.

Modification:
Add a Buffer implementation that take advantage of sun.misc.Unsafe.
It has not yet been verified if this is faster in any way than, say the ByteBuffer implementation or the MemorySegment implementation.

Result:
Another Buffer implementation that can be used when Unsafe is available.
This commit is contained in:
Chris Vest 2021-03-19 17:22:28 +01:00
parent e7f7335804
commit c73dd07384
12 changed files with 1818 additions and 21 deletions

View File

@ -78,6 +78,8 @@
-server -server
-dsa -da -ea:io.netty... -dsa -da -ea:io.netty...
-XX:+HeapDumpOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError
-Dio.netty.tryReflectionSetAccessible=true
--add-opens java.base/java.nio=io.netty.common
</argLine.common> </argLine.common>
<!-- <argLine.printGC>-XX:+PrintGCDetails</argLine.printGC>--> <!-- <argLine.printGC>-XX:+PrintGCDetails</argLine.printGC>-->
<argLine.printGC/> <argLine.printGC/>

View File

@ -26,5 +26,6 @@ public interface MemoryManager {
Drop<Buffer> drop(); Drop<Buffer> drop();
Object unwrapRecoverableMemory(Buffer buf); Object unwrapRecoverableMemory(Buffer buf);
int capacityOfRecoverableMemory(Object memory); int capacityOfRecoverableMemory(Object memory);
// todo should recoverMemory re-attach a cleaner?
Buffer recoverMemory(AllocatorControl allocatorControl, Object recoverableMemory, Drop<Buffer> drop); Buffer recoverMemory(AllocatorControl allocatorControl, Object recoverableMemory, Drop<Buffer> drop);
} }

View File

@ -34,6 +34,9 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.ReadOnlyBufferException; import java.nio.ReadOnlyBufferException;
import static io.netty.buffer.api.internal.Statics.bufferIsClosed;
import static io.netty.buffer.api.internal.Statics.bufferIsReadOnly;
class NioBuffer extends RcSupport<Buffer, NioBuffer> implements Buffer, ReadableComponent, WritableComponent { class NioBuffer extends RcSupport<Buffer, NioBuffer> implements Buffer, ReadableComponent, WritableComponent {
private static final ByteBuffer CLOSED_BUFFER = ByteBuffer.allocate(0); private static final ByteBuffer CLOSED_BUFFER = ByteBuffer.allocate(0);
@ -417,9 +420,9 @@ class NioBuffer extends RcSupport<Buffer, NioBuffer> implements Buffer, Readable
} }
var drop = (ArcDrop<NioBuffer>) unsafeGetDrop(); var drop = (ArcDrop<NioBuffer>) unsafeGetDrop();
unsafeSetDrop(new ArcDrop<>(drop)); unsafeSetDrop(new ArcDrop<>(drop));
var bifurcatedSeg = rmem.slice(0, woff); var bifurcatedBuffer = rmem.slice(0, woff);
// TODO maybe incrementing the existing ArcDrop is enough; maybe we don't need to wrap it in another ArcDrop. // TODO maybe incrementing the existing ArcDrop is enough; maybe we don't need to wrap it in another ArcDrop.
var bifurcatedBuf = new NioBuffer(base, bifurcatedSeg, control, new ArcDrop<>(drop.increment())); var bifurcatedBuf = new NioBuffer(base, bifurcatedBuffer, control, new ArcDrop<>(drop.increment()));
bifurcatedBuf.woff = woff; bifurcatedBuf.woff = woff;
bifurcatedBuf.roff = roff; bifurcatedBuf.roff = roff;
bifurcatedBuf.order(order()); bifurcatedBuf.order(order());
@ -893,7 +896,7 @@ class NioBuffer extends RcSupport<Buffer, NioBuffer> implements Buffer, Readable
wmem.putInt(woff, value); wmem.putInt(woff, value);
return this; return this;
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
throw checkWriteState(e, NioBuffer.this.woff); throw checkWriteState(e, this.woff);
} catch (ReadOnlyBufferException e) { } catch (ReadOnlyBufferException e) {
throw bufferIsReadOnly(); throw bufferIsReadOnly();
} }
@ -918,7 +921,7 @@ class NioBuffer extends RcSupport<Buffer, NioBuffer> implements Buffer, Readable
wmem.putInt(woff, (int) (value & 0xFFFFFFFFL)); wmem.putInt(woff, (int) (value & 0xFFFFFFFFL));
return this; return this;
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
throw checkWriteState(e, NioBuffer.this.woff); throw checkWriteState(e, this.woff);
} catch (ReadOnlyBufferException e) { } catch (ReadOnlyBufferException e) {
throw bufferIsReadOnly(); throw bufferIsReadOnly();
} }
@ -1135,14 +1138,6 @@ class NioBuffer extends RcSupport<Buffer, NioBuffer> implements Buffer, Readable
return outOfBounds(index); return outOfBounds(index);
} }
private static IllegalStateException bufferIsClosed() {
return new IllegalStateException("This buffer is closed.");
}
private static IllegalStateException bufferIsReadOnly() {
return new IllegalStateException("This buffer is read-only.");
}
private IndexOutOfBoundsException outOfBounds(int index) { private IndexOutOfBoundsException outOfBounds(int index) {
return new IndexOutOfBoundsException( return new IndexOutOfBoundsException(
"Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " + "Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " +

View File

@ -68,4 +68,12 @@ public interface Statics {
dest.order(prevOrder); dest.order(prevOrder);
} }
} }
static IllegalStateException bufferIsClosed() {
return new IllegalStateException("This buffer is closed.");
}
static IllegalStateException bufferIsReadOnly() {
return new IllegalStateException("This buffer is read-only.");
}
} }

View File

@ -38,6 +38,8 @@ import jdk.incubator.foreign.ResourceScope;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import static io.netty.buffer.api.internal.Statics.bufferIsClosed;
import static io.netty.buffer.api.internal.Statics.bufferIsReadOnly;
import static jdk.incubator.foreign.MemoryAccess.getByteAtOffset; import static jdk.incubator.foreign.MemoryAccess.getByteAtOffset;
import static jdk.incubator.foreign.MemoryAccess.getCharAtOffset; import static jdk.incubator.foreign.MemoryAccess.getCharAtOffset;
import static jdk.incubator.foreign.MemoryAccess.getDoubleAtOffset; import static jdk.incubator.foreign.MemoryAccess.getDoubleAtOffset;
@ -1168,14 +1170,6 @@ class MemSegBuffer extends RcSupport<Buffer, MemSegBuffer> implements Buffer, Re
return outOfBounds(index); return outOfBounds(index);
} }
private static IllegalStateException bufferIsClosed() {
return new IllegalStateException("This buffer is closed.");
}
private static IllegalStateException bufferIsReadOnly() {
return new IllegalStateException("This buffer is read-only.");
}
private IndexOutOfBoundsException outOfBounds(int index) { private IndexOutOfBoundsException outOfBounds(int index) {
return new IndexOutOfBoundsException( return new IndexOutOfBoundsException(
"Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " + "Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " +

View File

@ -0,0 +1,55 @@
/*
* Copyright 2021 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.buffer.api.unsafe;
import io.netty.buffer.api.Buffer;
import io.netty.buffer.api.Drop;
import io.netty.util.internal.PlatformDependent;
import java.lang.ref.Cleaner;
public class CleanerDrop implements Drop<Buffer> {
private final Drop<Buffer> drop;
public CleanerDrop(UnsafeMemory memory, Drop<Buffer> drop, Cleaner cleaner) {
this.drop = drop;
long address = memory.address;
cleaner.register(memory, new FreeAddress(address));
}
@Override
public void drop(Buffer obj) {
drop.drop(obj);
}
@Override
public void attach(Buffer obj) {
drop.attach(obj);
}
private static class FreeAddress implements Runnable {
private final long address;
FreeAddress(long address) {
this.address = address;
}
@Override
public void run() {
PlatformDependent.freeMemory(address);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
/*
* Copyright 2021 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.buffer.api.unsafe;
class UnsafeMemory {
final Object base;
final long address;
final int size;
UnsafeMemory(Object base, long address, int size) {
this.base = base;
this.address = address;
this.size = size;
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright 2021 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.buffer.api.unsafe;
import io.netty.buffer.api.AllocatorControl;
import io.netty.buffer.api.Buffer;
import io.netty.buffer.api.Drop;
import io.netty.buffer.api.MemoryManager;
import io.netty.buffer.api.internal.Statics;
import io.netty.util.internal.PlatformDependent;
import java.lang.ref.Cleaner;
import static io.netty.buffer.api.internal.Statics.convert;
public class UnsafeMemoryManager implements MemoryManager {
private final boolean offheap;
public UnsafeMemoryManager(boolean offheap) {
this.offheap = offheap;
}
@Override
public boolean isNative() {
return offheap;
}
@Override
public Buffer allocateConfined(AllocatorControl allocatorControl, long size, Drop<Buffer> drop, Cleaner cleaner) {
return allocateShared(allocatorControl, size, drop, cleaner);
}
@Override
public Buffer allocateShared(AllocatorControl allocatorControl, long size, Drop<Buffer> drop, Cleaner cleaner) {
final Object base;
final long address;
final UnsafeMemory memory;
final int size32 = Math.toIntExact(size);
if (cleaner == null) {
cleaner = Statics.CLEANER;
}
if (offheap) {
base = null;
address = PlatformDependent.allocateMemory(size);
PlatformDependent.setMemory(address, size, (byte) 0);
memory = new UnsafeMemory(base, address, size32);
drop = new CleanerDrop(memory, drop, cleaner);
} else {
base = new byte[size32];
address = PlatformDependent.byteArrayBaseOffset();
memory = new UnsafeMemory(base, address, size32);
}
return new UnsafeBuffer(memory, 0, size32, allocatorControl, convert(drop));
}
@Override
public Drop<Buffer> drop() {
// We cannot reliably drop unsafe memory. We have to rely on the cleaner to do that.
return Statics.NO_OP_DROP;
}
@Override
public Object unwrapRecoverableMemory(Buffer buf) {
return ((UnsafeBuffer) buf).recover();
}
@Override
public int capacityOfRecoverableMemory(Object memory) {
return ((UnsafeMemory) memory).size;
}
@Override
public Buffer recoverMemory(AllocatorControl allocatorControl, Object recoverableMemory, Drop<Buffer> drop) {
UnsafeMemory memory = (UnsafeMemory) recoverableMemory;
return new UnsafeBuffer(memory, 0, memory.size, allocatorControl, convert(drop));
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2021 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.buffer.api.unsafe;
import io.netty.buffer.api.MemoryManager;
import io.netty.buffer.api.MemoryManagers;
import io.netty.util.internal.PlatformDependent;
public class UnsafeMemoryManagers implements MemoryManagers {
public UnsafeMemoryManagers() {
if (!PlatformDependent.hasUnsafe()) {
throw new UnsupportedOperationException("Unsafe is not available.");
}
if (!PlatformDependent.hasDirectBufferNoCleanerConstructor()) {
throw new UnsupportedOperationException("DirectByteBuffer internal constructor is not available.");
}
}
@Override
public MemoryManager getHeapMemoryManager() {
return new UnsafeMemoryManager(false);
}
@Override
public MemoryManager getNativeMemoryManager() {
return new UnsafeMemoryManager(true);
}
@Override
public String toString() {
return "US";
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2021 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* A {@link io.netty.buffer.api.Buffer} implementation that is based on {@code sun.misc.Unsafe}.
*/
package io.netty.buffer.api.unsafe;

View File

@ -32,5 +32,6 @@ module netty.incubator.buffer {
provides io.netty.buffer.api.MemoryManagers with provides io.netty.buffer.api.MemoryManagers with
io.netty.buffer.api.memseg.SegmentMemoryManagers, io.netty.buffer.api.memseg.SegmentMemoryManagers,
io.netty.buffer.api.bytebuffer.ByteBufferMemoryManagers; io.netty.buffer.api.bytebuffer.ByteBufferMemoryManagers,
io.netty.buffer.api.unsafe.UnsafeMemoryManagers;
} }