From af6024acfdb6898165b77a983d8d0ec0fb6059b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20M=C3=B6nig?= Date: Wed, 10 Jan 2024 10:16:00 +0100 Subject: [PATCH] optimized internal matrix ops by speeding up shape & rank determination assuming well-formed tables --- HISTORY.md | 3 +++ snap.html | 6 +++--- src/lists.js | 23 ++++++++++++++++++++++- src/objects.js | 4 ++-- src/threads.js | 15 ++++++++++----- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 95cf736e21..3944cae1a5 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -17,6 +17,9 @@ * fixed speech balloons inside ASK menus * added safeguard against accidentally querying too many list dimensions (forgetting to transpose a convolution) +2024-01-10 +* lists, threads, objects: optimized internal matrix ops by speeding up shape & rank determination assuming well-formed tables + 2024-01-09 * pixels library: added Sobel edge-detection variants for left/right/top/bottom * threads: use the microphone's sample rate for playing back lists of samples once it has been used, otherwise 44.1 kHz diff --git a/snap.html b/snap.html index 2f16553715..c71b9e3130 100755 --- a/snap.html +++ b/snap.html @@ -17,12 +17,12 @@ - - + + - + diff --git a/src/lists.js b/src/lists.js index ed7cf0f0aa..6a82bafc3f 100644 --- a/src/lists.js +++ b/src/lists.js @@ -65,7 +65,7 @@ Context, ZERO, WHITE*/ // Global settings ///////////////////////////////////////////////////// -modules.lists = '2024-January-08'; +modules.lists = '2024-January-10'; var List; var ListWatcherMorph; @@ -660,6 +660,14 @@ List.prototype.rank = function () { return rank; }; +List.prototype.quickRank = function () { + // answer the number of my dimensions + // only look at the first item of each dimension, + // assuming regularly shaped nested lists + var item = this.at(1); + return item instanceof List ? item.quickRank() + 1 : 1; +}; + List.prototype.shape = function () { // answer a list of the maximum size for each dimension var dim, @@ -678,6 +686,19 @@ List.prototype.shape = function () { return shp; }; +List.prototype.quickShape = function () { + // answer a list of each dimension's size + // only look at the first item of each dimension, + // assuming regularly shaped nested lists + var shp = [], + item = this; + while (item instanceof List) { + shp.push(item.length()); + item = item.at(1); + } + return new List(shp); +}; + List.prototype.getDimension = function (rank = 0) { // private - answer a list of all elements of the specified rank if (rank < 1) {return new List(); } diff --git a/src/objects.js b/src/objects.js index 4dba9188bb..c8336ece30 100644 --- a/src/objects.js +++ b/src/objects.js @@ -95,7 +95,7 @@ embedMetadataPNG, SnapExtensions, SnapSerializer, snapEquals*/ /*jshint esversion: 11*/ -modules.objects = '2024-January-08'; +modules.objects = '2024-January-10'; var SpriteMorph; var StageMorph; @@ -4340,7 +4340,7 @@ SpriteMorph.prototype.doSwitchToCostume = function (id, noShadow) { h = 0, stage; if (id instanceof List) { // try to turn a list of pixels into a costume - if (id.shape().at(2) <= 4) { + if (id.quickShape().at(2) <= 4) { if (this.costume) { // recycle dimensions of current costume w = this.costume.width(); diff --git a/src/threads.js b/src/threads.js index 0f4b67df33..4a43d34345 100644 --- a/src/threads.js +++ b/src/threads.js @@ -65,7 +65,7 @@ StagePickerMorph, CustomBlockDefinition, CommentMorph*/ /*jshint esversion: 11, bitwise: false, evil: true*/ -modules.threads = '2024-January-09'; +modules.threads = '2024-January-10'; var ThreadManager; var Process; @@ -2378,6 +2378,11 @@ Process.prototype.reportRank = function (data) { return data instanceof List ? data.rank() : 0; }; +Process.prototype.reportQuickRank = function (data) { + // private - assume a regularly shaped nested list + return data instanceof List ? data.quickRank() : 0; +}; + Process.prototype.reportDimensions = function (data) { return data instanceof List ? data.shape() : new List(); }; @@ -4518,7 +4523,7 @@ Process.prototype.hyperMonadic = function (baseOp, arg) { }; Process.prototype.hyperDyadic = function (baseOp, a, b) { - var match = Math.min(this.reportRank(a), this.reportRank(b)); + var match = Math.min(this.reportQuickRank(a), this.reportQuickRank(b)); return this.hyperZip(baseOp, a, b, match, match); }; @@ -4528,8 +4533,8 @@ Process.prototype.hyperZip = function (baseOp, a, b, zipa, zipb) { // this allows to write 2d matrix convolutions for 3+d inputs with // 2d kernels e.g. for image processing without having to first // reshape the kernel matrix to match the broadcast shape. - var arank = this.reportRank(a), - brank = this.reportRank(b), + var arank = this.reportQuickRank(a), + brank = this.reportQuickRank(b), len, i, result; if (arank === brank || (arank <= zipa && brank <= zipb)) { if (arank + brank === 0) { @@ -7204,7 +7209,7 @@ Process.prototype.reportNewCostume = function (pixels, width, height, name) { } if (width <= 0 || height <= 0) { // try to interpret the pixels as matrix - shp = this.reportDimensions(pixels); + shp = pixels.quickShape(); if (shp.at(2) > 4) { height = shp.at(1); width = shp.at(2);