Alchemy(3)---- An Alchemy speed test

An Alchemy speed test


Following from my previous articles on setting up an Alchemy development environment in Flex Builder 3 and passing/returning objects to/from C++ , I wanted to test some of the claims of the speed increase possible with the use of this tool.

With the particular interest in 3D Flash applications, I wanted to test specific mathematical operations using vectors and matrices, namely cross products, normalisation, rotation matrix calculations and vector transformations.

I had initially aimed to create a mathematical library where these functions can be calculated on native ActionScript objects - for example create a function to calculate the cross product of two vectors. However one aspect of Alchemy that became immediately evident is that the cost of marshaling data through the AS3-C++ API is horrendously expensive. This is quite normal so I guess I was naive to expect good results from this. But to give you an example of how expensive this is, for a simple iterative calculation of the cross product of two vectors followed by a normalisation: if the mathematical functions are performed in C++, ie iteratively calling the Alchemy compiled functions, the result is about 1000 times slower than natively performing the calculations in AS3!

So, my first advice is: limit the number of Alchemy calls!!

Anyway, in this article I’ll concentrate on performing pure C++ speed tests (called from AS3) - so computationally intensive calculations performed in a single Alchemy call - compared to the equivalent pure AS3 speed tests.

The tests performed here concentrate on vector and matrix operations. I’ve therefore created very simple Vector and Matrix classes in C++. The Vector class is used to perform dot product, normalisation and cross product operations as shown below.

Vector3D.h :

 
  1. #ifndef VECTOR3D_H_
  2. #define VECTOR3D_H_
  3.  
  4. #include "AS3.h"
  5.  
  6. class  Vector3D {
  7.  
  8. public  :
  9.   Vector3D();
  10.   Vector3D( double  x,  double  y,  double  z);
  11.   Vector3D( const  AS3_Val& as3Vector);
  12.    virtual  ~Vector3D();
  13.  
  14.    double  dot( const  Vector3D& v)  const ;
  15.   Vector3D cross( const  Vector3D& v)  const ;
  16.    double  modulus()  const ;
  17.   Vector3D normalise()  const ;
  18.  
  19.    void  setX( double  x);
  20.    void  setY( double  y);
  21.    void  setZ( double  z);
  22.  
  23.    double  getX()  const ;
  24.    double  getY()  const ;
  25.    double  getZ()  const ;
  26.  
  27. private  :
  28.    double  _x;
  29.    double  _y;
  30.    double  _z;
  31.  
  32. };
  33.  
  34. #endif /*VECTOR3D_H_*/
  35.  
  36. Vector3D.cpp :
  37.  
  38.  
  39.  
  40. #include "Vector3D.h"
  41. #include 
  42.  
  43. Vector3D::Vector3D() :
  44.   _x(0),
  45.   _y(0),
  46.   _z(0) {
  47. }
  48.  
  49. Vector3D::Vector3D( const  AS3_Val& as3Vector) {
  50.   AS3_ObjectValue(as3Vector,  "x:DoubleType, y:DoubleType, z:DoubleType" , &_x, &_y, &_z);
  51. }
  52.  
  53. Vector3D::Vector3D( double  x,  double  y,  double  z) :
  54.   _x(x),
  55.   _y(y),
  56.   _z(z) {
  57. }
  58.  
  59. Vector3D::~Vector3D() {
  60. }
  61.  
  62. double  Vector3D::dot( const  Vector3D& v)  const  {
  63.    return  v._x*_x + v._y*_y + v._z*_z;
  64. }
  65.  
  66. Vector3D Vector3D::cross( const  Vector3D& v)  const  {
  67.  
  68.   Vector3D result;
  69.   result._x = _y*v._z - _z*v._y;
  70.   result._y = _z*v._x - _x*v._z;
  71.   result._z = _x*v._y - _y*v._x;
  72.  
  73.    return  result;
  74. }
  75.  
  76. double  Vector3D::modulus()  const  {
  77.    return  std::sqrt(_x*_x + _y*_y + _z*_z);
  78. }
  79.  
  80. Vector3D Vector3D::normalise()  const  {
  81.    double  mod = modulus();
  82.    return  Vector3D(_x/mod, _y/mod, _z/mod);
  83. }
  84.  
  85. void  Vector3D::setX( double  x) {
  86.   _x = x;
  87. }
  88.  
  89. void  Vector3D::setY( double  y) {
  90.   _y = y;
  91. }
  92.  
  93. void  Vector3D::setZ( double  z) {
  94.   _z = z;
  95. }
  96.  
  97. double  Vector3D::getX()  const  {
  98.    return  _x;
  99. }
  100.  
  101. double  Vector3D::getY()  const  {
  102.    return  _y;
  103. }
  104.  
  105. double  Vector3D::getZ()  const  {
  106.    return  _z;
  107. }

One point, specific to Alchemy, is in one of the constructors for the Vector3D: the properties are extracted from the passed AS3 Vector3D object, as discussed in my previous article.

The Matrix3D C++ class is as follows.

Matrix3D.h :

 
  1. #ifndef MATRIX3D_H_
  2. #define MATRIX3D_H_
  3.  
  4. #include "Vector3D.h"
  5.  
  6. class  Matrix3D {
  7.  
  8. public  :
  9.   Matrix3D();
  10.    virtual  ~Matrix3D();
  11.  
  12.    void  setRotationX( double  degrees);
  13.    void  setRotationY( double  degrees);
  14.    void  setRotationZ( double  degrees);
  15.  
  16.    void  setIdentity();
  17.  
  18.   Vector3D transformVector( const  Vector3D& vector)  const ;
  19.  
  20. private  :
  21.    double  _M00;
  22.    double  _M01;
  23.    double  _M02;
  24.    double  _M10;
  25.    double  _M11;
  26.    double  _M12;
  27.    double  _M20;
  28.    double  _M21;
  29.    double  _M22;
  30.  
  31. };
  32.  
  33. #endif /*MATRIX3D_H_*/
  34.  
  35. Matrix3D.cpp :
  36.  
  37.  
  38.  
  39. #include "Matrix3D.h"
  40. #include 
  41.  
  42. Matrix3D::Matrix3D() :
  43.   _M00(1),
  44.   _M01(0),
  45.   _M02(0),
  46.   _M10(0),
  47.   _M11(1),
  48.   _M12(0),
  49.   _M20(0),
  50.   _M21(0),
  51.   _M22(1) {
  52. }
  53.  
  54. Matrix3D::~Matrix3D() {
  55. }
  56.  
  57. void  Matrix3D::setIdentity() {
  58.   _M00 = 1;
  59.   _M01 = 0;
  60.   _M02 = 0;
  61.   _M10 = 0;
  62.   _M11 = 1;
  63.   _M12 = 0;
  64.   _M20 = 0;
  65.   _M21 = 0;
  66.   _M22 = 1;
  67. }
  68.  
  69. void  Matrix3D::setRotationX( double  degrees) {
  70.   setIdentity();
  71.    double  radians = degrees / 180 * M_PI;
  72.  
  73.   _M11 = cos(radians);
  74.   _M12 = -sin(radians);
  75.   _M21 = sin(radians);
  76.   _M22 = cos(radians);
  77. }
  78.  
  79. void  Matrix3D::setRotationY( double  degrees) {
  80.   setIdentity();
  81.    double  radians = degrees / 180 * M_PI;
  82.  
  83.   _M00 = cos(radians);
  84.   _M02 = sin(radians);
  85.   _M20 = -sin(radians);
  86.   _M22 = cos(radians);
  87. }
  88.  
  89. void  Matrix3D::setRotationZ( double  degrees) {
  90.   setIdentity();
  91.    double  radians = degrees / 180 * M_PI;
  92.  
  93.   _M00 = cos(radians);
  94.   _M01 = -sin(radians);
  95.   _M10 = sin(radians);
  96.   _M11 = cos(radians);
  97. }
  98.  
  99. Vector3D Matrix3D::transformVector( const  Vector3D& vector)  const  {
  100.   Vector3D result;
  101.  
  102.   result.setX(_M00*vector.getX() + _M01*vector.getY() + _M02*vector.getZ());
  103.   result.setY(_M10*vector.getX() + _M11*vector.getY() + _M12*vector.getZ());
  104.   result.setZ(_M20*vector.getX() + _M21*vector.getY() + _M22*vector.getZ());
  105.  
  106.    return  result;
  107. }

One of the objectives of using the Matrix3D class is to test the performance of the trigonometric functions. A common source of intensive calculations in 3D graphics is the rotation of vectors so this provides a useful test directly aimed at this field.

Two tests are to be examined: one for cross product calculations and another for matrix transformations. These are defined in the main.cpp file.

 
  1. #include "AS3.h"
  2. #include "Vector3D.h"
  3. #include "Matrix3D.h"
  4.  
  5. AS3_Val speedTest1( void * self, AS3_Val args) {
  6.  
  7.    // Declare AS3 variables
  8.   AS3_Val as3Vector1;
  9.   AS3_Val as3Vector2;
  10.  
  11.    // Extract variables from arguments array
  12.   AS3_ArrayValue(args,  "AS3ValType, AS3ValType" , &as3Vector1, &as3Vector2);
  13.  
  14.    // Create native C++ objects with AS3 parameters
  15.   Vector3D vector1(as3Vector1);
  16.   Vector3D vector2(as3Vector2);
  17.  
  18.   Vector3D vector3;
  19.        
  20.    // Speed test : calculate cross products and normalise
  21.    for  ( int  i = 0; i < 1000000; i++) {
  22.     vector3 = vector1.cross(vector2);
  23.     vector3 = vector3.normalise();
  24.     vector1 = vector2;
  25.     vector2 = vector3;
  26.   }
  27.  
  28.    // Obtain a class descriptor for the AS3 Vector3D class
  29.   AS3_Val vector3DClass = AS3_NSGet(AS3_String( "flash.geom" ), AS3_String( "Vector3D" ));
  30.   AS3_Val  params  = AS3_Array( "" );
  31.  
  32.    // Construct a new AS3 Vector3D object with empty parameters
  33.   AS3_Val result = AS3_New(vector3DClass,  params );
  34.  
  35.    // Set the x, y and z properties of the AS3 Vector3D object, casting as appropriate
  36.   AS3_Set(result, AS3_String( "x" ), AS3_Number(vector3.getX()));
  37.   AS3_Set(result, AS3_String( "y" ), AS3_Number(vector3.getY()));
  38.   AS3_Set(result, AS3_String( "z" ), AS3_Number(vector3.getZ()));
  39.  
  40.    // Release what's no longer needed
  41.   AS3_Release( params );
  42.   AS3_Release(vector3DClass);
  43.  
  44.    // return the AS3 Vector
  45.    return  result;
  46. }
  47.  
  48. AS3_Val speedTest2( void * self, AS3_Val args) {
  49.  
  50.    // Declare AS3 variable
  51.   AS3_Val as3Vector;
  52.  
  53.    // Extract variables from arguments array
  54.   AS3_ArrayValue(args,  "AS3ValType" , &as3Vector);
  55.  
  56.    // Create native C++ object with AS3 parameters
  57.   Vector3D vector(as3Vector);
  58.  
  59.   Vector3D copy = vector;
  60.  
  61.   Matrix3D rotationX;
  62.   Matrix3D rotationY;
  63.   Matrix3D rotationZ;
  64.        
  65.    // Speed test : calculate rotation matrices and transform vector
  66.    for  ( int  i = 0; i < 1000; i++) {
  67.     vector = copy;
  68.      for  ( double  ang = 0; ang < 180; ang++) {
  69.       rotationX.setRotationX(ang);
  70.       rotationY.setRotationY(ang);
  71.       rotationZ.setRotationZ(ang);
  72.      
  73.       vector = rotationX.transformVector(vector);
  74.       vector = rotationY.transformVector(vector);
  75.       vector = rotationZ.transformVector(vector);
  76.     }
  77.   }
  78.  
  79.    // Obtain a class descriptor for the AS3 Vector3D class
  80.   AS3_Val vector3DClass = AS3_NSGet(AS3_String( "flash.geom" ), AS3_String( "Vector3D" ));
  81.   AS3_Val  params  = AS3_Array( "" );
  82.  
  83.    // Construct a new AS3 Vector3D object with empty parameters
  84.   AS3_Val result = AS3_New(vector3DClass,  params );
  85.  
  86.    // Set the x, y and z properties of the AS3 Vector3D object, casting as appropriate
  87.   AS3_Set(result, AS3_String( "x" ), AS3_Number(vector.getX()));
  88.   AS3_Set(result, AS3_String( "y" ), AS3_Number(vector.getY()));
  89.   AS3_Set(result, AS3_String( "z" ), AS3_Number(vector.getZ()));
  90.  
  91.    // Release what's no longer needed
  92.   AS3_Release( params );
  93.   AS3_Release(vector3DClass);
  94.  
  95.    // return the AS3 Vector
  96.    return  result;
  97. }
  98.  
  99. /**
  100.  * Main entry point for Alchemy compiler. Declares all functions available
  101.  * through the Alchemy bridge.
  102.  */
  103. int  main() {
  104.    // Declare all methods exposed to AS3 typed as Function instances
  105.   AS3_Val speedTest1Method = AS3_Function(NULL, speedTest1);
  106.   AS3_Val speedTest2Method = AS3_Function(NULL, speedTest2);
  107.  
  108.    // Construct an object that contains references to all the functions
  109.   AS3_Val result = AS3_Object( "speedTest1:AS3ValType, speedTest2:AS3ValType" , speedTest1Method, speedTest2Method);
  110.  
  111.    // Release what's no longer needed
  112.   AS3_Release(speedTest1Method);
  113.   AS3_Release(speedTest2Method);
  114.  
  115.    // Notify the bridge of what has been created -- THIS DOES NOT RETURN!
  116.   AS3_LibInit(result);
  117.  
  118.    // Should never get here!
  119.    return  0;
  120. }

For an explanation of the code and the C++ API of Alchemy, I’ll refer you to my previous article on passing and returning objects to and from C++ using Alchemy .

The first test, speedTest1 , performs 1,000,000 times the cross product of two vectors (initially passed by AS3) followed by a normalisation. The resulting vector is used in the following iteration. At the end of all the iterations, the final vector is returned to AS3.

The second test, speedTest2 , calculates rotation vectors around the x, y and z axes. A vector (initially passed by AS3), is then rotated by each matrix individually. This is repeated for 180 steps, increasing the angle of rotation by 1 degree at a time. This again is repeated for a total of 1,000 iterations. The final vector is returned to AS3.

Let’s have a look now at the ActionScript class that calls these tests, and the equivalent pure AS3 tests.

 
  1. package  {
  2.  
  3.    import  cmodule.vector.CLibInit;
  4.  
  5.    import   flash .display. Sprite ;
  6.    import   flash .display. StageAlign ;
  7.    import   flash .display. StageScaleMode ;
  8.    import   flash .geom.Matrix3D;
  9.    import   flash .geom.Vector3D;
  10.    import   flash . text . TextField ;
  11.    import   flash . text . TextFieldAutoSize ;
  12.    import   flash .utils. getTimer ;
  13.  
  14.    public   class class  AlchemySpeedTest  extends   Sprite  {
  15.  
  16.      private   var  vectorUtils: Object ;
  17.  
  18.      public   function  AlchemySpeedTest() {
  19.  
  20.        // Set up the stage
  21.        stage . align  =  StageAlign . TOP_LEFT ;
  22.        stage . scaleMode  =  StageScaleMode . NO_SCALE ;
  23.  
  24.        // Create the Alchemy bridge to C++ methods
  25.        var   loader :CLibInit =  new  CLibInit;
  26.       vectorUtils =  loader . init ();
  27.  
  28.        // Create a text field           
  29.        var  timerText: TextField  =  new   TextField ();
  30.       timerText. autoSize  =  TextFieldAutoSize . LEFT ;
  31.        addChild (timerText);
  32.        
  33.        // Initialise a timer
  34.        var  time0: int  =  getTimer ()
  35.  
  36.        // Perform the speed test
  37.        var  vector:Vector3D = speedTest1();
  38.        //var vector:Vector3D = speedTest2();
  39.        //var vector:Vector3D = nativeSpeedTest1();
  40.        //var vector:Vector3D = nativeSpeedTest2();
  41.      
  42.        // Calculate the elapsed time
  43.     
分享到:
评论
iamzealotwang
  • 浏览: 73846 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

你可能感兴趣的:(C++,c,C#,Flash,actionscript)