lab.exec(jsscript)will always be slightly faster than
lab.do(script)Javascript code for any LALOLab script can be easily obtained by clicking the "Javascript code" button in LALOLab. Then, the following guidelines can be applied to further optimize the code.
If your page creates matrices and vectors, then it should create them with the LALOLib functions
zeros(m,n), ones(m,n), rand(m,n)...which will enforce a particular object structure on them for faster subsequent computations.
You can also build matrices with Arrays of Arrays and call
A = mat(anArrayOfArrays, true)to convert them to LALOLib matrices, but this will be slower and will use more memory.
The general purpose functions listed below are fine to use once in a while, but consume time to determine how to perform the operation depending on the type of the arguments. Therefore, it is always faster (and highly recommended inside loops) to use dedicated typed functions as detailed below.
General purpose function | Type of argument a | Type/value of argument b | Equivalent typed function |
---|---|---|---|
add(a,b) | number | vector | addScalarVector(a,b) |
vector | number | addScalarVector(b,a) | |
number | matrix | addScalarMatrix(a,b) | |
matrix | number | addScalarMatrix(b,a) | |
vector | vector | addVectors(a,b) | |
matrix | matrix | addMatrices(a,b) | |
sub(a,b) | number | vector | subScalarVector(a,b) |
vector | number | subVectorScalar(a,b) | |
number | matrix | subScalarMatrix(a,b) | |
matrix | number | subMatrixScalar(a,b) | |
vector | vector | subVectors(a,b) | |
matrix | matrix | subMatrices(a,b) | |
minus(a) | number | -a | |
vector | minusVector(a) | ||
matrix | minusMatrix(a) | ||
mul(a,b) | number | vector | mulScalarVector(a,b) |
number | matrix | mulScalarMatrix(a,b) | |
vector | vector | dot(a,b) | |
matrix | vector | mulMatrixVector(a,b) | |
matrix | matrix | mulMatrixMatrix(a,b) | |
entrywisemul(a,b) | vector | vector | entrywisemulVector(a,b) |
matrix | matrix | entrywisemulMatrix(a,b) | |
entrywisediv(a,b) | vector | number | divVectorScalar(a,b) |
number | vector | divScalarVector(a,b) | |
matrix | number | divMatrixScalar(a,b) | |
number | matrix | divScalarMatrix(a,b) | |
vector | vector | divVectors(a,b) | |
matrix | matrix | divMatrices(a,b) | |
outerprod(a,b) | vector | vector | outerprodVectors(a,b) |
sum(a,b) | vector | undefined or b=1 | sumVector(a) |
matrix | undefined | sumMatrix(a) | |
matrix | b=1 | sumMatrixRows(a) | |
matrix | b=2 | sumMatrixCols(a) | |
prod(a,b) | vector | undefined or b=1 | prodVector(a) |
matrix | undefined | prodMatrix(a) | |
matrix | b=1 | prodMatrixRows(a) | |
matrix | b=2 | prodMatrixCols(a) | |
min(a,b) | vector | minVector(a) | |
(or max) | matrix | minMatrix(a) | |
vector | number | minVectorScalar(a,b) | |
number | vector | minVectorScalar(b,a) | |
vector | vector | minVectorVector(b,a) | |
matrix | b=1 | minMatrixRows(a) | |
matrix | b=2 | minMatrixCols(a) | |
matrix | number | minMatrixScalar(a,b) | |
number | matrix | minMatrixScalar(b,a) | |
number | matrix | minMatrixMatrix(a,b) | |
transpose(a) | vector | transposeVector(a) | |
matrix | transposeMatrix(a) | ||
get(a) | vector | vectorCopy(a) | |
matrix | matrixCopy(a) | ||
get(a,b) | vector | number | a[b] |
vector | vector/Array | getSubVector(a,b) | |
get(a,b,c) | matrix | b: number, c=[] | getRows(a,[b]) |
matrix | b: vector/Array, c=[] | getRows(a,b) | |
matrix | b=[], c: number | getCols(a,[c]) | |
matrix | b=[], c: vector/Array | getCols(a,c) | |
matrix | b: vector/Array, c: vector/Array | getSubMatrix(a,b,c) | |
randn(), randn(1), randn(1,1) | randnScalar() | ||
Math functions | number | e.g., Math.exp(a) | e.g., exp(a)... | vector | e.g., expVector(a) |
matrix | e.g., expMatrix(a) |
The size of a vector x can be directly accessed as
x.length // vector dimensionThen, the element xi can be accessed with
x[i]
The size of a matrix A can be directly accessed as
A.length // number of rows A.m // number of rows (= A.length) A.n // number of columnsThen, the element Aij can be accessed with
A.val[i*A.n + j]
for(i = 0; i < X.length; i++) { Xi = getRows(X, [i]); ... work with Xi ... }With a call to get (or getRows) the rows are copied into Xi. But if work on Xi only involves reading its entries, this can be made faster by using X.row(i) as
for(i = 0; i < X.length; i++) { Xi = X.row(i); ... work with Xi ... }which provides a pointer-like reference to the ith row of X without copying the data. However, remember that in this case any modification to Xi also applies to X.
u = getSubVector(x, range(0, n) ); // equivalent to u = get(x, range(0, n) )by copying these entries into u. If the work with u only involves reading its entries, then we can instead get a pointer-like reference to the subvector as
u = x.subarray(0, n);without copying the data.
With LALOLib, there is no obvious need for loop hunting and vectorization.
For instance, in Matlab,x = (a(2:N) - a(1:N-1)) .* bis typically faster than
for i=1:N-1 x(i) = (a(i+1) - a(i)) * b(i) endBut with LALOLib,
for (i=0; i < N-1; i++) { x[i] = (a[i+1] - a[i]) * b[i] ; }is probably as fast as
x = entrywisemulVectors( subVectors( a.subarray(1,N), a.subarray(0,N-1) ) , b);which is faster than
x = entrywisemulVectors( subVectors( getSubVectors(a, range(1,N)), getSubVectors(a, range(0,N-1)) ), b);The reason is that linear algebra functions like subVectors end up being implemented as javascript loops in the flavor of the one above.
However, using the LALOLib functions can still be faster in some cases where the loop does not get optimized by the browser for one of the reasons detailed at the bottom of this page.
Memory cannot be explicitly freed in Javascript, which instead relies on an uncontroled garbage collector. This garbage collector periodically scans all variables and objects to detects the ones that can be destroyed. The more objects (or variables) you create, the longer the scans take, and eventually even the smallest computations can become very slow.
A typical example of this occurs when you create an m-x-n matrix with many rows as an Array of Arrays instead of using zeros(m,n):
A = new Array(m); for ( i=0; i < m; i++) A[i] = new Array(n); // or new Float64Array(n) or zeros(n)In this example, m+1 objects are created versus a single one when using A = zeros(m,n).
Another common mistake is to create global variables for temporary use. Any variable used temporarily in a function should be a local one in order to be freed from memory when the function returns. Remember that in javascript, local variables are declared with the keyword var. Undeclared variables are always global.
All basic linear algebra functions in LALOLib create a new Vector/Matrix to store their result, which makes them easy to use and matlab-like. However, inside a loop this can generate a lot of unnecessary memory allocations for temporary variables.
Consider for instance the following iterations of y = y + a x with vectors x, y and scalar a:
for (i=0; i < n; i++) { y = addVectors(y, mulScalarVector(a, x)); }At every iteration, a new vector is created to store the result of mulScalarVector and another one for the result of addVectors. Furthermore, these two get collected as garbage in the next iteration. Instead, it is more efficient to use the in-place operation for y = y + a x (saxpy):
for (i=0; i < n; i++) { saxpy(a, x, y); }
Similarly, the gaxpy function provides the in-place operation for computing y = y + A x with vectors x, y and a Matrix A as gaxpy(A,x,y).
Consider a loop in which a vector y is initialized to x before doing some work:
for (i=0; i < n; i++) { y = vectorCopy(x); ... work on y ... }The vectorCopy function actually creates an new Object at each iteration, which becomes garbage at the next one. A more efficient approach is to create a single vector before the loop and only copy the content of x into y:
y = zeros(x.length); for (i=0; i < n; i++) { y.set(x); // or vectorCopyInto(x,y) ... work on y ... }
For matrices, the content of X can be copied into Y without additional memory allocations with
Y.val.set(X.val);