Relaxed memory access constraint of ReferenceCounted.refCnt() for potentially better performance / More precise reference counting for MessageBuf

This commit is contained in:
Trustin Lee 2013-03-08 10:32:20 +09:00
parent 6ac9b17ddd
commit 12f1d96914
5 changed files with 129 additions and 46 deletions

View File

@ -16,8 +16,11 @@
package io.netty.buffer; package io.netty.buffer;
import io.netty.util.internal.PlatformDependent;
import java.util.AbstractQueue; import java.util.AbstractQueue;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/** /**
* Abstract base class for {@link MessageBuf} implementations. * Abstract base class for {@link MessageBuf} implementations.
@ -25,8 +28,27 @@ import java.util.Collection;
*/ */
public abstract class AbstractMessageBuf<T> extends AbstractQueue<T> implements MessageBuf<T> { public abstract class AbstractMessageBuf<T> extends AbstractQueue<T> implements MessageBuf<T> {
@SuppressWarnings("rawtypes")
private static final AtomicIntegerFieldUpdater<AbstractMessageBuf> refCntUpdater =
AtomicIntegerFieldUpdater.newUpdater(AbstractMessageBuf.class, "refCnt");
private static final long REFCNT_FIELD_OFFSET;
static {
long refCntFieldOffset = -1;
try {
if (PlatformDependent.hasUnsafe()) {
refCntFieldOffset = PlatformDependent.objectFieldOffset(AbstractMessageBuf.class.getDeclaredField("refCnt"));
}
} catch (Throwable ignored) { }
REFCNT_FIELD_OFFSET = refCntFieldOffset;
}
private final int maxCapacity; private final int maxCapacity;
private int refCnt = 1;
@SuppressWarnings("FieldMayBeFinal")
private volatile int refCnt = 1;
protected AbstractMessageBuf(int maxCapacity) { protected AbstractMessageBuf(int maxCapacity) {
if (maxCapacity < 0) { if (maxCapacity < 0) {
@ -42,58 +64,69 @@ public abstract class AbstractMessageBuf<T> extends AbstractQueue<T> implements
@Override @Override
public final int refCnt() { public final int refCnt() {
if (REFCNT_FIELD_OFFSET >= 0) {
// Try to do non-volatile read for performance.
return PlatformDependent.getInt(this, REFCNT_FIELD_OFFSET);
} else {
return refCnt; return refCnt;
} }
}
@Override @Override
public final MessageBuf<T> retain() { public MessageBuf<T> retain() {
for (;;) {
int refCnt = this.refCnt; int refCnt = this.refCnt;
if (refCnt <= 0) { if (refCnt == 0) {
throw new IllegalBufferAccessException(); throw new IllegalBufferAccessException();
} }
if (refCnt == Integer.MAX_VALUE) { if (refCnt == Integer.MAX_VALUE) {
throw new IllegalBufferAccessException("refCnt overflow"); throw new IllegalBufferAccessException("refCnt overflow");
} }
if (refCntUpdater.compareAndSet(this, refCnt, refCnt + 1)) {
this.refCnt = refCnt + 1; break;
}
}
return this; return this;
} }
@Override @Override
public final MessageBuf<T> retain(int increment) { public MessageBuf<T> retain(int increment) {
if (increment <= 0) { if (increment <= 0) {
throw new IllegalArgumentException("increment: " + increment + " (expected: > 0)"); throw new IllegalArgumentException("increment: " + increment + " (expected: > 0)");
} }
for (;;) {
int refCnt = this.refCnt; int refCnt = this.refCnt;
if (refCnt <= 0) { if (refCnt == 0) {
throw new IllegalBufferAccessException(); throw new IllegalBufferAccessException();
} }
if (refCnt > Integer.MAX_VALUE - increment) { if (refCnt > Integer.MAX_VALUE - increment) {
throw new IllegalBufferAccessException("refCnt overflow"); throw new IllegalBufferAccessException("refCnt overflow");
} }
if (refCntUpdater.compareAndSet(this, refCnt, refCnt + increment)) {
this.refCnt = refCnt + increment; break;
}
}
return this; return this;
} }
@Override @Override
public final boolean release() { public final boolean release() {
for (;;) {
int refCnt = this.refCnt; int refCnt = this.refCnt;
if (refCnt <= 0) { if (refCnt == 0) {
throw new IllegalBufferAccessException(); throw new IllegalBufferAccessException();
} }
this.refCnt = refCnt --; if (refCntUpdater.compareAndSet(this, refCnt, refCnt - 1)) {
if (refCnt == 0) { if (refCnt == 1) {
deallocate(); deallocate();
return true; return true;
} }
return false; return false;
} }
}
}
@Override @Override
public final boolean release(int decrement) { public final boolean release(int decrement) {
@ -101,19 +134,21 @@ public abstract class AbstractMessageBuf<T> extends AbstractQueue<T> implements
throw new IllegalArgumentException("decrement: " + decrement + " (expected: > 0)"); throw new IllegalArgumentException("decrement: " + decrement + " (expected: > 0)");
} }
for (;;) {
int refCnt = this.refCnt; int refCnt = this.refCnt;
if (refCnt < decrement) { if (refCnt < decrement) {
throw new IllegalBufferAccessException(); throw new IllegalBufferAccessException();
} }
this.refCnt = refCnt -= decrement; if (refCntUpdater.compareAndSet(this, refCnt, refCnt - decrement)) {
if (refCnt == 0) { if (refCnt == decrement) {
deallocate(); deallocate();
return true; return true;
} }
return false; return false;
} }
}
}
protected abstract void deallocate(); protected abstract void deallocate();

View File

@ -15,6 +15,8 @@
*/ */
package io.netty.buffer; package io.netty.buffer;
import io.netty.util.internal.PlatformDependent;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public abstract class AbstractReferenceCounted implements ReferenceCounted { public abstract class AbstractReferenceCounted implements ReferenceCounted {
@ -22,13 +24,31 @@ public abstract class AbstractReferenceCounted implements ReferenceCounted {
private static final AtomicIntegerFieldUpdater<AbstractReferenceCounted> refCntUpdater = private static final AtomicIntegerFieldUpdater<AbstractReferenceCounted> refCntUpdater =
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCounted.class, "refCnt"); AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCounted.class, "refCnt");
private static final long REFCNT_FIELD_OFFSET;
static {
long refCntFieldOffset = -1;
try {
if (PlatformDependent.hasUnsafe()) {
refCntFieldOffset = PlatformDependent.objectFieldOffset(AbstractReferenceCounted.class.getDeclaredField("refCnt"));
}
} catch (Throwable ignored) { }
REFCNT_FIELD_OFFSET = refCntFieldOffset;
}
@SuppressWarnings("FieldMayBeFinal") @SuppressWarnings("FieldMayBeFinal")
private volatile int refCnt = 1; private volatile int refCnt = 1;
@Override @Override
public int refCnt() { public final int refCnt() {
if (REFCNT_FIELD_OFFSET >= 0) {
// Try to do non-volatile read for performance.
return PlatformDependent.getInt(this, REFCNT_FIELD_OFFSET);
} else {
return refCnt; return refCnt;
} }
}
@Override @Override
public ReferenceCounted retain() { public ReferenceCounted retain() {

View File

@ -16,6 +16,8 @@
package io.netty.buffer; package io.netty.buffer;
import io.netty.util.internal.PlatformDependent;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/** /**
@ -26,6 +28,19 @@ public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater = private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater =
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt"); AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
private static final long REFCNT_FIELD_OFFSET;
static {
long refCntFieldOffset = -1;
try {
if (PlatformDependent.hasUnsafe()) {
refCntFieldOffset = PlatformDependent.objectFieldOffset(AbstractReferenceCountedByteBuf.class.getDeclaredField("refCnt"));
}
} catch (Throwable ignored) { }
REFCNT_FIELD_OFFSET = refCntFieldOffset;
}
@SuppressWarnings("FieldMayBeFinal") @SuppressWarnings("FieldMayBeFinal")
private volatile int refCnt = 1; private volatile int refCnt = 1;
@ -34,9 +49,14 @@ public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
} }
@Override @Override
public int refCnt() { public final int refCnt() {
if (REFCNT_FIELD_OFFSET >= 0) {
// Try to do non-volatile read for performance.
return PlatformDependent.getInt(this, REFCNT_FIELD_OFFSET);
} else {
return refCnt; return refCnt;
} }
}
@Override @Override
public ByteBuf retain() { public ByteBuf retain() {

View File

@ -217,6 +217,10 @@ public final class PlatformDependent {
return PlatformDependent0.getObject(object, fieldOffset); return PlatformDependent0.getObject(object, fieldOffset);
} }
public static int getInt(Object object, long fieldOffset) {
return PlatformDependent0.getInt(object, fieldOffset);
}
public static long objectFieldOffset(Field field) { public static long objectFieldOffset(Field field) {
return PlatformDependent0.objectFieldOffset(field); return PlatformDependent0.objectFieldOffset(field);
} }

View File

@ -149,6 +149,10 @@ final class PlatformDependent0 {
return UNSAFE.getObject(object, fieldOffset); return UNSAFE.getObject(object, fieldOffset);
} }
static int getInt(Object object, long fieldOffset) {
return UNSAFE.getInt(object, fieldOffset);
}
private static long getLong(Object object, long fieldOffset) { private static long getLong(Object object, long fieldOffset) {
return UNSAFE.getLong(object, fieldOffset); return UNSAFE.getLong(object, fieldOffset);
} }