Optimizing

This page summarizes the best coding strategies for efficient code. While MatrixLib has been designed to provide efficiency independent of the user, there are nonetheless strategies that may be employed to improve the performance of code dependent on matrix operations. The following is a list, with explanation, of the most important of these guidelines.

  1. Use get() to access matrix elements whenever possible. Only use the parentheses operator, (), when you must set a value. Since ml_matrix::get() is implemented inline, it doesn't result in function call overhead; It also does not need to consider resizing. This results in a considerable increase in speed.

  2. Resizing matrices is a slow operation. If you can, ensure that a matrix is the proper size initially so that resizing can be avoided. This is especially true in the case of repeated stack() or append() calls - by creating a destination matrix of the appropriate size and then using ml_matrix::inc_rows() or ml_matrix::inc_cols() better performance can be achieved.

  3. Use the combined *=, +=, /=, etc., operators whenever possible. While this results in code that looks unusual, it results in fewer temporary variables and therefore a speed increase. For instance, to add A + B + C where all are matrices, you might write ml_matrix D = A + B + C; However, the faster way to write this is: ml_matrix D(A); (D+=B)+=C; The first line uses the copy constructor to create D from A, which is faster than first creating a D matrix and then setting it equal to A (ml_matrix D = A). The second line first adds B to D, then adds C to D. This version adds one line of code, but avoids creating the two invisible temporary matrices (one for A+B and one for the addition of C to the first temporary) created in the first example. No temporary variables are needed, and there are exactly the same number of arithmetic operations.

  4. Nearly all non-member functions in MatrixLib are implemented in terms of their member function equivalents. As a result, it is almost invariably faster to use the member functions when possible, as their use results in the avoidance of a second function call.

  5. Avoid unnecessary type conversions. MatrixLib works with doubles. Do the same with anything that interacts with MatrixLib to avoid int-to-double promotions during computation. Numbers that don't interact, such as index variables which aren't added, subtracted, or otherwise operated upon, can be integers. However, numbers used in any formula should be doubles. For example, if you are tripling the elements of a matrix, be sure to multiply by 3.0 instead of 3.

  6. If you're iterating through a matrix, iterate in the direction that the matrix is stored in. If the ML_COLUMN_MAJOR macro is defined, have the outer loop iterate columns and the inner loop iterate rows: that is, go from (1,1) to (X,1), then from (1,2) to (X,2), and so on. If the macro is not defined, do the opposite: go from (1,1) to (1,X), then from (2,1) to (2,X), and so on. MatrixLib's internal storage order is variable and is based on the configuration options given at the time your library was compiled; the ML_COLUMN_MAJOR macro will allow user code to determine this.

  7. While the ml_int_array class provides significant convenience to the user, it can also impact performance if used heavily. Especially when working in tight loops that require arrays of matrix indices, it can be much more efficient to work with standard C arrays. If the functionality of ml_int_array remains desired but the array is invariant over a loop, the ml_int_array::get_c_arr() function can be used to get the C array version of a ml_int_array outside of the loop, rather than forcing MatrixLib to extract it during each iteration.