Skip to content

Commit 8b459f1

Browse files
authored
feat: increase performance of the map and forEach methods of DenseMatrix (#3446)
1 parent 4a25655 commit 8b459f1

File tree

1 file changed

+80
-52
lines changed

1 file changed

+80
-52
lines changed

src/type/matrix/DenseMatrix.js

Lines changed: 80 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -526,86 +526,77 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies
526526
}
527527

528528
/**
529-
* Applies a callback function to a reference to each element of the matrix
529+
* Create a new matrix with the results of the callback function executed on
530+
* each entry of the matrix.
530531
* @memberof DenseMatrix
531532
* @param {Function} callback The callback function is invoked with three
532-
* parameters: the array containing the element,
533-
* the index of the element within that array (as an integer),
534-
* and for non unarry callbacks copy of the current index (as an array of integers).
533+
* parameters: the value of the element, the index
534+
* of the element, and the Matrix being traversed.
535+
* @param {boolean} skipZeros If true, the callback function is invoked only for non-zero entries
536+
* @param {boolean} isUnary If true, the callback function is invoked with one parameter
537+
*
538+
* @return {DenseMatrix} matrix
535539
*/
536-
DenseMatrix.prototype._forEach = function (callback) {
537-
const isUnary = callback.length === 2 // callback has 2 parameters: value, index
538-
const maxDepth = this._size.length - 1
540+
DenseMatrix.prototype.map = function (callback, skipZeros = false, isUnary = false) {
541+
const me = this
542+
const maxDepth = me._size.length - 1
539543

540-
if (maxDepth < 0) return
544+
if (maxDepth < 0) return me.clone()
541545

542-
if (isUnary) {
543-
iterateUnary(this._data)
544-
return
545-
}
546+
const fastCallback = optimizeCallback(callback, me, 'map', isUnary)
547+
const fastCallbackFn = fastCallback.fn
546548

549+
const result = me.create(undefined, me._datatype)
550+
result._size = me._size
551+
if (isUnary || fastCallback.isUnary) {
552+
result._data = iterateUnary(me._data)
553+
return result
554+
}
547555
if (maxDepth === 0) {
548-
for (let i = 0; i < this._data.length; i++) {
549-
callback(this._data, i, [i])
556+
const inputData = me.valueOf()
557+
const data = Array(inputData.length)
558+
for (let i = 0; i < inputData.length; i++) {
559+
data[i] = fastCallbackFn(inputData[i], [i], me)
550560
}
551-
return
561+
result._data = data
562+
return result
552563
}
553564

554-
const index = new Array(maxDepth + 1)
565+
const index = []
566+
result._data = iterate(me._data)
567+
return result
555568

556-
iterate(this._data)
557569
function iterate (data, depth = 0) {
570+
const result = Array(data.length)
558571
if (depth < maxDepth) {
559572
for (let i = 0; i < data.length; i++) {
560573
index[depth] = i
561-
iterate(data[i], depth + 1)
574+
result[i] = iterate(data[i], depth + 1)
562575
}
563576
} else {
564577
for (let i = 0; i < data.length; i++) {
565578
index[depth] = i
566-
callback(data, i, index.slice())
579+
result[i] = fastCallbackFn(data[i], index.slice(), me)
567580
}
568581
}
582+
return result
569583
}
584+
570585
function iterateUnary (data, depth = 0) {
586+
const result = Array(data.length)
571587
if (depth < maxDepth) {
572588
for (let i = 0; i < data.length; i++) {
573-
iterateUnary(data[i], depth + 1)
589+
result[i] = iterateUnary(data[i], depth + 1)
574590
}
575591
} else {
576592
for (let i = 0; i < data.length; i++) {
577-
callback(data, i)
593+
result[i] = fastCallbackFn(data[i])
578594
}
579595
}
596+
return result
580597
}
581598
}
582599

583-
/**
584-
* Create a new matrix with the results of the callback function executed on
585-
* each entry of the matrix.
586-
* @memberof DenseMatrix
587-
* @param {Function} callback The callback function is invoked with three
588-
* parameters: the value of the element, the index
589-
* of the element, and the Matrix being traversed.
590-
* @param {boolean} skipZeros If true, the callback function is invoked only for non-zero entries
591-
* @param {boolean} isUnary If true, the callback function is invoked with one parameter
592-
*
593-
* @return {DenseMatrix} matrix
594-
*/
595-
DenseMatrix.prototype.map = function (callback, skipZeros = false, isUnary = false) {
596-
const me = this
597-
const result = new DenseMatrix(me)
598-
const fastCallback = optimizeCallback(callback, me._data, 'map', isUnary)
599-
600-
const applyCallback = isUnary || fastCallback.isUnary
601-
? (arr, i) => { arr[i] = fastCallback.fn(arr[i]) }
602-
: (arr, i, index) => { arr[i] = fastCallback.fn(arr[i], index, me) }
603-
604-
result._forEach(applyCallback)
605-
606-
return result
607-
}
608-
609600
/**
610601
* Execute a callback function on each entry of the matrix.
611602
* @memberof DenseMatrix
@@ -617,13 +608,50 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies
617608
*/
618609
DenseMatrix.prototype.forEach = function (callback, skipZeros = false, isUnary = false) {
619610
const me = this
620-
const fastCallback = optimizeCallback(callback, me._data, 'map', isUnary)
611+
const maxDepth = me._size.length - 1
621612

622-
const applyCallback = isUnary || fastCallback.isUnary
623-
? (arr, i) => { fastCallback.fn(arr[i]) }
624-
: (arr, i, index) => { fastCallback.fn(arr[i], index, me) }
613+
if (maxDepth < 0) return
625614

626-
me._forEach(applyCallback)
615+
const fastCallback = optimizeCallback(callback, me, 'map', isUnary)
616+
const fastCallbackFn = fastCallback.fn
617+
if (isUnary || fastCallback.isUnary) {
618+
iterateUnary(me._data)
619+
return
620+
}
621+
if (maxDepth === 0) {
622+
for (let i = 0; i < me._data.length; i++) {
623+
fastCallbackFn(me._data[i], [i], me)
624+
}
625+
return
626+
}
627+
const index = []
628+
iterate(me._data)
629+
630+
function iterate (data, depth = 0) {
631+
if (depth < maxDepth) {
632+
for (let i = 0; i < data.length; i++) {
633+
index[depth] = i
634+
iterate(data[i], depth + 1)
635+
}
636+
} else {
637+
for (let i = 0; i < data.length; i++) {
638+
index[depth] = i
639+
fastCallbackFn(data[i], index.slice(), me)
640+
}
641+
}
642+
}
643+
644+
function iterateUnary (data, depth = 0) {
645+
if (depth < maxDepth) {
646+
for (let i = 0; i < data.length; i++) {
647+
iterateUnary(data[i], depth + 1)
648+
}
649+
} else {
650+
for (let i = 0; i < data.length; i++) {
651+
fastCallbackFn(data[i])
652+
}
653+
}
654+
}
627655
}
628656

629657
/**

0 commit comments

Comments
 (0)