Skip to content

Instantly share code, notes, and snippets.

@headius
Created December 17, 2011 18:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save headius/1490893 to your computer and use it in GitHub Desktop.
Save headius/1490893 to your computer and use it in GitHub Desktop.
Two attempts to make instance vars volatile. The ARA version is 10-15% slower, and Unsafe version is 5-10% slower.
diff --git a/src/org/jruby/RubyBasicObject.java b/src/org/jruby/RubyBasicObject.java
index 3bdc916..952c400 100644
--- a/src/org/jruby/RubyBasicObject.java
+++ b/src/org/jruby/RubyBasicObject.java
@@ -37,6 +37,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.jruby.anno.JRubyMethod;
@@ -112,7 +113,7 @@ public class RubyBasicObject implements Cloneable, IRubyObject, Serializable, Co
protected int flags;
// variable table, lazily allocated as needed (if needed)
- private transient volatile Object[] varTable;
+ private transient volatile AtomicReferenceArray<Object> varTable;
/**
* The error message used when some one tries to modify an
@@ -1215,27 +1216,27 @@ public class RubyBasicObject implements Cloneable, IRubyObject, Serializable, Co
/**
* Get variable table for read purposes. May return null if uninitialized.
*/
- protected final Object[] getVariableTableForRead() {
+ protected final AtomicReferenceArray<Object> getVariableTableForRead() {
return varTable;
}
private static final AtomicReferenceFieldUpdater VARTABLE_UPDATER =
- AtomicReferenceFieldUpdater.newUpdater(RubyBasicObject.class, Object[].class, "varTable");
+ AtomicReferenceFieldUpdater.newUpdater(RubyBasicObject.class, AtomicReferenceArray.class, "varTable");
/**
* Get variable table for write purposes. Initializes if uninitialized, and
* resizes if necessary.
*/
- protected final Object[] getVariableTableForWrite(int index) {
+ protected final AtomicReferenceArray<Object> getVariableTableForWrite(int index) {
while (true) {
- Object[] myVarTable = varTable;
- Object[] newTable;
+ AtomicReferenceArray<Object> myVarTable = varTable;
+ AtomicReferenceArray<Object> newTable;
if (myVarTable == null) {
- newTable = new Object[getMetaClass().getRealClass().getVariableTableSizeWithExtras()];
- } else if (myVarTable.length <= index) {
- newTable = new Object[getMetaClass().getRealClass().getVariableTableSizeWithExtras()];
- System.arraycopy(myVarTable, 0, newTable, 0, myVarTable.length);
+ newTable = new AtomicReferenceArray<Object>(getMetaClass().getRealClass().getVariableTableSizeWithExtras());
+ } else if (myVarTable.length() <= index) {
+ newTable = new AtomicReferenceArray<Object>(getMetaClass().getRealClass().getVariableTableSizeWithExtras());
+ for (int i = 0; i < myVarTable.length(); i++) newTable.set(i, myVarTable.get(i));
} else {
return myVarTable;
}
@@ -1248,23 +1249,23 @@ public class RubyBasicObject implements Cloneable, IRubyObject, Serializable, Co
}
public Object getVariable(int index) {
- Object[] ivarTable;
+ AtomicReferenceArray<Object> ivarTable;
if (index < 0 || (ivarTable = getVariableTableForRead()) == null) return null;
- if (ivarTable.length > index) return ivarTable[index];
+ if (ivarTable.length() > index) return ivarTable.get(index);
return null;
}
public void setVariable(int index, Object value) {
ensureInstanceVariablesSettable();
if (index < 0) return;
- Object[] ivarTable = getVariableTableForWrite(index);
- ivarTable[index] = value;
+ AtomicReferenceArray<Object> ivarTable = getVariableTableForWrite(index);
+ ivarTable.set(index, value);
}
private void setObjectId(int index, long value) {
if (index < 0) return;
- Object[] ivarTable = getVariableTableForWrite(index);
- ivarTable[index] = value;
+ AtomicReferenceArray<Object> ivarTable = getVariableTableForWrite(index);
+ ivarTable.set(index, value);
}
public final Object getNativeHandle() {
@@ -1273,8 +1274,8 @@ public class RubyBasicObject implements Cloneable, IRubyObject, Serializable, Co
public final void setNativeHandle(Object value) {
int index = getMetaClass().getRealClass().getNativeHandleAccessorForWrite().getIndex();
- Object[] ivarTable = getVariableTableForWrite(index);
- ivarTable[index] = value;
+ AtomicReferenceArray<Object> ivarTable = getVariableTableForWrite(index);
+ ivarTable.set(index, value);
}
//
@@ -1293,7 +1294,7 @@ public class RubyBasicObject implements Cloneable, IRubyObject, Serializable, Co
*/
public boolean hasVariables() {
// we check both to exclude object_id
- return getMetaClass().getRealClass().getVariableTableSize() > 0 && varTable != null && varTable.length > 0;
+ return getMetaClass().getRealClass().getVariableTableSize() > 0 && varTable != null && varTable.length() > 0;
}
/**
@@ -1303,7 +1304,7 @@ public class RubyBasicObject implements Cloneable, IRubyObject, Serializable, Co
@Deprecated
public int getVariableCount() {
// we use min to exclude object_id
- return varTable == null ? 0 : Math.min(varTable.length, getMetaClass().getRealClass().getVariableTableSize());
+ return varTable == null ? 0 : Math.min(varTable.length(), getMetaClass().getRealClass().getVariableTableSize());
}
/**
diff --git a/src/org/jruby/RubyBasicObject.java b/src/org/jruby/RubyBasicObject.java
index 3bdc916..d5607c7 100644
--- a/src/org/jruby/RubyBasicObject.java
+++ b/src/org/jruby/RubyBasicObject.java
@@ -66,6 +66,7 @@ import org.jruby.util.IdUtil;
import org.jruby.util.TypeConverter;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;
+import org.jruby.util.unsafe.UnsafeGetter;
import static org.jruby.javasupport.util.RuntimeHelpers.invokedynamic;
import static org.jruby.runtime.MethodIndex.OP_EQUAL;
@@ -114,6 +115,38 @@ public class RubyBasicObject implements Cloneable, IRubyObject, Serializable, Co
// variable table, lazily allocated as needed (if needed)
private transient volatile Object[] varTable;
+ // unsafe logic for volatile updates to var table
+ private static final sun.misc.Unsafe unsafe;
+ static {
+ sun.misc.Unsafe _unsafe;
+ try {
+ _unsafe = UnsafeGetter.getUnsafe();
+ } catch (UnsafeGetter.UnsafeInaccessibleException x) {
+ _unsafe = null;
+ }
+ unsafe = _unsafe;
+ }
+ private static final int base = unsafe.arrayBaseOffset(Object[].class);
+ private static final int shift;
+
+ static {
+ int scale = unsafe.arrayIndexScale(Object[].class);
+ if ((scale & (scale - 1)) != 0)
+ throw new Error("data type scale not a power of two");
+ shift = 31 - Integer.numberOfLeadingZeros(scale);
+ }
+
+ private long checkedByteOffset(int i) {
+ if (i < 0 || i >= varTable.length)
+ throw new IndexOutOfBoundsException("index " + i);
+
+ return byteOffset(i);
+ }
+
+ private static long byteOffset(int i) {
+ return ((long) i << shift) + base;
+ }
+
/**
* The error message used when some one tries to modify an
* instance variable in a high security setting.
@@ -1250,7 +1283,7 @@ public class RubyBasicObject implements Cloneable, IRubyObject, Serializable, Co
public Object getVariable(int index) {
Object[] ivarTable;
if (index < 0 || (ivarTable = getVariableTableForRead()) == null) return null;
- if (ivarTable.length > index) return ivarTable[index];
+ if (ivarTable.length > index) return unsafe.getObjectVolatile(varTable, checkedByteOffset(index));
return null;
}
@@ -1258,7 +1291,7 @@ public class RubyBasicObject implements Cloneable, IRubyObject, Serializable, Co
ensureInstanceVariablesSettable();
if (index < 0) return;
Object[] ivarTable = getVariableTableForWrite(index);
- ivarTable[index] = value;
+ unsafe.putObjectVolatile(varTable, checkedByteOffset(index), value);
}
private void setObjectId(int index, long value) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment