From 55ea6eb963aab6f931f3fc1ebc6df0be432efcf8 Mon Sep 17 00:00:00 2001 From: Ilya Gyrdymov Date: Tue, 19 Nov 2019 01:19:01 +0200 Subject: [PATCH] Matrix constructors speed up --- benchmark/main.dart | 6 +- .../float32_matrix_from_columns.dart | 2 +- ...art => float32_matrix_from_flattened.dart} | 6 +- ...ist.dart => float32_matrix_from_list.dart} | 6 +- ...ows.dart => float32_matrix_from_rows.dart} | 2 +- .../float32_matrix_data_manager.dart | 73 ++++++++--------- .../float64_matrix_data_manager.dart | 82 +++++++++---------- .../data_manager/matrix_data_manager.dart | 1 - lib/src/vector/float32x4_vector.dart | 6 +- lib/src/vector/float64x2_vector.dart | 6 +- ...olumns_constructor_test_group_factory.dart | 4 +- ...m_rows_constructor_test_group_factory.dart | 4 +- .../insert_columns_test_group_factory.dart | 4 +- 13 files changed, 93 insertions(+), 109 deletions(-) rename benchmark/matrix/float32/matrix_initializing/{float32_from_flattened.dart => float32_matrix_from_flattened.dart} (76%) rename benchmark/matrix/float32/matrix_initializing/{float32_from_list.dart => float32_matrix_from_list.dart} (81%) rename benchmark/matrix/float32/matrix_initializing/{float32_from_rows.dart => float32_matrix_from_rows.dart} (93%) diff --git a/benchmark/main.dart b/benchmark/main.dart index 684cc5c6..9529eb5d 100644 --- a/benchmark/main.dart +++ b/benchmark/main.dart @@ -1,7 +1,7 @@ import 'matrix/float32/matrix_initializing/float32_matrix_from_columns.dart'; -import 'matrix/float32/matrix_initializing/float32_from_flattened.dart'; -import 'matrix/float32/matrix_initializing/float32_from_list.dart'; -import 'matrix/float32/matrix_initializing/float32_from_rows.dart'; +import 'matrix/float32/matrix_initializing/float32_matrix_from_flattened.dart'; +import 'matrix/float32/matrix_initializing/float32_matrix_from_list.dart'; +import 'matrix/float32/matrix_initializing/float32_matrix_from_rows.dart'; import 'vector/float32/vector_initializing/float32x4_from_list.dart'; import 'vector/float32/vector_initializing/float32x4_random_filled.dart'; import 'vector/float32/vector_operations/float32x4_vector_abs.dart'; diff --git a/benchmark/matrix/float32/matrix_initializing/float32_matrix_from_columns.dart b/benchmark/matrix/float32/matrix_initializing/float32_matrix_from_columns.dart index 378bab0e..5f0a9f8d 100644 --- a/benchmark/matrix/float32/matrix_initializing/float32_matrix_from_columns.dart +++ b/benchmark/matrix/float32/matrix_initializing/float32_matrix_from_columns.dart @@ -1,4 +1,4 @@ -// Approx. 1.6 second (MacBook Air mid 2017), Dart VM version: 2.5.0 +// Approx. 2.5 second (MacBook Air mid 2017), Dart VM version: 2.5.0 import 'package:benchmark_harness/benchmark_harness.dart'; import 'package:ml_linalg/dtype.dart'; diff --git a/benchmark/matrix/float32/matrix_initializing/float32_from_flattened.dart b/benchmark/matrix/float32/matrix_initializing/float32_matrix_from_flattened.dart similarity index 76% rename from benchmark/matrix/float32/matrix_initializing/float32_from_flattened.dart rename to benchmark/matrix/float32/matrix_initializing/float32_matrix_from_flattened.dart index 466147b5..5266d771 100644 --- a/benchmark/matrix/float32/matrix_initializing/float32_from_flattened.dart +++ b/benchmark/matrix/float32/matrix_initializing/float32_matrix_from_flattened.dart @@ -1,4 +1,4 @@ -// Approx. 4.5 seconds (MacBook Air mid 2017), Dart VM version: 2.5.0 +// Approx. 5 seconds (MacBook Air mid 2017), Dart VM version: 2.5.0 import 'package:benchmark_harness/benchmark_harness.dart'; import 'package:ml_linalg/dtype.dart'; @@ -10,7 +10,7 @@ const numOfColumns = 1000; class Float32MatrixFromFlattenedBenchmark extends BenchmarkBase { Float32MatrixFromFlattenedBenchmark() : - super('Matrix initialization, from flattened list'); + super('Matrix initialization (fromFlattenedList)'); List _source; @@ -26,7 +26,7 @@ class Float32MatrixFromFlattenedBenchmark extends BenchmarkBase { @override void setup() { - _source = Vector.randomFilled(numOfRows * numOfColumns) + _source = Vector.randomFilled(numOfRows * numOfColumns, min: -1000, max: 1000) .toList(growable: false); } } diff --git a/benchmark/matrix/float32/matrix_initializing/float32_from_list.dart b/benchmark/matrix/float32/matrix_initializing/float32_matrix_from_list.dart similarity index 81% rename from benchmark/matrix/float32/matrix_initializing/float32_from_list.dart rename to benchmark/matrix/float32/matrix_initializing/float32_matrix_from_list.dart index b33f938d..5c35feb4 100644 --- a/benchmark/matrix/float32/matrix_initializing/float32_from_list.dart +++ b/benchmark/matrix/float32/matrix_initializing/float32_matrix_from_list.dart @@ -1,4 +1,4 @@ -// Approx. 0.9 second (MacBook Air mid 2017), Dart VM version: 2.5.0 +// Approx. 1 second (MacBook Air mid 2017), Dart VM version: 2.5.0 import 'package:benchmark_harness/benchmark_harness.dart'; import 'package:ml_linalg/dtype.dart'; @@ -10,12 +10,12 @@ const numOfColumns = 1000; class Float32MatrixFromListBenchmark extends BenchmarkBase { Float32MatrixFromListBenchmark() : - super('Matrix initialization, from list'); + super('Matrix initialization (fromList)'); final _source = List.filled(numOfRows, Vector .randomFilled(numOfColumns, min: -10000, max: 10000) - .toList(), + .toList(growable: false), ); static void main() { diff --git a/benchmark/matrix/float32/matrix_initializing/float32_from_rows.dart b/benchmark/matrix/float32/matrix_initializing/float32_matrix_from_rows.dart similarity index 93% rename from benchmark/matrix/float32/matrix_initializing/float32_from_rows.dart rename to benchmark/matrix/float32/matrix_initializing/float32_matrix_from_rows.dart index fda0ec05..e7d171c0 100644 --- a/benchmark/matrix/float32/matrix_initializing/float32_from_rows.dart +++ b/benchmark/matrix/float32/matrix_initializing/float32_matrix_from_rows.dart @@ -10,7 +10,7 @@ const numOfColumns = 1000; class Float32MatrixFromRowsBenchmark extends BenchmarkBase { Float32MatrixFromRowsBenchmark() : - super('Matrix initialization, from rows'); + super('Matrix initialization (fromRows)'); final _source = List.filled(numOfColumns, diff --git a/lib/src/matrix/data_manager/float32_matrix_data_manager.dart b/lib/src/matrix/data_manager/float32_matrix_data_manager.dart index 559b6e01..1d972291 100644 --- a/lib/src/matrix/data_manager/float32_matrix_data_manager.dart +++ b/lib/src/matrix/data_manager/float32_matrix_data_manager.dart @@ -20,15 +20,11 @@ class Float32MatrixDataManager implements MatrixDataManager { _colsCache = List(getLengthOfFirstOrZero(source)), _data = ByteData(source.length * getLengthOfFirstOrZero(source) * _bytesPerElement) { - var i = 0; - var j = 0; final dataAsList = _data.buffer.asFloat32List(); - for (final row in source) { - for (final value in row) { - dataAsList[i * columnsNum + j++] = value; + for (int i = 0, j = 0; i < source.length; i++) { + for (int j = 0; j < source[i].length; j++) { + dataAsList[i * columnsNum + j] = source[i][j]; } - i++; - j = 0; } } @@ -41,14 +37,20 @@ class Float32MatrixDataManager implements MatrixDataManager { _colsCache = List(getLengthOfFirstOrZero(source)), _data = ByteData(source.length * getLengthOfFirstOrZero(source) * _bytesPerElement) { - var i = 0; - var j = 0; final dataAsList = _data.buffer.asFloat32List(); - for (final row in source) { - for (final value in row) { + for (int i = 0, j = 0; i < source.length; i++) { + final row = source[i]; + if (row.dtype != dtype) { + throw Exception('Expected vector type: `$dtype`, ' + 'but given: ${row.dtype}'); + } + if (row.length != columnsNum) { + throw Exception('Expected vector length: `$columnsNum`, ' + 'but given: ${row.length}'); + } + for (final value in source[i]) { dataAsList[i * columnsNum + j++] = value; } - i++; j = 0; } } @@ -65,8 +67,14 @@ class Float32MatrixDataManager implements MatrixDataManager { final dataAsList = _data.buffer.asFloat32List(); for (int i = 0, j = 0; i < source.length; i++) { final column = source[i]; - // we use for..in here because `column` provides efficient - // Float32List.iterator + if (column.dtype != dtype) { + throw Exception('Expected vector type: `$dtype`, ' + 'but given: ${column.dtype}'); + } + if (column.length != rowsNum) { + throw Exception('Expected vector length: `$rowsNum`, ' + 'but given: ${column.length}'); + } for (final value in column) { dataAsList[j++ * columnsNum + i] = value; } @@ -112,7 +120,10 @@ class Float32MatrixDataManager implements MatrixDataManager { _rowsCache = List(size), _colsCache = List(size), _data = ByteData(size * size * _bytesPerElement) { - _updateByteDataForDiagonalMatrix((i) => scalar); + for (int i = 0; i < size; i++) { + _data.setFloat32((i * columnsNum + i) * _bytesPerElement, scalar, + Endian.host); + } } @override @@ -142,23 +153,17 @@ class Float32MatrixDataManager implements MatrixDataManager { bool get hasData => rowsNum > 0 && columnsNum > 0; @override - List getValues(int indexFrom, int count) { + Vector getRow(int index) { if (!hasData) { throw Exception('Matrix is empty'); } + final indexFrom = index * columnsNum; if (indexFrom >= rowsNum * columnsNum) { throw RangeError.range(indexFrom, 0, rowsNum * columnsNum); } - return _data.buffer.asFloat32List(indexFrom * _bytesPerElement, count); - } - - @override - Vector getRow(int index) { - if (!hasData) { - throw Exception('Matrix is empty'); - } - _rowsCache[index] ??= Vector.fromList(getValues(index * columnsNum, - columnsNum), dtype: dtype); + final values = _data.buffer.asFloat32List( + indexFrom * _bytesPerElement, columnsNum); + _rowsCache[index] ??= Vector.fromList(values, dtype: dtype); return _rowsCache[index]; } @@ -169,22 +174,12 @@ class Float32MatrixDataManager implements MatrixDataManager { } if (_colsCache[index] == null) { final result = List(rowsNum); - for (final i in rowIndices) { - //@TODO: find a more efficient way to get the single value - result[i] = getValues(i * columnsNum + index, 1).first; + for (int i = 0; i < result.length; i++) { + result[i] = _data.getFloat32( + (i * columnsNum + index) * _bytesPerElement, Endian.host); } _colsCache[index] = Vector.fromList(result, dtype: dtype); } return _colsCache[index]; } - - void _updateByteDataForDiagonalMatrix(double generateValue(int i)) { - for (int i = 0; i < rowsNum; i++) { - for (int j = 0; j < columnsNum; j++) { - final value = i == j ? generateValue(i) : 0.0; - _data.setFloat32((i * columnsNum + j) * _bytesPerElement, value, - Endian.host); - } - } - } } diff --git a/lib/src/matrix/data_manager/float64_matrix_data_manager.dart b/lib/src/matrix/data_manager/float64_matrix_data_manager.dart index 7817c3a6..d16b3215 100644 --- a/lib/src/matrix/data_manager/float64_matrix_data_manager.dart +++ b/lib/src/matrix/data_manager/float64_matrix_data_manager.dart @@ -22,15 +22,11 @@ class Float64MatrixDataManager implements MatrixDataManager { _colsCache = List(getLengthOfFirstOrZero(source)), _data = ByteData(source.length * getLengthOfFirstOrZero(source) * _bytesPerElement) { - var i = 0; - var j = 0; final dataAsList = _data.buffer.asFloat64List(); - for (final row in source) { - for (final value in row) { - dataAsList[i * columnsNum + j++] = value; + for (int i = 0, j = 0; i < source.length; i++) { + for (int j = 0; j < source[i].length; j++) { + dataAsList[i * columnsNum + j] = source[i][j]; } - i++; - j = 0; } } @@ -43,14 +39,20 @@ class Float64MatrixDataManager implements MatrixDataManager { _colsCache = List(getLengthOfFirstOrZero(source)), _data = ByteData(source.length * getLengthOfFirstOrZero(source) * _bytesPerElement) { - var i = 0; - var j = 0; final dataAsList = _data.buffer.asFloat64List(); - for (final row in source) { - for (final value in row) { + for (int i = 0, j = 0; i < source.length; i++) { + final row = source[i]; + if (row.dtype != dtype) { + throw Exception('Expected vector type: `$dtype`, ' + 'but given: ${row.dtype}'); + } + if (row.length != columnsNum) { + throw Exception('Expected vector length: `$columnsNum`, ' + 'but given: ${row.length}'); + } + for (final value in source[i]) { dataAsList[i * columnsNum + j++] = value; } - i++; j = 0; } } @@ -64,14 +66,20 @@ class Float64MatrixDataManager implements MatrixDataManager { _colsCache = source, _data = ByteData(source.length * getLengthOfFirstOrZero(source) * _bytesPerElement) { - var i = 0; - var j = 0; final dataAsList = _data.buffer.asFloat64List(); - for (final column in source) { + for (int i = 0, j = 0; i < source.length; i++) { + final column = source[i]; + if (column.dtype != dtype) { + throw Exception('Expected vector type: `$dtype`, ' + 'but given: ${column.dtype}'); + } + if (column.length != rowsNum) { + throw Exception('Expected vector length: `$rowsNum`, ' + 'but given: ${column.length}'); + } for (final value in column) { dataAsList[j++ * columnsNum + i] = value; } - i++; j = 0; } } @@ -100,7 +108,10 @@ class Float64MatrixDataManager implements MatrixDataManager { _rowsCache = List(source.length), _colsCache = List(source.length), _data = ByteData(source.length * source.length * _bytesPerElement) { - _updateByteDataForDiagonalMatrix((i) => source[i]); + for (int i = 0; i < rowsNum; i++) { + _data.setFloat64((i * columnsNum + i) * _bytesPerElement, source[i], + Endian.host); + } } Float64MatrixDataManager.scalar(double scalar, int size) : @@ -111,7 +122,10 @@ class Float64MatrixDataManager implements MatrixDataManager { _rowsCache = List(size), _colsCache = List(size), _data = ByteData(size * size * _bytesPerElement) { - _updateByteDataForDiagonalMatrix((i) => scalar); + for (int i = 0; i < size; i++) { + _data.setFloat64((i * columnsNum + i) * _bytesPerElement, scalar, + Endian.host); + } } @override @@ -141,23 +155,17 @@ class Float64MatrixDataManager implements MatrixDataManager { bool get hasData => rowsNum > 0 && columnsNum > 0; @override - List getValues(int indexFrom, int count) { + Vector getRow(int index) { if (!hasData) { throw Exception('Matrix is empty'); } + final indexFrom = index * columnsNum; if (indexFrom >= rowsNum * columnsNum) { throw RangeError.range(indexFrom, 0, rowsNum * columnsNum); } - return _data.buffer.asFloat64List(indexFrom * _bytesPerElement, count); - } - - @override - Vector getRow(int index) { - if (!hasData) { - throw Exception('Matrix is empty'); - } - _rowsCache[index] ??= Vector.fromList(getValues(index * columnsNum, - columnsNum), dtype: dtype); + final values = _data.buffer.asFloat64List( + indexFrom * _bytesPerElement, columnsNum); + _rowsCache[index] ??= Vector.fromList(values, dtype: dtype); return _rowsCache[index]; } @@ -168,22 +176,12 @@ class Float64MatrixDataManager implements MatrixDataManager { } if (_colsCache[index] == null) { final result = List(rowsNum); - for (final i in rowIndices) { - //@TODO: find a more efficient way to get the single value - result[i] = getValues(i * columnsNum + index, 1).first; + for (int i = 0; i < result.length; i++) { + result[i] = _data.getFloat64( + (i * columnsNum + index) * _bytesPerElement, Endian.host); } _colsCache[index] = Vector.fromList(result, dtype: dtype); } return _colsCache[index]; } - - void _updateByteDataForDiagonalMatrix(double generateValue(int i)) { - for (int i = 0; i < rowsNum; i++) { - for (int j = 0; j < columnsNum; j++) { - final value = i == j ? generateValue(i) : 0.0; - _data.setFloat64((i * columnsNum + j) * _bytesPerElement, value, - Endian.host); - } - } - } } diff --git a/lib/src/matrix/data_manager/matrix_data_manager.dart b/lib/src/matrix/data_manager/matrix_data_manager.dart index eed27353..845e4d28 100644 --- a/lib/src/matrix/data_manager/matrix_data_manager.dart +++ b/lib/src/matrix/data_manager/matrix_data_manager.dart @@ -11,5 +11,4 @@ abstract class MatrixDataManager { bool get hasData; Vector getColumn(int index); Vector getRow(int index); - List getValues(int index, int length); } diff --git a/lib/src/vector/float32x4_vector.dart b/lib/src/vector/float32x4_vector.dart index 247171e1..a915f642 100644 --- a/lib/src/vector/float32x4_vector.dart +++ b/lib/src/vector/float32x4_vector.dart @@ -60,11 +60,7 @@ class Float32x4Vector with IterableMixin implements Vector { Float32x4Vector.zero(this.length, this._cacheManager, this._simdHelper) : _numOfBuckets = _getNumOfBuckets(length, _bucketSize), _buffer = ByteData(_getNumOfBuckets(length, _bucketSize) * - _bytesPerSimdElement).buffer { - for (int i = 0; i < length; i++) { - _buffer.asByteData().setFloat32(_bytesPerElement * i, 0.0, Endian.host); - } - } + _bytesPerSimdElement).buffer; Float32x4Vector.fromSimdList(Float32x4List data, this.length, this._cacheManager, this._simdHelper) : diff --git a/lib/src/vector/float64x2_vector.dart b/lib/src/vector/float64x2_vector.dart index 73e45414..c424848f 100644 --- a/lib/src/vector/float64x2_vector.dart +++ b/lib/src/vector/float64x2_vector.dart @@ -62,11 +62,7 @@ class Float64x2Vector with IterableMixin implements Vector { Float64x2Vector.zero(this.length, this._cacheManager, this._simdHelper) : _numOfBuckets = _getNumOfBuckets(length, _bucketSize), _buffer = ByteData(_getNumOfBuckets(length, _bucketSize) * - _bytesPerSimdElement).buffer { - for (int i = 0; i < length; i++) { - _buffer.asByteData().setFloat64(_bytesPerElement * i, 0.0, Endian.host); - } - } + _bytesPerSimdElement).buffer; Float64x2Vector.fromSimdList(Float64x2List data, this.length, this._cacheManager, this._simdHelper) : diff --git a/test/integration_test/matrix/constructors/from_columns/from_columns_constructor_test_group_factory.dart b/test/integration_test/matrix/constructors/from_columns/from_columns_constructor_test_group_factory.dart index 558bb116..ad5bd312 100644 --- a/test/integration_test/matrix/constructors/from_columns/from_columns_constructor_test_group_factory.dart +++ b/test/integration_test/matrix/constructors/from_columns/from_columns_constructor_test_group_factory.dart @@ -11,8 +11,8 @@ void matrixFromColumnsConstructorTestGroupFactory(DType dtype) => test('should create an instance with predefined vectors as matrix ' 'columns', () { final actual = Matrix.fromColumns([ - Vector.fromList([1.0, 2.0, 3.0, 4.0, 5.0]), - Vector.fromList([6.0, 7.0, 8.0, 9.0, 0.0]), + Vector.fromList([1.0, 2.0, 3.0, 4.0, 5.0], dtype: dtype), + Vector.fromList([6.0, 7.0, 8.0, 9.0, 0.0], dtype: dtype), ], dtype: dtype); final expected = [ diff --git a/test/integration_test/matrix/constructors/from_rows/from_rows_constructor_test_group_factory.dart b/test/integration_test/matrix/constructors/from_rows/from_rows_constructor_test_group_factory.dart index bdc5336d..557bf745 100644 --- a/test/integration_test/matrix/constructors/from_rows/from_rows_constructor_test_group_factory.dart +++ b/test/integration_test/matrix/constructors/from_rows/from_rows_constructor_test_group_factory.dart @@ -11,8 +11,8 @@ void matrixFromRowsConstructorTestGroupFactory(DType dtype) => test('should create an instance with predefined vectors as matrix ' 'rows', () { final actual = Matrix.fromRows([ - Vector.fromList([1.0, 2.0, 3.0, 4.0, 5.0]), - Vector.fromList([6.0, 7.0, 8.0, 9.0, 0.0]), + Vector.fromList([1.0, 2.0, 3.0, 4.0, 5.0], dtype: dtype), + Vector.fromList([6.0, 7.0, 8.0, 9.0, 0.0], dtype: dtype), ], dtype: dtype); final expected = [ diff --git a/test/integration_test/matrix/methods/insert_columns/insert_columns_test_group_factory.dart b/test/integration_test/matrix/methods/insert_columns/insert_columns_test_group_factory.dart index 4962c634..f0b9a213 100644 --- a/test/integration_test/matrix/methods/insert_columns/insert_columns_test_group_factory.dart +++ b/test/integration_test/matrix/methods/insert_columns/insert_columns_test_group_factory.dart @@ -243,7 +243,7 @@ void matrixInsertColumnsTestGroupFactory(DType dtype) => ])); }); - test('should throw an error if a new column has an invalid length', () { + test('should throw an error if new column has invalid length', () { final matrix = Matrix.fromList([ [4.0, 8.0, 12.0, 16.0, 34.0], [20.0, 24.0, 28.0, 32.0, 23.0], @@ -255,7 +255,7 @@ void matrixInsertColumnsTestGroupFactory(DType dtype) => final newCol = Vector.fromList( [100.0, 200.0, 300.0, 400.0, 500.0, 1000.0], dtype: dtype); - expect(() => matrix.insertColumns(4, [newCol]), throwsRangeError); + expect(() => matrix.insertColumns(4, [newCol]), throwsException); }); test('should throw an error if the passed column index is greater than or '