Below is a benchmark showing performance differences between current
sgine Matrix implementation and simplex3d.
I have tried to compare different operations such as multiplication,
local multiplication ( *= in simplex3d) and transformations. However
allocating sgine Matrix in a loop would crash my computer. My guess
this happens due to directBuffer allocation and JVM not being able to
manage direct buffers allocation/deallocation efficiently. This may
not be an issue in general, but it's very easy to fall into the trap
and write a code that allocates many Matrix4 objects, crashing the
system.
Because of this limitation I had to stick with transformation tests,
which behave as expected. One thing to note is that sgine
transformation operations happend in order right to left, so:
Matrix4() trasnlate() scale() will scale first, then translate. In
simplex3d the order of operations is how they are wrtitten, from left
to right.
Since simplex3d transformations are optimised and are using 3x4
matrices instead of 4x4, i have added a test that is constructing and
multiplying simplex3d 4x4 matrices. This way it is more fair, because
sgine is using 4x4 matrices.
Below are the results (in milliseconds):
Sgine transform time: 12889.
Simplex transform time: 4999.
Simplex simulated transform time: 7397.
Simplex3d is 2.5 times faster when using optimized Mat3x4, and 1.75
times faster when doing transformations by multipling 4x4 matrices.
Notice that simplex3d is making a new instance on every operation, and
in case of simulating transformations it is making new instances to
multiply by. Yet it significantly outperforms sgine implementation.
The main reasons are: sgine Matrix using direct buffer, and tracking
updates to the Matrix.
Conclusion: making new instances on new operations are small potatoes
compares to backing with direct buffers and tracking changes. If the
performance is your main concern - upgrade! You will get an easy to
use math library that can help you write strightforward code. Mangling
your garbage objects manually will not net you much performance, but
will make your code bugprone, hard to read, and hard to maintain.
Moreover, the fastest possible math is when you inline it. And you
will end up doing it for heavily used methods such as
transformVertexBuffer(matrix, buffer) regardless of what math package
you use.
Benchmark code:
{{{
// Place both sgine.jar and simplex3d-math.jar on the classpath.
package bench
import java.nio._
import simplex3d.math._
import simplex3d.math.doublem.renamed._
import simplex3d.math.doublem.DoubleMath._
import org.sgine.math.mutable._
/**
* @author Aleksey Nikiforov (lex)
*/
object MatBench {
def main(args: Array[String]) {
val bench = new MatBench()
bench.run
bench.run
bench.run
}
}
class MatBench {
val length = 10000
val loops = 1000
def run() {
var start = 0L
//WARNING! Running the test with the following line uncommented
will crash your computer.
//testSgineCrash(length, loops)
System.gc()
start = System.currentTimeMillis
testTransformSgine(length, loops)
val sgineTransform = System.currentTimeMillis - start
System.gc()
start = System.currentTimeMillis
testTransformSimplex(length, loops)
val simplexTransform = System.currentTimeMillis - start
System.gc()
start = System.currentTimeMillis
testSimulatedTransformSimplex(length, loops)
val simplexSimTransform = System.currentTimeMillis - start
println()
println("Sgine transform time: " + sgineTransform + ".")
println("Simplex transform time: " + simplexTransform + ".")
println("Simplex simulated transform time: " + simplexSimTransform
+ ".")
println()
}
// This seemingly harmless code will consume all the available
memory.
def testSgineCrash(length: Int, loops: Int) {
val m = Matrix4(
0.1, 0.4, 0.7, 0.3,
0.6, 0.3, 0.6, 0.8,
0.9, 0.2, 0.3, 0.7,
0, 0, 0, 1
)
var l = 0; while (l < loops) {
var i = 0; while (i < length) {
m mult Matrix4(
i + 0, i + 1, i + 2, i + 3,
i + 4, i + 5, i + 6, i + 7,
i + 8, i + 9, i + 10, i + 11,
i + 12, i + 13, i + 14, i + 15
)
i += 1
}
l += 1
}
println(m)
}
def testTransformSgine(length: Int, loops: Int) {
val m = Matrix4(
0.1, 0.4, 0.7, 0.3,
0.6, 0.3, 0.6, 0.8,
0.9, 0.2, 0.3, 0.7,
0, 0, 0, 1
)
var l = 0; while (l < loops) {
var i = 0; while (i < length) {
m rotate(x = i) translate(l, i + 1, i + 2) scaleAll(1.00001)
i += 1
}
l += 1
}
println(m)
}
def testTransformSimplex(length: Int, loops: Int) {
var m = Mat3x4(
0.1, 0.4, 0.7, 0.3,
0.6, 0.3, 0.6, 0.8,
0.9, 0.2, 0.3, 0.7
)
var l = 0; while (l < loops) {
var i = 0; while (i < length) {
m = m rotateX(i) translate(Vec3(l, i + 1, i + 2))
scale(1.00001)
i += 1
}
l += 1
}
println(m)
}
// A comparision with unoptimised multiplication of Mat4x4, to be
fair.
def testSimulatedTransformSimplex(length: Int, loops: Int) {
var m = Mat4(
0.1, 0.4, 0.7, 0,
0.6, 0.3, 0.6, 0,
0.9, 0.2, 0.3, 0,
0.3, 0.8, 0.7, 1
)
var l = 0; while (l < loops) {
var i = 0; while (i < length) {
val s = Mat4(1.00001)
s.m33 = 1
val t = Mat4(1)
t(3) = Vec3(l, i + 1, i + 2)*1.00001
m = s * t * Mat4(rotationMat(i, Vec3.UnitX)) * m
i += 1
}
l += 1
}
println(m)
}
}
}}}
On Jun 21, 6:20 pm, "Hicks, Matt" <
mhi...@captiveimagination.com>
wrote:
> Thanks Lex!
>
> Here are a few items of concern for me:
>
> 1. Most of the code seems focused around instantiation of new objects
> instead of mutability of existing objects. I'm concerned about the
> performance impact of this in relation to GC'ing for larger applications.
> In Java 7 you said your code is optimized for escape analysis which is
> awesome, but I'm concerned about people that are not and will not be running
> Java 7 in the future.
> 2. The matrix classes do not back against a direct buffer, correct? That
> is one of the features I find very nice in Sgine currently is that I can
> simply reference matrix.buffer to pass to OpenGL. I like the idea of all
> objects being backed by a direct buffer...what are your thoughts on this?
> 3. All mutable classes in Sgine currently support the Modifiable trait
> >
http://code.google.com/p/simplex3d/downloads/detail?name=simplex3d-ma...