Skip to content

Instantly share code, notes, and snippets.

@headius
Created September 6, 2012 20:02
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/4c15977b9572cba63f92 to your computer and use it in GitHub Desktop.
Save headius/4c15977b9572cba63f92 to your computer and use it in GitHub Desktop.
VolatileStruct, benchmark, and results
From 6f4e6295ef0de1350cb9de423e85b3834a359c7d Mon Sep 17 00:00:00 2001
From: Charles Oliver Nutter <headius@headius.com>
Date: Thu, 6 Sep 2012 15:01:47 -0500
Subject: [PATCH] Add VolatileStruct class.
---
src/org/jruby/RubyStruct.java | 113 +++++----
src/org/jruby/VolatileStruct.java | 447 +++++++++++++++++++++++++++++++++
src/org/jruby/runtime/ClassIndex.java | 3 +-
3 files changed, 512 insertions(+), 51 deletions(-)
create mode 100644 src/org/jruby/VolatileStruct.java
diff --git a/src/org/jruby/RubyStruct.java b/src/org/jruby/RubyStruct.java
index 869a4dd..6c323e0 100644
--- a/src/org/jruby/RubyStruct.java
+++ b/src/org/jruby/RubyStruct.java
@@ -54,8 +54,6 @@ import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.javasupport.util.RuntimeHelpers;
import org.jruby.runtime.ClassIndex;
-import java.util.concurrent.Callable;
-
import static org.jruby.runtime.Visibility.*;
import static org.jruby.javasupport.util.RuntimeHelpers.invokedynamic;
@@ -73,7 +71,7 @@ public class RubyStruct extends RubyObject {
* @param runtime
* @param rubyClass
*/
- private RubyStruct(Ruby runtime, RubyClass rubyClass) {
+ protected RubyStruct(Ruby runtime, RubyClass rubyClass) {
super(runtime, rubyClass);
int size = RubyNumeric.fix2int(getInternalVariable((RubyClass)rubyClass, "__size__"));
@@ -90,6 +88,8 @@ public class RubyStruct extends RubyObject {
structClass.includeModule(runtime.getEnumerable());
structClass.defineAnnotatedMethods(RubyStruct.class);
+ VolatileStruct.createVolatileStructClass(runtime);
+
return structClass;
}
@@ -98,7 +98,7 @@ public class RubyStruct extends RubyObject {
return ClassIndex.STRUCT;
}
- private static IRubyObject getInternalVariable(RubyClass type, String internedName) {
+ protected static IRubyObject getInternalVariable(RubyClass type, String internedName) {
RubyClass structClass = type.getRuntime().getStructClass();
IRubyObject variable;
@@ -113,11 +113,11 @@ public class RubyStruct extends RubyObject {
return type.getRuntime().getNil();
}
- private RubyClass classOf() {
+ protected RubyClass classOf() {
return getMetaClass() instanceof MetaClass ? getMetaClass().getSuperClass() : getMetaClass();
}
- private void modify() {
+ protected void modify() {
testFrozen();
}
@@ -224,6 +224,22 @@ public class RubyStruct extends RubyObject {
newStruct.getSingletonClass().defineAnnotatedMethods(StructMethods.class);
// define access methods.
+ defineAccessors(args, name, nilName, newStruct);
+
+ doYield(block, runtime, newStruct);
+
+ return newStruct;
+ }
+
+ protected static void doYield(Block block, Ruby runtime, RubyClass newStruct) {
+ if (block.isGiven()) {
+ // Struct bodies should be public by default, so set block visibility to public. JRUBY-1185.
+ block.getBinding().setVisibility(Visibility.PUBLIC);
+ block.yieldNonArray(runtime.getCurrentContext(), null, newStruct, newStruct);
+ }
+ }
+
+ protected static void defineAccessors(IRubyObject[] args, String name, boolean nilName, final RubyClass newStruct) {
for (int i = (name == null && !nilName) ? 0 : 1; i < args.length; i++) {
final String memberName = args[i].asJavaString();
// if we are storing a name as well, index is one too high for values
@@ -249,12 +265,12 @@ public class RubyStruct extends RubyObject {
@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
Arity.checkArgumentCount(context.runtime, name, args, 1, 1);
- return ((RubyStruct)self).set(args[0], index);
+ return ((RubyStruct)self).set(index, args[0]);
}
@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg) {
- return ((RubyStruct)self).set(arg, index);
+ return ((RubyStruct)self).set(index, arg);
}
@Override
@@ -263,16 +279,8 @@ public class RubyStruct extends RubyObject {
}
});
}
-
- if (block.isGiven()) {
- // Struct bodies should be public by default, so set block visibility to public. JRUBY-1185.
- block.getBinding().setVisibility(Visibility.PUBLIC);
- block.yieldNonArray(runtime.getCurrentContext(), null, newStruct, newStruct);
- }
-
- return newStruct;
}
-
+
// For binding purposes on the newly created struct types
public static class StructMethods {
@JRubyMethod(name = {"new", "[]"}, rest = true)
@@ -356,9 +364,9 @@ public class RubyStruct extends RubyObject {
return struct;
}
- private void checkSize(int length) {
- if (length > values.length) {
- throw getRuntime().newArgumentError("struct size differs (" + length +" for " + values.length + ")");
+ protected void checkSize(int length) {
+ if (length > length()) {
+ throw getRuntime().newArgumentError("struct size differs (" + length +" for " + length() + ")");
}
}
@@ -455,27 +463,32 @@ public class RubyStruct extends RubyObject {
public RubyArray select(ThreadContext context, Block block) {
RubyArray array = RubyArray.newArray(context.runtime);
- for (int i = 0; i < values.length; i++) {
- if (block.yield(context, values[i]).isTrue()) {
- array.append(values[i]);
+ for (int i = 0; i < length(); i++) {
+ IRubyObject value = get(i);
+ if (block.yield(context, value).isTrue()) {
+ array.append(value);
}
}
return array;
}
- public IRubyObject set(IRubyObject value, int index) {
+ protected IRubyObject set(int index, IRubyObject value) {
modify();
return values[index] = value;
}
- private RaiseException notStructMemberError(String name) {
- return getRuntime().newNameError(name + " is not struct member", name);
+ protected IRubyObject get(int index) {
+ return values[index];
}
- public IRubyObject get(int index) {
- return values[index];
+ protected int length() {
+ return values.length;
+ }
+
+ protected RaiseException notStructMemberError(String name) {
+ return getRuntime().newNameError(name + " is not struct member", name);
}
@Override
@@ -502,8 +515,8 @@ public class RubyStruct extends RubyObject {
if (recur) {
return runtime.getTrue();
}
- for (int i = 0; i < values.length; i++) {
- if (!equalInternal(context, values[i], otherStruct.values[i])) return runtime.getFalse();
+ for (int i = 0; i < length(); i++) {
+ if (!equalInternal(context, get(i), otherStruct.get(i))) return runtime.getFalse();
}
return runtime.getTrue();
}
@@ -528,8 +541,8 @@ public class RubyStruct extends RubyObject {
if (recur) {
return runtime.getTrue();
}
- for (int i = 0; i < values.length; i++) {
- if (!eqlInternal(context, values[i], otherStruct.values[i])) return runtime.getFalse();
+ for (int i = 0; i < length(); i++) {
+ if (!eqlInternal(context, get(i), otherStruct.get(i))) return runtime.getFalse();
}
return runtime.getTrue();
}
@@ -553,7 +566,7 @@ public class RubyStruct extends RubyObject {
// FIXME: MRI has special case for constants here
buffer.append(RubyString.objAsString(context, member.eltInternal(i)).getByteList());
buffer.append('=');
- buffer.append(inspect(context, values[i]).getByteList());
+ buffer.append(inspect(context, get(i)).getByteList());
}
buffer.append('>');
@@ -579,12 +592,12 @@ public class RubyStruct extends RubyObject {
@JRubyMethod(name = {"size", "length"} )
public RubyFixnum size() {
- return getRuntime().newFixnum(values.length);
+ return getRuntime().newFixnum(length());
}
public IRubyObject eachInternal(ThreadContext context, Block block) {
- for (int i = 0; i < values.length; i++) {
- block.yield(context, values[i]);
+ for (int i = 0; i < length(); i++) {
+ block.yield(context, get(i));
}
return this;
@@ -600,8 +613,8 @@ public class RubyStruct extends RubyObject {
assert !member.isNil() : "uninitialized struct";
- for (int i = 0; i < values.length; i++) {
- block.yield(context, getRuntime().newArrayNoCopy(new IRubyObject[]{member.eltInternal(i), values[i]}));
+ for (int i = 0; i < length(); i++) {
+ block.yield(context, getRuntime().newArrayNoCopy(new IRubyObject[]{member.eltInternal(i), get(i)}));
}
return this;
@@ -620,15 +633,15 @@ public class RubyStruct extends RubyObject {
int idx = RubyNumeric.fix2int(key);
- idx = idx < 0 ? values.length + idx : idx;
+ idx = idx < 0 ? length() + idx : idx;
if (idx < 0) {
- throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
- } else if (idx >= values.length) {
- throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
+ throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + length() + ")");
+ } else if (idx >= length()) {
+ throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + length() + ")");
}
- return values[idx];
+ return get(idx);
}
@JRubyMethod(name = "[]=", required = 2)
@@ -639,16 +652,16 @@ public class RubyStruct extends RubyObject {
int idx = RubyNumeric.fix2int(key);
- idx = idx < 0 ? values.length + idx : idx;
+ idx = idx < 0 ? length() + idx : idx;
if (idx < 0) {
- throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
- } else if (idx >= values.length) {
- throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
+ throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + length() + ")");
+ } else if (idx >= length()) {
+ throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + length() + ")");
}
modify();
- return values[idx] = value;
+ return set(idx, value);
}
// FIXME: This is copied code from RubyArray. Both RE, Struct, and Array should share one impl
@@ -656,7 +669,7 @@ public class RubyStruct extends RubyObject {
// of something lower.
@JRubyMethod(rest = true)
public IRubyObject values_at(IRubyObject[] args) {
- int olen = values.length;
+ int olen = length();
RubyArray result = getRuntime().newArray(args.length);
for (int i = 0; i < args.length; i++) {
@@ -694,7 +707,7 @@ public class RubyStruct extends RubyObject {
for (int i = 0; i < array.size(); i++) {
RubySymbol name = (RubySymbol) array.eltInternal(i);
output.dumpObject(name);
- output.dumpObject(struct.values[i]);
+ output.dumpObject(struct.get(i));
}
}
diff --git a/src/org/jruby/VolatileStruct.java b/src/org/jruby/VolatileStruct.java
new file mode 100644
index 0000000..203575a
--- /dev/null
+++ b/src/org/jruby/VolatileStruct.java
@@ -0,0 +1,447 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
+ * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
+ * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
+ * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
+ * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
+ * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the CPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby;
+
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.common.IRubyWarnings.ID;
+import org.jruby.internal.runtime.methods.CallConfiguration;
+import org.jruby.internal.runtime.methods.DynamicMethod;
+import org.jruby.javasupport.util.RuntimeHelpers;
+import org.jruby.runtime.Arity;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ClassIndex;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.Visibility;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.IdUtil;
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+
+import static org.jruby.javasupport.util.RuntimeHelpers.invokedynamic;
+import static org.jruby.runtime.MethodIndex.HASH;
+
+/**
+ * @author jpetersen
+ */
+@JRubyClass(name="Volatile")
+public class VolatileStruct extends RubyStruct {
+ private final AtomicReferenceArray<IRubyObject> values;
+
+ /**
+ * Constructor for VolatileStruct.
+ * @param runtime
+ * @param rubyClass
+ */
+ private VolatileStruct(Ruby runtime, RubyClass rubyClass) {
+ super(runtime, rubyClass);
+
+ int size = RubyNumeric.fix2int(getInternalVariable((RubyClass)rubyClass, "__size__"));
+
+ IRubyObject[] nils = runtime.getNilPrefilledArray();
+ if (size <= nils.length) {
+ values = new AtomicReferenceArray<IRubyObject>(Arrays.copyOf(runtime.getNilPrefilledArray(), size));
+ } else {
+ IRubyObject[] tmp = new IRubyObject[size];
+ RuntimeHelpers.fillNil(tmp, runtime);
+ values = new AtomicReferenceArray<IRubyObject>(tmp);
+ }
+ }
+
+ public static RubyClass createVolatileStructClass(Ruby runtime) {
+ RubyClass volatileStructClass = runtime.defineClass("VolatileStruct", runtime.getStructClass(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
+ volatileStructClass.index = ClassIndex.VOLATILE_STRUCT;
+ volatileStructClass.includeModule(runtime.getEnumerable());
+ volatileStructClass.defineAnnotatedMethods(VolatileStruct.class);
+
+ return volatileStructClass;
+ }
+
+ @Override
+ public int getNativeTypeIndex() {
+ return ClassIndex.VOLATILE_STRUCT;
+ }
+
+ @Override
+ public RubyFixnum hash(ThreadContext context) {
+ Ruby runtime = getRuntime();
+ int h = getMetaClass().getRealClass().hashCode();
+
+ for (int i = 0; i < values.length(); i++) {
+ h = (h << 1) | (h < 0 ? 1 : 0);
+ h ^= RubyNumeric.num2long(invokedynamic(context, values.get(i), HASH));
+ }
+
+ return runtime.newFixnum(h);
+ }
+
+ // Struct methods
+
+ /** Create new Struct class.
+ *
+ * MRI: rb_struct_s_def / make_struct
+ *
+ */
+ @JRubyMethod(name = "new", required = 1, rest = true, meta = true)
+ public static RubyClass newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
+ String name = null;
+ boolean nilName = false;
+ Ruby runtime = recv.getRuntime();
+
+ if (args.length > 0) {
+ IRubyObject firstArgAsString = args[0].checkStringType();
+ if (!firstArgAsString.isNil()) {
+ name = ((RubyString)firstArgAsString).getByteList().toString();
+ } else if (args[0].isNil()) {
+ nilName = true;
+ }
+ }
+
+ RubyArray member = runtime.newArray();
+
+ for (int i = (name == null && !nilName) ? 0 : 1; i < args.length; i++) {
+ member.append(runtime.newSymbol(args[i].asJavaString()));
+ }
+
+ RubyClass newStruct;
+ RubyClass superClass = (RubyClass)recv;
+
+ if (name == null || nilName) {
+ newStruct = RubyClass.newClass(runtime, superClass);
+ newStruct.setAllocator(VOLATILE_STRUCT_INSTANCE_ALLOCATOR);
+ newStruct.makeMetaClass(superClass.getMetaClass());
+ newStruct.inherit(superClass);
+ } else {
+ if (!IdUtil.isConstant(name)) {
+ throw runtime.newNameError("identifier " + name + " needs to be constant", name);
+ }
+
+ IRubyObject type = superClass.getConstantAt(name);
+ if (type != null) {
+ ThreadContext context = runtime.getCurrentContext();
+ runtime.getWarnings().warn(ID.STRUCT_CONSTANT_REDEFINED, context.getFile(), context.getLine(), "redefining constant Struct::" + name);
+ superClass.remove_const(context, runtime.newString(name));
+ }
+ newStruct = superClass.defineClassUnder(name, superClass, VOLATILE_STRUCT_INSTANCE_ALLOCATOR);
+ }
+
+ // set reified class to VolatileStruct, for Java subclasses to use
+ newStruct.setReifiedClass(VolatileStruct.class);
+ newStruct.index = ClassIndex.STRUCT;
+
+ newStruct.setInternalVariable("__size__", member.length());
+ newStruct.setInternalVariable("__member__", member);
+
+ newStruct.getSingletonClass().defineAnnotatedMethods(VolatileStructMethods.class);
+
+ // define access methods.
+ defineAccessors(args, name, nilName, newStruct);
+ defineAtomicAccessors(args, name, nilName, newStruct);
+
+ doYield(block, runtime, newStruct);
+
+ return newStruct;
+ }
+
+ protected static void defineAtomicAccessors(IRubyObject[] args, String name, boolean nilName, final RubyClass newStruct) {
+ for (int i = (name == null && !nilName) ? 0 : 1; i < args.length; i++) {
+ final String memberName = args[i].asJavaString();
+ // if we are storing a name as well, index is one too high for values
+ final int index = (name == null && !nilName) ? i : i - 1;
+ newStruct.addMethod("swap_" + memberName, new DynamicMethod(newStruct, Visibility.PUBLIC, CallConfiguration.FrameNoneScopeNone) {
+ @Override
+ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
+ Arity.checkArgumentCount(context.runtime, name, args, 1, 1);
+ return ((VolatileStruct)self).values.getAndSet(index, args[0]);
+ }
+
+ @Override
+ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
+ return ((VolatileStruct)self).values.getAndSet(index, arg0);
+ }
+
+ @Override
+ public DynamicMethod dup() {
+ return this;
+ }
+ });
+ newStruct.addMethod("cas_" + memberName, new DynamicMethod(newStruct, Visibility.PUBLIC, CallConfiguration.FrameNoneScopeNone) {
+ @Override
+ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
+ Arity.checkArgumentCount(context.runtime, name, args, 2, 2);
+ return context.runtime.newBoolean(
+ ((VolatileStruct)self).values.compareAndSet(index, args[0], args[1]));
+ }
+
+ @Override
+ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1) {
+ return context.runtime.newBoolean(
+ ((VolatileStruct)self).values.compareAndSet(index, arg0, arg1));
+ }
+
+ @Override
+ public DynamicMethod dup() {
+ return this;
+ }
+ });
+ }
+ }
+
+ // For binding purposes on the newly created struct types
+ public static class VolatileStructMethods {
+ @JRubyMethod(name = {"new", "[]"}, rest = true)
+ public static IRubyObject newStruct(IRubyObject recv, IRubyObject[] args, Block block) {
+ return VolatileStruct.newStruct(recv, args, block);
+ }
+
+ @JRubyMethod(name = {"new", "[]"})
+ public static IRubyObject newStruct(IRubyObject recv, Block block) {
+ return VolatileStruct.newStruct(recv, block);
+ }
+
+ @JRubyMethod(name = {"new", "[]"})
+ public static IRubyObject newStruct(IRubyObject recv, IRubyObject arg0, Block block) {
+ return VolatileStruct.newStruct(recv, arg0, block);
+ }
+
+ @JRubyMethod(name = {"new", "[]"})
+ public static IRubyObject newStruct(IRubyObject recv, IRubyObject arg0, IRubyObject arg1, Block block) {
+ return VolatileStruct.newStruct(recv, arg0, arg1, block);
+ }
+
+ @JRubyMethod(name = {"new", "[]"})
+ public static IRubyObject newStruct(IRubyObject recv, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
+ return VolatileStruct.newStruct(recv, arg0, arg1, arg2, block);
+ }
+
+ @JRubyMethod(name = "members", compat = CompatVersion.RUBY1_8)
+ public static IRubyObject members(IRubyObject recv, Block block) {
+ return VolatileStruct.members(recv, block);
+ }
+
+ @JRubyMethod(name = "members", compat = CompatVersion.RUBY1_9)
+ public static IRubyObject members19(IRubyObject recv, Block block) {
+ return VolatileStruct.members19(recv, block);
+ }
+ }
+
+ /** Create new Structure.
+ *
+ * MRI: struct_alloc
+ *
+ */
+ public static VolatileStruct newStruct(IRubyObject recv, IRubyObject[] args, Block block) {
+ VolatileStruct struct = new VolatileStruct(recv.getRuntime(), (RubyClass) recv);
+
+ struct.callInit(args, block);
+
+ return struct;
+ }
+
+ public static VolatileStruct newStruct(IRubyObject recv, Block block) {
+ VolatileStruct struct = new VolatileStruct(recv.getRuntime(), (RubyClass) recv);
+
+ struct.callInit(block);
+
+ return struct;
+ }
+
+ public static VolatileStruct newStruct(IRubyObject recv, IRubyObject arg0, Block block) {
+ VolatileStruct struct = new VolatileStruct(recv.getRuntime(), (RubyClass) recv);
+
+ struct.callInit(arg0, block);
+
+ return struct;
+ }
+
+ public static VolatileStruct newStruct(IRubyObject recv, IRubyObject arg0, IRubyObject arg1, Block block) {
+ VolatileStruct struct = new VolatileStruct(recv.getRuntime(), (RubyClass) recv);
+
+ struct.callInit(arg0, arg1, block);
+
+ return struct;
+ }
+
+ public static VolatileStruct newStruct(IRubyObject recv, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
+ VolatileStruct struct = new VolatileStruct(recv.getRuntime(), (RubyClass) recv);
+
+ struct.callInit(arg0, arg1, arg2, block);
+
+ return struct;
+ }
+
+ @Override
+ public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
+ modify();
+ checkSize(args.length);
+
+ IRubyObject nil = context.nil;
+
+ int i = 0;
+ for (; i < args.length; i++) {
+ values.set(i, args[i]);
+ }
+ for (; i < values.length(); i++) {
+ values.set(i, nil);
+ }
+
+ return nil;
+ }
+
+ @Override
+ public IRubyObject initialize(ThreadContext context) {
+ IRubyObject nil = context.nil;
+ return initializeInternal(context, 0, nil, nil, nil);
+ }
+
+ @Override
+ public IRubyObject initialize(ThreadContext context, IRubyObject arg0) {
+ IRubyObject nil = context.nil;
+ return initializeInternal(context, 1, arg0, nil, nil);
+ }
+
+ @Override
+ public IRubyObject initialize(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
+ return initializeInternal(context, 2, arg0, arg1, context.nil);
+ }
+
+ @Override
+ public IRubyObject initialize(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
+ return initializeInternal(context, 3, arg0, arg1, arg2);
+ }
+
+ @Override
+ public IRubyObject initializeInternal(ThreadContext context, int provided, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
+ modify();
+ checkSize(provided);
+
+ switch (provided) {
+ case 3:
+ values.set(2, arg2);
+ case 2:
+ values.set(1, arg1);
+ case 1:
+ values.set(0, arg0);
+ }
+
+ IRubyObject nil = context.nil;
+
+ for (; provided < values.length(); provided++) {
+ values.set(provided, nil);
+ }
+
+ return nil;
+ }
+
+ public static RubyArray members(IRubyObject recv, Block block) {
+ RubyArray member = (RubyArray) getInternalVariable((RubyClass) recv, "__member__");
+
+ assert !member.isNil() : "uninitialized struct";
+
+ RubyArray result = recv.getRuntime().newArray(member.getLength());
+ for (int i = 0,k=member.getLength(); i < k; i++) {
+ // this looks weird, but it's because they're RubySymbol and that's java.lang.String internally
+ result.append(recv.getRuntime().newString(member.eltInternal(i).asJavaString()));
+ }
+
+ return result;
+ }
+
+ public static RubyArray members19(IRubyObject recv, Block block) {
+ RubyArray member = (RubyArray) getInternalVariable((RubyClass) recv, "__member__");
+
+ assert !member.isNil() : "uninitialized struct";
+
+ RubyArray result = recv.getRuntime().newArray(member.getLength());
+ for (int i = 0,k=member.getLength(); i < k; i++) {
+ result.append(member.eltInternal(i));
+ }
+
+ return result;
+ }
+
+ @Override
+ public RubyArray members() {
+ return members(classOf(), Block.NULL_BLOCK);
+ }
+
+ @Override
+ public RubyArray members19() {
+ return members19(classOf(), Block.NULL_BLOCK);
+ }
+
+ protected IRubyObject set(int index, IRubyObject value) {
+ modify();
+
+ values.set(index, value);
+
+ return value;
+ }
+
+ protected IRubyObject get(int index) {
+ return values.get(index);
+ }
+
+ protected int length() {
+ return values.length();
+ }
+
+ @Override
+ public void copySpecialInstanceVariables(IRubyObject clone) {
+ VolatileStruct struct = (VolatileStruct)clone;
+ for (int i = 0; i < length(); i++) {
+ struct.set(i, get(i));
+ }
+ }
+
+ @Override
+ public RubyArray to_a() {
+ IRubyObject[] ary = new IRubyObject[values.length()];
+
+ for (int i = 0; i < values.length(); i++) ary[i] = values.get(i);
+
+ return getRuntime().newArray(ary);
+ }
+
+ private static ObjectAllocator VOLATILE_STRUCT_INSTANCE_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ VolatileStruct instance = new VolatileStruct(runtime, klass);
+
+ instance.setMetaClass(klass);
+
+ return instance;
+ }
+ };
+}
diff --git a/src/org/jruby/runtime/ClassIndex.java b/src/org/jruby/runtime/ClassIndex.java
index bb77739..00a0291 100644
--- a/src/org/jruby/runtime/ClassIndex.java
+++ b/src/org/jruby/runtime/ClassIndex.java
@@ -54,7 +54,8 @@ public final class ClassIndex {
public static final int UNBOUNDMETHOD = 36;
public static final int CONTINUATION = 37;
public static final int BASICOBJECT = 38;
- public static final int MAX_CLASSES = 39;
+ public static final int VOLATILE_STRUCT = 39;
+ public static final int MAX_CLASSES = 40;
/** Creates a new instance of ClassIndex */
private ClassIndex() {
--
1.7.10.4
require 'benchmark/ips'
HAVE_VSTRUCT = defined?(VolatileStruct)
Foo = Struct.new(:a, :b, :c, :d, :e)
Bar = VolatileStruct.new(:a, :b, :c, :d, :e) if HAVE_VSTRUCT
Benchmark.ips do |bm|
foo = Foo.new(1, 2, 3, 4, 5)
bar = Bar.new(1, 2, 3, 4, 5) if HAVE_VSTRUCT
bm.report("struct member access") {|n|
n.times { foo.a; foo.b; foo.c; foo.d; foo.e }
}
bm.report("struct member mutate") {|n|
n.times { foo.a=1; foo.b=1; foo.c=1; foo.d=1; foo.e=1 }
}
bm.report("struct to_s") {|n|
n.times { foo.to_s }
}
bm.report("struct each") {|n|
n.times { foo.each {|x| x} }
}
bm.report("struct allocation") {|n|
n.times { Foo.new(1, 2, 3, 4, 5) }
}
next unless HAVE_VSTRUCT
bm.report("vstruct member access") {|n|
n.times { bar.a; bar.b; bar.c; bar.d; bar.e }
}
bm.report("vstruct member mutate") {|n|
n.times { bar.a=1; bar.b=2; bar.c=3; bar.d=4; bar.e=5 }
}
bm.report("vstruct to_s") {|n|
n.times { bar.to_s }
}
bm.report("vstruct each") {|n|
n.times { bar.each {|x| x} }
}
bm.report("vstruct allocation") {|n|
n.times { Bar.new(1, 2, 3, 4, 5) }
}
bm.report("vstruct swap") {|n|
n.times { bar.swap_a(1); bar.swap_b(2); bar.swap_c(3); bar.swap_d(4); bar.swap_e(5) }
}
bm.report("vstruct cas") {|n|
n.times { bar.cas_a(1,1); bar.cas_b(2,2); bar.cas_c(3,3); bar.cas_d(4,4); bar.cas_e(5,5) }
}
end
Ruby 1.9.3
struct member access 2704659.5 (±1.8%) i/s - 13523244 in 5.001728s
struct member mutate 1392143.5 (±2.2%) i/s - 6984750 in 5.019915s
struct to_s 310352.7 (±1.4%) i/s - 1566728 in 5.049164s
struct each 2069069.9 (±2.4%) i/s - 10388650 in 5.024132s
struct allocation 1940827.3 (±8.2%) i/s - 9642576 in 5.003262s
JRuby
struct member access 8876567.2 (±5.5%) i/s - 44150546 in 4.992000s
struct member mutate 7959387.2 (±5.1%) i/s - 39744642 in 5.008000s
struct to_s 863746.5 (±2.5%) i/s - 4348708 in 5.038000s
struct each 2827729.3 (±3.0%) i/s - 14165010 in 5.014000s
struct allocation 6065116.7 (±4.9%) i/s - 30225746 in 4.997000s
vstruct member access
7535692.1 (±4.8%) i/s - 37605387 in 5.003000s
vstruct member mutate
5455515.1 (±4.4%) i/s - 27215706 in 4.999000s
vstruct to_s 849120.5 (±2.6%) i/s - 4279102 in 5.043000s
vstruct each 2796831.4 (±3.8%) i/s - 13979980 in 5.007000s
vstruct allocation 3227549.3 (±4.6%) i/s - 16135345 in 5.014000s
vstruct swap 6802820.6 (±4.8%) i/s - 33909096 in 4.997000s
vstruct cas 7096547.3 (±4.4%) i/s - 35355473 in 4.993000s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment