private static final void benchmark(int elements) { int bbSize = elements * 3 * 4; int vcSize = elements * 1; ByteBuffer bbA = StructAccess.createByteBuffer(bbSize); ByteBuffer bbB = StructAccess.createByteBuffer(bbSize); ByteBuffer bbC = StructAccess.createByteBuffer(bbSize); baseA = StructAccess.getBase(bbA); baseB = StructAccess.getBase(bbB); baseR = StructAccess.getBase(bbC); bytes = bbA.capacity(); Vec3[] vcA = new Vec3[vcSize]; Vec3[] vcB = new Vec3[vcSize]; Vec3[] vcC = new Vec3[vcSize]; for (int i = 0; i < vcSize; i++) { vcA[i] = new Vec3(); vcB[i] = new Vec3(); vcC[i] = new Vec3(); } int runs = 4; int loops = 1024 * 4; System.out.println("Running benchmark with " + elements + " 3d vecs..."); try { Thread.sleep(1000); } catch (Exception exc) { // } for (int run = 0; run < runs; run++) { // benchmark reset(vcA, vcB, vcC, bbA, bbB, bbC); long tVc0 = System.nanoTime(); for (int k = 0; k < loops; k++) batchWeight(vcA, vcB, 0.75F, vcC); long tVc1 = System.nanoTime(); reset(vcA, vcB, vcC, bbA, bbB, bbC); long tBbSA0 = System.nanoTime(); for (int k = 0; k < loops; k++) batchWeightSA(bbA, bbB, 0.75F, bbC); long tBbSA1 = System.nanoTime(); reset(vcA, vcB, vcC, bbA, bbB, bbC); long tBbUN0 = System.nanoTime(); for (int k = 0; k < loops; k++) batchWeightUN(0.75F); long tBbUN1 = System.nanoTime(); reset(vcA, vcB, vcC, bbA, bbB, bbC); long tMo0 = System.nanoTime(); for (int k = 0; k < loops; k++) batchWeightMO(bbA, bbB, 0.75F, bbC); long tMo1 = System.nanoTime(); // print results float tVc = (float) ((tVc1 - tVc0) / 1000000.0); float tBbSA = (float) ((tBbSA1 - tBbSA0) / 1000000.0); float tBbUN = (float) ((tBbUN1 - tBbUN0) / 1000000.0); float tMo = (float) ((tMo1 - tMo0) / 1000000.0); if (run != runs - 1) continue; System.out.println("math on Vec3[]: " + ((int) (tVc * 10) / 10.0F) + "ms " + (int) (((int) (elements / tVc * 10) / 10.0F) * 1000) + " / sec"); System.out.println("math on FloatBuffer: " + ((int) (tBbSA * 10) / 10.0F) + "ms " + (int) (((int) (elements / tBbSA * 10) / 10.0F) * 1000) + " / sec"); System.out.println("math on unsafe buffer: " + ((int) (tBbUN * 10) / 10.0F) + "ms " + (int) (((int) (elements / tBbUN * 10) / 10.0F) * 1000) + " / sec"); System.out.println("math on unsafe struct: " + ((int) (tMo * 10) / 10.0F) + "ms " + (int) (((int) (elements / tMo * 10) / 10.0F) * 1000) + " / sec"); System.out.println(); } // buffers may n-o-t be GCed before here // or we'd have memory-corrupts and native crashes bbA.clear(); bbB.clear(); bbC.clear(); bbA = null; bbB = null; bbC = null; } private static final void reset(Vec3[] vcA, Vec3[] vcB, Vec3[] vcC, ByteBuffer bbA, ByteBuffer bbB, ByteBuffer bbC) { for (int i = 0; i < vcA.length; i++) { vcA[i].x = 0; vcB[i].x = 0; vcC[i].x = 0; vcA[i].y = 0; vcB[i].y = 0; vcC[i].y = 0; vcA[i].z = 0; vcB[i].z = 0; vcC[i].z = 0; } bbA.clear(); bbB.clear(); bbC.clear(); while (bbA.hasRemaining()) { bbA.put((byte) 0x00); bbB.put((byte) 0x00); bbC.put((byte) 0x00); } } private static final void batchWeight(Vec3[] a, Vec3[] b, float weight, Vec3[] r) { // r = a * (1.0-weight) + b * weight float aMul = 1.0F - weight; float bMul = weight; for (int i = 0; i < a.length; i++) { Vec3 a0 = a[i]; Vec3 b0 = b[i]; Vec3 r0 = r[i]; r0.x = a0.x * aMul + b0.x * bMul; r0.y = a0.y * aMul + b0.y * bMul; r0.z = a0.z * aMul + b0.z * bMul; } } private static final void batchWeightSA(ByteBuffer a, ByteBuffer b, float weight, ByteBuffer r) { // r = a * (1.0-weight) + b * weight float aMul = 1.0F - weight; float bMul = weight; a.clear(); b.clear(); r.clear(); FloatBuffer fa = a.asFloatBuffer(); FloatBuffer fb = b.asFloatBuffer(); FloatBuffer fr = r.asFloatBuffer(); int loops = fa.capacity() / 3; for (int i = 0; i < loops; i++) { fr.put(fa.get() * aMul + fb.get() * bMul); fr.put(fa.get() * aMul + fb.get() * bMul); fr.put(fa.get() * aMul + fb.get() * bMul); } } static long baseA, baseB, baseR; static int bytes; private static final void batchWeightUN(float weight) { // r = a * (1.0-weight) + b * weight float aMul = 1.0F - weight; float bMul = weight; Unsafe access = StructAccess.getAccess(); int sizeOfFloat = 4; int end = bytes; // stack variables are much faster than class fields final long baseA = ClientEngine.baseA; final long baseB = ClientEngine.baseB; final long baseR = ClientEngine.baseR; for (int i = 0; i < end;) { access.putFloat(i + baseR, access.getFloat(i + baseA) * aMul + access.getFloat(i + baseB) * bMul); i += sizeOfFloat; access.putFloat(i + baseR, access.getFloat(i + baseA) * aMul + access.getFloat(i + baseB) * bMul); i += sizeOfFloat; access.putFloat(i + baseR, access.getFloat(i + baseA) * aMul + access.getFloat(i + baseB) * bMul); i += sizeOfFloat; } } private static final void batchWeightMO(ByteBuffer a, ByteBuffer b, float weight, ByteBuffer r) { // r = a * (1.0-weight) + b * weight VecC vA = new VecC(a); VecC vB = new VecC(b); VecC vR = new VecC(r); float aMul = 1.0F - weight; float bMul = weight; int loops = a.capacity() / VecC.SIZEOF; for (int i = 0; i < loops; i++) { vA.unsafePosition(i); vB.unsafePosition(i); vR.unsafePosition(i); vR.x(vA.x() * aMul + vB.x() * bMul); vR.y(vA.y() * aMul + vB.y() * bMul); vR.z(vA.z() * aMul + vB.z() * bMul); } } /** * VECC */ class VecC implements Struct { public static final int SIZEOF = 12; private static final Unsafe access = StructAccess.getAccess(); private final long base; private int offset; private final int maxIndex; public VecC(ByteBuffer bb) { if (bb.order() != ByteOrder.nativeOrder()) throw new IllegalStateException("ByteBuffer must be in native order"); base = StructAccess.getBase(bb); maxIndex = (bb.capacity() / SIZEOF) - 1; } /** * Struct interface implementation */ public final void whipe() { for (int i = 0; i < SIZEOF; i++) access.putByte(offset + base + i, (byte) 0x00); } public final void writeTo(ByteBuffer bb) { this.checkBuffer(bb); access.copyMemory(offset + base, StructAccess.getBase(bb) + bb.position(), SIZEOF); bb.position(bb.position() + SIZEOF); } public final void readFrom(ByteBuffer bb) { this.checkBuffer(bb); access.copyMemory(StructAccess.getBase(bb) + bb.position(), offset + base, SIZEOF); bb.position(bb.position() + SIZEOF); } // unsafeWriteTo (no checkbuffer) // unsafeReadFrom (no checkbuffer) /** * Instance methods */ public final void position(int index) { if (index < 0 || index > maxIndex) throw new IllegalArgumentException("Invalid position: " + index + "/" + maxIndex); this.unsafePosition(index); } public final void unsafePosition(int index) { offset = index * SIZEOF; } public final void copyTo(VecC struct) { access.copyMemory(offset + base, struct.offset + struct.base, SIZEOF); } public final void copyFrom(VecC struct) { access.copyMemory(struct.offset + struct.base, offset + base, SIZEOF); } private final void checkBuffer(ByteBuffer bb) { if (bb.order() != ByteOrder.nativeOrder()) throw new IllegalStateException("ByteBuffer must be in native order"); if (bb.remaining() < SIZEOF) throw new IllegalStateException("Not enough bytes remaining in buffer: " + bb.remaining() + "/" + SIZEOF); } /** * Getters & Setters */ public final void x(float x) { access.putFloat(0 + offset + base, x); } public final float x() { return access.getFloat(0 + offset + base); } public final void y(float y) { access.putFloat(4 + offset + base, y); } public final float y() { return access.getFloat(4 + offset + base); } public final void z(float z) { access.putFloat(8 + offset + base, z); } public final float z() { return access.getFloat(8 + offset + base); } }