Binglong's space

Random notes on computer, phone, life, anything

Posts Tagged ‘matrix’

Apple SIMD Vector and Matrix

Posted by binglongx on February 5, 2025

Include

Include for both C and C++ users:

#include <simd/simd.h>

C Users

Vector Types

C vector type example (see <simd/vector_types.h>):

simd_float3     // 3x1 column vector of float elements

If you look further, the vector is a Clang/GCC extension for SIMD architecture like AVX/Neon/OpenCL:

/*! @abstract A vector of three 32-bit floating-point numbers.
 *  @description In C++ and Metal, this type is also available as
 *  simd::float3. Note that vectors of this type are padded to have the same
 *  size and alignment as simd_float4.                                        */
typedef __attribute__((__ext_vector_type__(3))) float simd_float3;

You can regard it as an array of >= N elements that would fit into a SIMD wide register. 

The compiler has built-in support for:

  • Basic arithmetic operators for lanewise operations (vector-vector, and vector-scalar);
  • [i] to access i-th element;
  • .x.y.z.w , .xy etc. to access the elements.

Matrix Types

C matrix type example (see <simd/types.h>):

simd_float3x3  // 3x3 matrix of float elements

If you look further, the matrix is simply implemented as an array of columns:

/*! @abstract A matrix with 3 rows and 3 columns.                             */
typedef struct { simd_float3 columns[3]; } simd_float3x3;

Storage wise, the matrix is column major. Conceptually you use it as matrix without needs to care the storage format.

This also means you can access the column vectors easily, like

simd_float3x3 m;
simd_float3 column0 = m.columns[0];
simd_float3& column1 = m.columns[1];

There is no easy way to access a row vector or sub-matrix.

Vector Construction

Examples:

simd_double3 d3{1.0, 2.3, 4.5};                 // direct initialization
simd_float3 f3 = simd_make_float3(1, 2, 3.14f); // helper function
simd_float4 f4 = simd_make_float4(f3, 1);       // helper function

Matrix Construction

Direct initialization in column major:

simd_double3x3 d3x3{{
    {1, 0, 0}, // column 0
    {0,-1, 0}, // column 1
    {0, 0,-1}, // column 2
}};

Initialization with column vectors:

simd_float3x3 f3x3{
    simd_float3{1, 2, 3}, // column 0
    simd_float3{4, 5, 6}, // column 1
    simd_float3{7, 8, 9}, // column 2
};

Or as convenience, initialization with vectors for rows through a helper function:

simd_float3x3 f = simd_matrix_from_rows(
    simd_float3{1, 2, 3}, // row 0
    simd_float3{4, 5, 6}, // row 1
    simd_float3{7, 8, 9}  // row 2
);

Another example to construct a 4×4 transform matrix from 3×3 rotation matrix and 3×1 vector:

simd_float3x3 R;
simd_float3 t{1, 2, 3};
simd_float4x4 T{
    simd_make_float4(R.columns[0], 0), // column 0
    simd_make_float4(R.columns[1], 0), // column 1
    simd_make_float4(R.columns[2], 0), // column 2
    simd_make_float4(t, 1),            // column 3
};

Matrix Operations

Matrix operations can be found in <simd/matrix.h>, for example:

simd_float3 v1{1, 2, 3};
simd_float3x3 m1;
simd_float3 v2 = simd_mul(m1, v1);   // matrix * vector
simd_float3x3 m2;
simd_float3x3 m3 = simd_mul(m1, m2); // matrix * matrix

C++ Users

Vector Types

C++ vector type example (see <simd/vector_types.h>):

simd::float3     // 3x1 column vector of float elements

If you look further, it is just a synonym of the C type:

namespace simd {
    /*! @abstract A vector of three 32-bit floating-point numbers.
     *  @description In C or Objective-C, this type is available as
     *  simd_float3. Vectors of this type are padded to have the same size and
     *  alignment as simd_float4.                                               */
    typedef ::simd_float3 float3;
}

So the compiler has the same built-in support for:

  • Basic arithmetic operators for lanewise operations (vector-vector, and vector-scalar);
  • [i] to access i-th element;
  • .x.y.z.w , .xy etc. to access the elements.

Matrix Types

C++ matrix type example (see <simd/matrix_types.h>):

simd::float3x3  // 3x3 matrix of float elements

If you look further, the matrix type inherits the C matrix type with a few constructors added (but nothing else):

// in namespace simd
struct float3x3 : ::simd_float3x3 {
    float3x3() : ::simd_float3x3((simd_float3x3){0}) { }
    float3x3(float diagonal) : float3x3((float3)diagonal) { }
    float3x3(float3 v) : ::simd_float3x3((simd_float3x3){(float3){v.x,0,0}, (float3){0,v.y,0}, (float3){0,0,v.z}}) { }
    float3x3(float3 c0, float3 c1, float3 c2) : ::simd_float3x3((simd_float3x3){c0, c1, c2}) { }
    float3x3(::simd_float3x3 m) : ::simd_float3x3(m) { }
    float3x3(::simd_quatf q) : ::simd_float3x3(::simd_matrix3x3(q)) { }
};

Compared to the C matrix type, the constructors allow initializing the matrix more creatively: filling 0s, creating diagonal matrix, creating from columns, creating from C matrix, etc.

Because it is a thin wrapper over the C matrix, there is still no easy way to access a row vector or sub-matrix.

Vector Construction

Because the C++ vector is basically the same thing as the C vector type, you can use all the C means to initialize the object. There are also C++ helper functions to make vector objects, if you prefer.

simd::float3 f1{1.0, 2.3, 4.5};                   // direct initialization
simd::float3 f2 = simd_make_float3(1, 2, 3.14f);  // C helper function
simd::float4 f3 = simd_make_float4(f2, 1);        // C helper function
simd::float3 f4 = simd::make_float3(1, 2, 3.14f); // C++ helper function
simd::float4 f5 = simd::make_float4(f2, 1);       // C++ helper function

Matrix Construction

Because matrix is a class, you need to call one of the constructors to create a matrix object. For example, initialize through an intermediate C matrix using direct numbers:

simd::double3x3 d3x3{ simd_double3x3 {{
    {1, 0, 0}, // column 0
    {0,-1, 0}, // column 1
    {0, 0,-1}, // column 2
}} };

Initialization with column vectors:

simd::float3x3 f3x3{
    simd_float3{1, 2, 3},       // column 0: from C float3
    simd::float3{4, 5, 6},      // column 1: from C++ float3
    simd::make_float3(7, 8, 9), // column 2: from C++ helper function
};

If you need to construct from row vectors, call the same C helper function:

simd::float3x3 f3x3 = simd_matrix_from_rows(
    simd::float3{1, 2, 3}, // row 0
    simd::float3{4, 5, 6}, // row 1
    simd::float3{7, 8, 9}  // row 2
);

Matrix Operations

With operator overloading, it’s more pleasant to perform matrix operations in C++. Matrix operations can be found in <simd/matrix.h>, for example:

simd::float3 v1{1, 2, 3};
simd::float3x3 m1;
simd::float3 v2 = m1 * v1;      // matrix * vector
simd::float3x3 m2;
simd::float3x3 m3 = m1 * m2;    // matrix * matrix
simd::float3 v3 = m1 * m2 * v2; // matrix * matrix * vector

Conclusion

Apple SIMD library provides fast and simple vector and matrix operations.

Posted in C++ | Tagged: , , , , , , , , , , , | Leave a Comment »