Wiki

Clone wiki

blaze / Intra-Statement Optimization


One of the prime features of the Blaze library is the automatic intra-statement optimization. In order to optimize the overall performance of every single statement Blaze attempts to rearrange the operands based on their types. For instance, the following addition of dense and sparse vectors

blaze::DynamicVector<double> d1, d2, d3;
blaze::CompressedVector<double> s1;

// ... Resizing and initialization

d3 = d1 + s1 + d2;

is automatically rearranged and evaluated as

// ...
d3 = d1 + d2 + s1;  // <- Note that s1 and d2 have been rearranged

This order of operands is highly favorable for the overall performance since the addition of the two dense vectors d1 and d2 can be handled much more efficiently in a vectorized fashion.

This intra-statement optimization can have a tremendous effect on the performance of a statement. Consider for instance the following computation:

blaze::DynamicMatrix<double> A, B;
blaze::DynamicVector<double> x, y;

// ... Resizing and initialization

y = A * B * x;

Since multiplications are evaluated from left to right, this statement would result in a matrix/matrix multiplication, followed by a matrix/vector multiplication. However, if the right subexpression is evaluated first, the performance can be dramatically improved since the matrix/matrix multiplication can be avoided in favor of a second matrix/vector multiplication. The Blaze library exploits this by automatically restructuring the expression such that the right multiplication is evaluated first:

// ...
y = A * ( B * x );

Note however that although this intra-statement optimization may result in a measurable or even significant performance improvement, this behavior may be undesirable for several reasons, for instance because of numerical stability. Therefore, in case the order of evaluation matters, the best solution is to be explicit and to separate a statement into several statements:

blaze::DynamicVector<double> d1, d2, d3;
blaze::CompressedVector<double> s1;

// ... Resizing and initialization

d3  = d1 + s1;  // Compute the dense vector/sparse vector addition first ...
d3 += d2;       // ... and afterwards add the second dense vector
blaze::DynamicMatrix<double> A, B, C;
blaze::DynamicVector<double> x, y;

// ... Resizing and initialization

C = A * B;  // Compute the left-hand side matrix-matrix multiplication first ...
y = C * x;  // ... before the right-hand side matrix-vector multiplication

Alternatively, it is also possible to use the eval() function to fix the order of evaluation:

blaze::DynamicVector<double> d1, d2, d3;
blaze::CompressedVector<double> s1;

// ... Resizing and initialization

d3 = d1 + eval( s1 + d2 );
blaze::DynamicMatrix<double> A, B;
blaze::DynamicVector<double> x, y;

// ... Resizing and initialization

y = eval( A * B ) * x;

Previous: Block Vectors and Matrices ---- Next: Frequently Asked Questions (FAQ)

Updated