Skip to content

Commit

Permalink
improvement in multiply algo + docs
Browse files Browse the repository at this point in the history
  • Loading branch information
doleron committed Jul 1, 2020
1 parent 6b874a1 commit b938e04
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 32 deletions.
34 changes: 21 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@

A faster Matrix for the Java Script World.

There are some awesome matrix libraries for Java Script in the wild.
There are awesome matrix libraries for Java Script in the wild.
But if you are looking for something faster for numeric computing, consider using this library.

## Benchmarks

Some Matrix multiplication benchmarks:

| | 2x2 | 3x3 | 4x4 | 16x16 | 32x32 | 256x256 | 512x512 |
|----------------|:---------:|:---------:|:-------:|:------:|:-----:|:-------:|:-------:|
| Matrix-reef JS | 1,787,425 | 1,557,114 | 869,244 | 45,497 | 6,312 | 16.06 | 1.92 |
| ml-Matrix | 188,606 | 156,872 | 127,508 | 15,417 | 5,319 | 15.76 | 1.89 |
| Math.js | 129,336 | 101,767 | 63,528 | 3,574 | 553 | 0.92 | 0.08 |
| | 2x2 | 3x3 | 4x4 | 16x16 | 32x32 | 256x256 | 512x512 | 1024x1024 |
|----------------|:----------:|:---------:|:---------:|:-------:|:------:|:-------:|:-------:|:---------:|
| Matrix-reef JS | 12,525,475 | 7,767,275 | 4,264,070 | 192,831 | 31,382 | 95.34 | 10.25 | 1.73 |
| Alternative M | 692,706 | 640,598 | 418,942 | 73,875 | 18,161 | 45.08 | 5.40 | 0.69 |
| Alternative S | 2,704,288 | 1,260,948 | 500,337 | 13,602 | 1,847 | 3.81 | 0.33 | 0.04 |
| Alternative J | 452,393 | 388,227 | 230,914 | 11,256 | 1,496 | 2.84 | 0.27 | 0.01 |

\# of operations per second. See [How to run the benchmarks](#how-to-run-the-benchmarks) for details.
\# multiplications per second (achieved on V8/node10.19 @ ubuntu 20.04 2 cpus and 8GB RAM on virtualbox). See [Running the benchmarks](#Running-the-benchmarks) for details.

## Disclaimer
Matrix-reef.js is still in its early stages. Some must-to-do features are not available, there is no docs, tutorials and no TypeScript. Thus, I'm afraid that Matrix-reef.js is not ready for production yet.
Expand All @@ -42,9 +43,12 @@ const B = new Matrix([

const C = A.multiply(B);
```
See [examples](#API-by-examples) below for a complete list of functionalities and demonstration of how to use them.
See [examples](#API-by-examples) below for a full list of functionalities.

## How to install

Matrix-reef.js is a pure Java Script library with no third-party dependencies.

### Node.js
```bash
$ npm install matrix-reef.js
Expand All @@ -58,6 +62,8 @@ const { Matrix } = require('matrix-reef.js');
<script src="https://cdn.jsdelivr.net/npm/matrix-reef.js@0.4.1/index.min.js"></script>
```
# API by examples
The API is streamline. Check out the examples below:

- **creating**: [constructors](examples/creational.md#constructors), [zeros, ones](examples/creational.md#zeros-and-ones), [identity](examples/creational.md#identity-aka-eye), [diagonal](examples/creational.md#diagonal) matrix from vector, [cloning](examples/creational.md#clone) and slicing.
- **accessing**: [get, set](examples/accessing.md#constructors), row, col, diagonal and path.
- **arithmetics**: [add, subtract](examples/arithmetics.md#get-and-set), [add scalar](examples/arithmetics.md#adding-scalar-to-matrix), [add row](examples/arithmetics.md#adding-row-or-column-to-matrix) to matrix and [add column](examples/arithmetics.md#adding-row-or-column-to-matrix) to matrix.
Expand All @@ -71,22 +77,24 @@ const { Matrix } = require('matrix-reef.js');

## Real world examples

This [example](examples/ann/training.ann.js) shows how to train a neural network using the [Iris](https://archive.ics.uci.edu/ml/datasets/iris) dataset and only `add`, `multiply`, `dotMultiply` and `transpose`:
This [example](examples/ann/training.ann.js) shows how to train a neural network using [Iris](https://archive.ics.uci.edu/ml/datasets/iris) dataset and only `add`, `multiply`, `dotMultiply`, `map` and `transpose`. You can run it by executing the following command:

```bash
$ node examples/training.ann.js
```
Should output:
THe output should be like:
```
Elapsed time: 0.81 secs for 5000 epochs.
```
## Running the benchmarks

The benchmark code is [here](blob/master/benchmarks/multiplication.js). This program uses <a href="https://github.com/bestiejs/benchmark.js" target="_blank">Benchmark.js</a> to compare the matrix-multiplication performance of Matrix-reef.js and two other Java Script Matrix implementations: <a href="https://github.com/josdejong/mathjs" target="_blank">Math.js</a> and <a href="https://github.com/mljs/matrix" target="_blank">ml-Matrix</a>.
Execute the following command to run a benchmark yourself:
There are several ways to evaluate a library. Here we are focused on throughput (that is, operations/sec). The code for this benchmark is [here](blob/master/benchmarks/multiplication.js). It uses <a href="https://github.com/bestiejs/benchmark.js" target="_blank">Benchmark.js</a> to compare the performance of Matrix-reef.js and three other Java Script matrix libraries.

To run the benchmark is easy. Just execute the following command to run a benchmark yourself:
```bash
node benchmarks/multiplication.js 10 4 10 5
```
The command above executes chunks of 10 `4x10` by `10x5` matrix multiplications. Ps.: Use `--max-old-space-size=4096` when running huge matrices.
The example above executes chunks of 10 `4x10` by `10x5` matrix multiplications.

## New features, bugs and collaboration
Do not hesitate to open a new issue whenever you find something wrong or does not working as expected. Suggestions of new features are pretty welcome as well as collaborations by PR test cases, benchmarks or even new features.
10 changes: 5 additions & 5 deletions benchmarks/benchmark.stuff.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ module.exports = {

const size = data.length;
suite

/*
.add('sylvester', function () {
for (let i = 0; i < size; ++i) {
Expand All @@ -73,7 +73,7 @@ module.exports = {
A.mmul(B);
}
})
})*/
.add('Matrix-Reef JS', function () {
for (let i = 0; i < size; ++i) {

Expand All @@ -86,7 +86,7 @@ module.exports = {

}
})
.add('MathJS', function () {
/*.add('MathJS', function () {
for (let i = 0; i < size; ++i) {
const matrices = data[i];
Expand All @@ -97,14 +97,14 @@ module.exports = {
MathJS.multiply(A, B);
}
})
})*/
.on('cycle', function (event) {
console.log(String(event.target));
})
.on('complete', function () {
console.log('Fastest is ' + this.filter('fastest').map('name'));
})
.run({ 'async': false, initCount : 3 });
.run({ 'async': false, initCount : 20, minSamples: 10000, minTime : 100 });
}

}
36 changes: 26 additions & 10 deletions src/api/lowlevelapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ module.exports = {
map2d: function (list, rows, cols, fun, restore) {
const size = list.length;
let result = restore;
for (let i = 0; i < size; ++i) {
for (let i = 0; i < rows; ++i) {
const i_cols = i * cols;
for (let j = 0; j < size; ++j) {
for (let j = 0; j < cols; ++j) {
const index = i_cols + j;
result[index] = fun(list[index], i, j);
}
Expand Down Expand Up @@ -159,20 +159,36 @@ module.exports = {
multiply: function (a, b, aRows, aCols, bRows, bCols, result) {
if (aCols != bRows) throw new Error("Incompatible matrices to multiply");

let bColumn = (bCols > 1) ? new Array(aCols) : b;
const bColumn = (bCols > 1) ? new Array(aCols) : b;
const safeBar = aCols - 4;
let P1, P2, P3, P4, PE;
for (let c = 0; c < bCols; ++c) {
if (bCols > 1) {
for (let br = 0; br < aCols; ++br) {
bColumn[br] = b[br * bCols + c];
let brOffset = c;
for (let br = 0; br < aCols; ++br, brOffset += bCols) {
bColumn[br] = b[brOffset];
}
}
for (let r = 0; r < aRows; ++r) {
let raOffset = 0;
let rbOffset = c;
for (let r = 0; r < aRows; ++r, raOffset += aCols, rbOffset += bCols) {
let sum = 0;
const r_aCols = r * aCols;
for (let br = 0; br < aCols; ++br) {
sum += a[r_aCols + br] * bColumn[br];
let br = 0;
for (; br < safeBar;) {
const t = raOffset + br;
P1 = a[t] * bColumn[br];
P2 = a[t + 1] * bColumn[br + 1];
P3 = a[t + 2] * bColumn[br + 2];
P3 = a[t + 3] * bColumn[br + 3];
br += 4;
sum += P1 + P2 + P3 + P4;
}
for (; br < aCols;) {
PE = a[raOffset + br] * bColumn[br];
sum += PE;
++br;
}
result[r * bCols + c] = sum;
result[rbOffset] = sum;
}
}
return result;
Expand Down
8 changes: 4 additions & 4 deletions test/arithmetics/multiplication.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,14 +133,14 @@ describe('blind check multiplication', function () {

const size = 1000;

this.timeout(size / 2);
this.timeout(Math.max(size / 2, 20000));

it("generating " + size + " multiplication use cases", function () {
for (let i = 0; i < size; ++i) {

const a_rows = Math.round(40 * Math.random()) + 1;
const a_cols = Math.round(40 * Math.random()) + 1;
const b_cols = Math.round(40 * Math.random()) + 1;
const a_rows = Math.round(128 * Math.random()) + 1;
const a_cols = Math.round(128 * Math.random()) + 1;
const b_cols = Math.round(128 * Math.random()) + 1;

const a = testUtils.randomArray(a_rows, a_cols, testUtils.rand50);
const b = testUtils.randomArray(a_cols, b_cols, testUtils.rand50);
Expand Down
6 changes: 6 additions & 0 deletions test/test.utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ module.exports = {
if (arrayCols && cols != arrayCols) throw new Error("Matrix cols and array cols does not match");
const arrayRows = array.length;
const storage = matrix.storage;

const storageSize = storage.length;
if (storageSize != (rows * cols))
throw new Error("The storage length " + storageSize +
" is not consistent to matrix dimensions [" + rows + ", " + cols + "].");

if (!arrayCols) {
for(let i = 0; i < arrayRows; ++i) {
const value = array[i];
Expand Down

0 comments on commit b938e04

Please sign in to comment.