java - Is using own int capacity faster than using .length field of an array? -
in "95% of performance clean representative models" talk martin thompson, between 17 , 21 minute, such code presented:
public class queue { private final object[] buffer; private final int capacity; // rest of code }
in 20:16 says:
you can better performance, leaving things
capacity
in there right thing do.
i tried come code example in capacity
faster buffer.length
, failed.
martin saying problems arise in 2 scenerios:
- in concurrent world. but,
length
fieldfinal
, jls 10.7. so, don't see how problem. - when cache misses. tried calling
capacity
vsbuffer.length
1 million times (with queue having 1 million of elements), there no significant difference. used jmh benchmarking.
could please provide code example, demonstrates case in capacity
superior buffer.length
in terms of performance?
the more common case (frequently spotted in real code), better.
please note, i'm totally taking away aspect of aesthetics, clean code, potential code re-factoring etc. i'm asking performance.
when access array normally, jvm uses length
anyway perform bounds check. when access array via sun.misc.unsafe
(like martin does), don't have pay implicit penalty.
array's length
field lies in same cache line first elements, have false sharing when multiple threads write first indices concurrently. using separate field buffer capacity break false sharing.
here benchmark shows how capacity
field makes array access substantially faster:
package bench; import org.openjdk.jmh.annotations.benchmark; import org.openjdk.jmh.annotations.param; import org.openjdk.jmh.annotations.scope; import org.openjdk.jmh.annotations.setup; import org.openjdk.jmh.annotations.state; import org.openjdk.jmh.annotations.threads; import sun.misc.unsafe; import java.lang.reflect.field; import java.util.concurrent.atomic.atomicreferencearray; @state(scope.benchmark) @threads(4) public class queue { private static final unsafe unsafe = getunsafe(); private static final long base = unsafe.arraybaseoffset(object[].class); private static final int scale = unsafe.arrayindexscale(object[].class); private atomicreferencearray<object> atomic; private object[] buffer; private int capacity; @param({"0", "25"}) private volatile int index; @setup public void setup() { capacity = 32; buffer = new object[capacity]; atomic = new atomicreferencearray<>(capacity); } @benchmark public void atomicarray() { atomic.set(index, "payload"); } @benchmark public void unsafearraylength() { int index = this.index; if (index < 0 || index >= buffer.length) { throw new arrayindexoutofboundsexception(); } unsafe.putobjectvolatile(buffer, base + index * scale, "payload"); } @benchmark public void unsafecapacityfield() { int index = this.index; if (index < 0 || index >= capacity) { throw new arrayindexoutofboundsexception(); } unsafe.putobjectvolatile(buffer, base + index * scale, "payload"); } private static unsafe getunsafe() { try { field f = unsafe.class.getdeclaredfield("theunsafe"); f.setaccessible(true); return (unsafe) f.get(null); } catch (illegalaccessexception | nosuchfieldexception e) { throw new assertionerror("should not happen"); } } }
results:
benchmark (index) mode cnt score error units queue.atomicarray 0 thrpt 5 41804,825 ± 928,882 ops/ms queue.atomicarray 25 thrpt 5 84713,201 ± 1067,911 ops/ms queue.unsafearraylength 0 thrpt 5 48656,296 ± 676,166 ops/ms queue.unsafearraylength 25 thrpt 5 88812,863 ± 1089,380 ops/ms queue.unsafecapacityfield 0 thrpt 5 88904,433 ± 360,936 ops/ms queue.unsafecapacityfield 25 thrpt 5 88633,490 ± 1426,329 ops/ms
Comments
Post a Comment