Performance of Coral vs Vector3D and Matrix3D
This is part 3 of a 4 part series comparing Coral, a derivative of the 3D math classes that used to be in the Flint Particles project, and Flash's native 3D math classes.
In the first post I introduced Coral, explaining what it is and why I'm releasing it as an open source project. The next post looked at the key architectural differences between Coral and the native Vector3D and Matrix3D classes. In a later post I will show you the bugs I discovered in the native 3D math classes. This post is about performance.
Coral is covered by a set of performance tests for measuring and optimizing it's performance. These tests are run using Grant Skinner's performance test suite. I adapted these tests to also apply to the native 3D geometry classes, for comparison. All the test code is in the Coral project on Github.
I ran these tests on my laptop, a MacBook with a 2.4GHz Core2Duo cpu, with the release version of Flash Player 10.2.152.26. All tests were over 1,000,000 iterations and each test is run four times and an average taken.
Vector3d performance tests
Coral Vector3d Performance tests. | Native Vector3D Performance tests. | |||
Description | Time (ms) | Description | Time (ms) | % diff |
new Vector3d() | 154.5 | new Vector3D() | 178 | 87% |
new Vector3d( x, y, z ) | 156 | new Vector3D( x, y, z ) | 173.5 | 90% |
Vector3d.reset( x, y, z ) | 21.75 | v = new Vector3D( x, y, z ) | 173.5 | 13% |
Vector3d.assign( v ) | 17.25 | u = v.clone() | 183.75 | 9% |
Vector3d.clone() | 179.75 | Vector3D.clone() | 183.75 | 98% |
Vector3d.add( v ) | 171 | Vector3D.add( v ) | 183.25 | 93% |
Vector3d.add( v, r ) | 13.5 | Vector3D.add( v ) | 183.25 | 7% |
Vector3d.subtract( v ) | 176.25 | Vector3D.subtract( v ) | 184.25 | 96% |
Vector3d.subtract( v, r ) | 13.75 | Vector3D.subtract( v ) | 184.25 | 7% |
Vector3d.multiply( s ) | 205.5 | Vector3D.clone().scaleBy( s ) | 191.75 | 107% |
Vector3d.multiply( s, r ) | 13 | Vector3D.clone().scaleBy( s ) | 191.75 | 7% |
Vector3d.divide( s ) | 207 | Vector3D.clone().scaleBy( 1 / s ) | 199.75 | 104% |
Vector3d.divide( s, r ) | 21.75 | Vector3D.clone().scaleBy( 1 / s ) | 199.75 | 11% |
Vector3d.incrementBy( v ) | 11.75 | Vector3D.incrementBy( v ) | 11 | 107% |
Vector3d.decrementBy( v ) | 12 | Vector3D.decrementBy( v ) | 12 | 100% |
Vector3d.scaleBy( s ) | 9.75 | Vector3D.scaleBy( s ) | 10 | 98% |
Vector3d.divideBy( s ) | 11 | Vector3D.scaleBy( 1 / s ) | 12.5 | 88% |
Vector3d.equals( v ) true | 16.25 | Vector3D.equals( v ) true | 16.25 | 100% |
Vector3d.equals( v ) false | 12.75 | Vector3D.equals( v ) false | 13.25 | 96% |
Vector3d.nearEquals( v ) true | 13 | Vector3D.nearEquals( v ) true | 20 | 65% |
Vector3d.nearEquals( v ) false | 13 | Vector3D.nearEquals( v ) false | 13.5 | 96% |
Vector3d.dotProduct( v ) | 12.5 | Vector3D.dotProduct( v ) | 12 | 104% |
Vector3d.crossProduct( v ) | 178 | Vector3D.crossProduct( v ) | 186.25 | 96% |
Vector3d.crossProduct( v, r ) | 18.5 | Vector3D.crossProduct( v ) | 186.25 | 10% |
Vector3d.length | 32.75 | Vector3D.length | 35.25 | 93% |
Vector3d.lengthSquared | 9.25 | Vector3D.lengthSquared | 9 | 103% |
Vector3d.negative() | 172 | Vector3D.clone().negate() | 192.75 | 89% |
Vector3d.negative( r ) | 13.5 | Vector3D.clone().negate() | 192.75 | 7% |
Vector3d.negate() | 10.75 | Vector3D.negate() | 11 | 98% |
Vector3d.normalize() | 47 | Vector3D.normalize() | 45.75 | 103% |
Vector3d.unit() | 222.75 | Vector3D.clone().normalize() | 240.75 | 93% |
Vector3d.unit( r ) | 41.25 | Vector3D.clone().normalize() | 240.75 | 17% |
Comparing Coral's Vector3d class to the native Vector3D class, the first thing I notice is that the majority of methods show insignificant differences. Those methods where significant differences occur are situations where Coral avoids creation of a temporary object. Thus, Coral is marginally faster when adding two vectors to produce a third new vector, but is very much quicker when it's allowed to use an existing vector to hold the result - something which the native vector class can't do.
Point3d performance tests
Coral Point3d Performance tests. | Native Vector3D Performance tests. | |||
Description | Time (ms) | Description | Time (ms) | % diff |
new Point3d() | 155 | new Vector3D() | 178 | 87% |
new Point3d( x, y, z ) | 164 | new Vector3D( x, y, z ) | 173.5 | 95% |
Point3d.reset( x, y, z ) | 20.75 | v = new Vector3D( x, y, z ) | 173.5 | 12% |
Point3d.assign( v ) | 12.5 | u = Vector3D.clone() | 183.75 | 7% |
Point3d.clone() | 166.75 | Vector3D.clone() | 183.75 | 91% |
Point3d.add( v ) | 173.5 | Vector3D.add( v ) | 183.25 | 95% |
Point3d.add( v, r ) | 13.25 | Vector3D.add( v ) | 183.25 | 7% |
Point3d.subtract( v ) | 170.75 | Vector3D.subtract( v ) | 184.25 | 93% |
Point3d.subtract( v, r ) | 14 | Vector3D.subtract( v ) | 184.25 | 8% |
Point3d.incrementBy( v ) | 11.75 | Vector3D.incrementBy( v ) | 11 | 107% |
Point3d.decrementBy( v ) | 10.75 | Vector3D.decrementBy( v ) | 12 | 90% |
Point3d.equals( v ) true | 15.25 | Vector3D.equals( v ) true | 16.25 | 94% |
Point3d.equals( v ) false | 12 | Vector3D.equals( v ) false | 13.25 | 91% |
Point3d.nearTo( p ) true | 13.75 | Vector3D.nearEquals( v ) true | 20 | 69% |
Point3d.nearTo( p ) false | 13.75 | Vector3D.nearEquals( v ) false | 13.5 | 102% |
Point3d.distance( v ) | 37 | Vector3D.subtract( v ).length | 214.75 | 17% |
Point3d.distanceSquared( v ) | 12 | Vector3D.subtract( v ).lengthSquared | 194.25 | 6% |
Point3d.project() | 15.75 | Vector3D.project() | 16.25 | 97% |
As with Coral's Vector3d class, the Point3d class has comparable (usually marginally faster) performance where functionality is identical, but is very much faster where it is able to avoid creating a temporary object.
Matrix3d performance tests
Coral Matrix3d Performance tests. | Native Matrix3D Performance tests. | |||
Description | Time (ms) | Description | Time (ms) | % diff |
new Matrix3d() | 185 | new Matrix3D() | 242.75 | 76% |
new Matrix3d( n11, n12, ... ) | 212 | new Matrix3D( v ) | 255.5 | 83% |
Matrix3d.clone() | 218.75 | Matrix3D.clone() | 637.75 | 34% |
Matrix3d.rawData | 536.75 | Matrix3D.rawData | 312.5 | 172% |
Matrix3d.rawData = v | 120.75 | Matrix3D.rawData = v | 54.5 | 222% |
Matrix3d.assign( m ) | 16.75 | m1.rawData = m2.rawData | 729.25 | 2% |
Matrix3d.newScale( x, y, z ) | 208.25 | new Matrix3D().appendScale( x, y, z ) | 314.5 | 66% |
Matrix3d.newTranslation( x, y, z ) | 209.75 | new Matrix3D().appendTranslation( x, y, z ) | 253.75 | 83% |
Matrix3d.newRotation( angle, axis, point ) | 387.25 | new Matrix3D().appendRotation( angle, axis, point ) | 502.5 | 77% |
Matrix3d.newBasisTransform( v1, v2, v3 ) | 266.75 | - | - | |
Matrix3d.append( m ) | 68.5 | Matrix3D.append( m ) | 65.75 | 104% |
Matrix3d.appendScale( x, y, z ) | 14.75 | Matrix3D.appendScale( x, y, z ) | 83.25 | 18% |
Matrix3d.appendTranslation( x, y, z ) | 25.5 | Matrix3D.appendTranslation( x, y, z ) | 21.25 | 120% |
Matrix3d.appendRotation( angle, axis, point ) | 263.25 | Matrix3D.appendRotation( angle, axis, point ) | 273.25 | 96% |
Matrix3d.appendBasisTransform( v1, v2, v3 ) | 104.75 | - | - | |
Matrix3d.prepend( m ) | 76.5 | Matrix3D.prepend( m ) | 65.75 | 116% |
Matrix3d.prependScale( x, y, z ) | 14.75 | Matrix3D.prependScale( x, y, z ) | 171.5 | 9% |
Matrix3d.prependTranslation( x, y, z ) | 22.25 | Matrix3D.prependTranslation( x, y, z ) | 89.5 | 25% |
Matrix3d.prependRotation( angle, axis, point ) | 278.25 | Matrix3D.prependRotation( angle, axis, point ) | 273.25 | 102% |
Matrix3d.prependBasisTransform( v1, v2, v3 ) | 103.5 | - | - | |
Matrix3d.determinant | 30.75 | Matrix3D.determinant | 84 | 37% |
Matrix3d.invert() | 138.5 | Matrix3D.invert() | 150.5 | 92% |
Matrix3d.inverse() | 315.75 | Matrix3D.clone().invert() | 821.5 | 38% |
Matrix3d.inverse( r ) | 127.75 | Matrix3D.clone().invert() | 821.5 | 16% |
Matrix3d.transformVector( v ) | 187.75 | Matrix3D.deltaTransformVector( v ) | 542 | 35% |
Matrix3d.transformVector( v, r ) | 23.75 | Matrix3D.deltaTransformVector( v ) | 542 | 4% |
Matrix3d.transformVectorSelf( v ) | 19.5 | Matrix3D.deltaTransformVector( v ) | 542 | 4% |
Matrix3d.transformPoint( v ) | 186.5 | Matrix3D.transformVector( v ) | 537.5 | 35% |
Matrix3d.transformPoint( v, r ) | 23.5 | Matrix3D.transformVector( v ) | 537.5 | 4% |
Matrix3d.transformPointSelf( v ) | 19.5 | Matrix3D.transformVector( v ) | 537.5 | 4% |
Matrix3d.transformVectors( v )* | 903 | - | - | |
Matrix3d.transformVectors( v, r )* | 182 | - | - | |
Matrix3d.transformVectorsSelf( v )* | 184 | - | - | |
Matrix3d.transformPoints( v )* | 904.5 | Matrix3D.transformVectors( v, r )* | 129.75 | 697% |
Matrix3d.transformPoints( v, r )* | 183 | Matrix3D.transformVectors( v, r )* | 129.75 | 141% |
Matrix3d.transformPointsSelf( v )* | 184.5 | Matrix3D.transformVectors( v, r )* | 129.75 | 142% |
* collection being transformed contained 30 vectors/points
The results of the matrix tests are more diverse than the vector and point tests. Creating transformation matrices using 'new' plus the various append/prepend transformation methods will usually be quicker with Coral than with the native classes. In some cases (e.g. prependScale) the difference is very large.
However, it could be argued that this is rarely important. The area where real speed is needed is when performing transformations on vectors and points. Here the performance profiles differ. Coral is very much faster when performing transformations on individual vectors and points, where it can produce a 25x speed improvement by avoiding creating temporary objects.
However, if you bundle all your points into a Vector.<Number> collection, the native Matrix3D class outperforms everything else.
Next
That's all for performance. Overall I have found Coral to be a lot more performant than the native 3D math classes. In the final post in this series I will look at the bugs in Flash's native 3D classes that were revealed by applying Coral's unit tests to the native 3D classes.
Footnote
The new Flash Player 11 Incubator pre-release became available on 28 February. This pre-release is only available as a debug version, so results from it are not comparable to the release players. Even so, I ran the tests out of interest and it looks like Matrix3D.transformVectors() has got a lot faster. There is nothing else of significance to draw from the debug version - we'll have to wait for a release version to land to find out more.
Also in the collection Coral, an Actionscript library for 3D Math