From 6319fffd82134c123111f042c9cf1a66b9af052d Mon Sep 17 00:00:00 2001 From: Antranig Basman Date: Wed, 11 May 2022 13:47:16 +0100 Subject: [PATCH 1/3] FLUID-6395: Initial test case to investigate fan-out relay problems - this configuration doesn't seem to hit the problem --- .../core/js/DataBindingTests.js | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/framework-tests/core/js/DataBindingTests.js b/tests/framework-tests/core/js/DataBindingTests.js index fac1966b02..a6c95bd195 100644 --- a/tests/framework-tests/core/js/DataBindingTests.js +++ b/tests/framework-tests/core/js/DataBindingTests.js @@ -1441,6 +1441,46 @@ jqUnit.test("FLUID-6390 V: Updating lensed components as an array", function () fluid.tests.fluid6390assertModelValues("Updated model values are correct", that, []); }); +/** FLUID-6395 - Performance in fan-out of relays **/ + +fluid.tests.fluid6395generate = function (fanOut) { + return fluid.generate(fanOut, function (index) { + return { + value: index + }; + }, true); +}; + +fluid.defaults("fluid.tests.fluid6395root", { + gradeNames: "fluid.modelComponent", + fanOut: 200, + model: { + modelSource: "@expand:fluid.tests.fluid6395generate({that}.options.fanOut)" + }, + dynamicComponents: { + child: { + sources: "{that}.model.modelSource", + type: "fluid.modelComponent", + options: { + model: { + value: "{source}.value" + } + } + } + } +}); + +jqUnit.test("FLUID-6395: Performance of relay rules with increasing fan-out", function () { + var that = fluid.tests.fluid6395root(); + var children = fluid.queryIoCSelector(that, "fluid.modelComponent"); + jqUnit.assertEquals("Constructed " + that.options.fanOut + " children: ", that.options.fanOut, children.length); + var now = Date.now(); + children[0].applier.change("value", 999); + var elapsed = Date.now() - now; + jqUnit.assertEquals("Change relayed to parent model ", 999, that.model.modelSource[0].value); + fluid.log("Applied change in " + elapsed + "ms"); +}); + /** FLUID-6570: Short-form free transforms **/ fluid.defaults("fluid.tests.fluid6570root", { From 8e6e5508545dbfc52566fc99d38e354153467d30 Mon Sep 17 00:00:00 2001 From: Antranig Basman Date: Mon, 16 May 2022 20:04:06 +0100 Subject: [PATCH 2/3] FLUID-6395: Second attempt still does not exhibit explosion --- src/framework/core/js/DataBinding.js | 4 +++ .../core/js/DataBindingTests.js | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/framework/core/js/DataBinding.js b/src/framework/core/js/DataBinding.js index b296633e21..046240c3e5 100644 --- a/src/framework/core/js/DataBinding.js +++ b/src/framework/core/js/DataBinding.js @@ -840,6 +840,8 @@ fluid.relayRecursionBailout = 100; // is transactional but it does not require the transaction to conclude in order to fire - it may be reused as many times as // required within the "overall" transaction whilst genuine (external) changes continue to arrive. +fluid.count = 0; + // TODO: Vast overcomplication and generation of closure garbage. SURELY we should be able to convert this into an externalised, arg-ist form /** Registers a listener operating one leg of a model relay relation, connecting the source and target. Called once or twice from `fluid.connectModelRelay` - * see the comment there for the three cases involved. Note that in its case iii)B) the applier to bind to is not the one attached to `target` but is instead @@ -875,6 +877,8 @@ fluid.registerDirectChangeRelay = function (target, targetSegs, source, sourceSe targetSegs = fluid.makeArray(targetSegs); sourceSegs = fluid.makeArray(sourceSegs); // take copies since originals will be trashed var sourceListener = function (newValue, oldValue, path, changeRequest, trans, applier) { + ++fluid.count; + fluid.log("SourceListener " + fluid.count); var transId = trans.id; var transRec = fluid.getModelTransactionRec(target, transId); if (applier && trans && !transRec[applier.applierId]) { // don't trash existing record which may contain "options" (FLUID-5397) diff --git a/tests/framework-tests/core/js/DataBindingTests.js b/tests/framework-tests/core/js/DataBindingTests.js index a6c95bd195..58c589f68c 100644 --- a/tests/framework-tests/core/js/DataBindingTests.js +++ b/tests/framework-tests/core/js/DataBindingTests.js @@ -1481,6 +1481,39 @@ jqUnit.test("FLUID-6395: Performance of relay rules with increasing fan-out", fu fluid.log("Applied change in " + elapsed + "ms"); }); +fluid.defaults("fluid.tests.fluid6395root2", { + gradeNames: "fluid.modelComponent", + fanOut: 100, + source: "@expand:fluid.tests.fluid6395generate({that}.options.fanOut)", + model: { + someValue: 42 + }, + dynamicComponents: { + child: { + sources: "{that}.options.source", + type: "fluid.modelComponent", + options: { + model: { + parentModel: "{fluid6395root2}.model" + } + } + } + } +}); + +jqUnit.test("FLUID-6395: Performance of relay rules with increasing fan-out", function () { + var that = fluid.tests.fluid6395root2(); + var children = fluid.queryIoCSelector(that, "fluid.modelComponent"); + jqUnit.assertEquals("Constructed " + that.options.fanOut + " children: ", that.options.fanOut, children.length); + var now = Date.now(); + // children[0].applier.change("parentModel.someValue", 999); + fluid.count = 0; + that.applier.change("someValue", 999); + var elapsed = Date.now() - now; + jqUnit.assertEquals("Change relayed to parent model ", 999, that.model.someValue); + fluid.log("Applied change in " + elapsed + "ms"); +}); + /** FLUID-6570: Short-form free transforms **/ fluid.defaults("fluid.tests.fluid6570root", { From 4620b6499cf0bf3c64bd731489fe8c290b51f0a8 Mon Sep 17 00:00:00 2001 From: Antranig Basman Date: Fri, 27 May 2022 11:57:26 +0100 Subject: [PATCH 3/3] FLUID-6395: Further attempts to cause work magnification by creating an extra level of component containment. Not successful - work is still proportional to component tree size. --- src/framework/core/js/DataBinding.js | 5 ++-- .../core/js/DataBindingTests.js | 26 +++++++++++++------ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/framework/core/js/DataBinding.js b/src/framework/core/js/DataBinding.js index 046240c3e5..81d5029d33 100644 --- a/src/framework/core/js/DataBinding.js +++ b/src/framework/core/js/DataBinding.js @@ -877,8 +877,6 @@ fluid.registerDirectChangeRelay = function (target, targetSegs, source, sourceSe targetSegs = fluid.makeArray(targetSegs); sourceSegs = fluid.makeArray(sourceSegs); // take copies since originals will be trashed var sourceListener = function (newValue, oldValue, path, changeRequest, trans, applier) { - ++fluid.count; - fluid.log("SourceListener " + fluid.count); var transId = trans.id; var transRec = fluid.getModelTransactionRec(target, transId); if (applier && trans && !transRec[applier.applierId]) { // don't trash existing record which may contain "options" (FLUID-5397) @@ -886,6 +884,9 @@ fluid.registerDirectChangeRelay = function (target, targetSegs, source, sourceSe } var transEl = transRec[applierId]; transRec[linkId] = transRec[linkId] || 0; + ++fluid.count; + //fluid.log("SourceListener " + fluid.count + " firing with value " + JSON.stringify(newValue, null, 2) + " from source ", source, " to target ", target, + // " sourceSegs ", sourceSegs, " targetSegs ", targetSegs); // Crude "oscillation prevention" system limits each link to maximum of 2 operations per cycle (presumably in opposite directions) var relay = true; // TODO: See FLUID-5303 - we currently disable this check entirely to solve FLUID-5293 - perhaps we might remove link counts entirely if (relay) { diff --git a/tests/framework-tests/core/js/DataBindingTests.js b/tests/framework-tests/core/js/DataBindingTests.js index 58c589f68c..0d16f54d72 100644 --- a/tests/framework-tests/core/js/DataBindingTests.js +++ b/tests/framework-tests/core/js/DataBindingTests.js @@ -1470,7 +1470,7 @@ fluid.defaults("fluid.tests.fluid6395root", { } }); -jqUnit.test("FLUID-6395: Performance of relay rules with increasing fan-out", function () { +jqUnit.test("FLUID-6395: Performance of relay rules with increasing fan-out - fine-grained sharing", function () { var that = fluid.tests.fluid6395root(); var children = fluid.queryIoCSelector(that, "fluid.modelComponent"); jqUnit.assertEquals("Constructed " + that.options.fanOut + " children: ", that.options.fanOut, children.length); @@ -1486,7 +1486,17 @@ fluid.defaults("fluid.tests.fluid6395root2", { fanOut: 100, source: "@expand:fluid.tests.fluid6395generate({that}.options.fanOut)", model: { - someValue: 42 + focus: {row: 0, col: 0} + }, + components: { + midChild: { + type: "fluid.modelComponent", + options: { + model: { + focus: "{fluid6395root2}.model.focus" + } + } + } }, dynamicComponents: { child: { @@ -1494,23 +1504,23 @@ fluid.defaults("fluid.tests.fluid6395root2", { type: "fluid.modelComponent", options: { model: { - parentModel: "{fluid6395root2}.model" + focus: "{midChild}.model.focus" } } } } }); -jqUnit.test("FLUID-6395: Performance of relay rules with increasing fan-out", function () { +jqUnit.test("FLUID-6395 II: Performance of relay rules with increasing fan-out - coarse-grained sharing", function () { var that = fluid.tests.fluid6395root2(); var children = fluid.queryIoCSelector(that, "fluid.modelComponent"); - jqUnit.assertEquals("Constructed " + that.options.fanOut + " children: ", that.options.fanOut, children.length); + jqUnit.assertEquals("Constructed " + that.options.fanOut + " children: ", that.options.fanOut + 1, children.length); var now = Date.now(); - // children[0].applier.change("parentModel.someValue", 999); + children[1].applier.change("focus", {row: 7, col: 5}); fluid.count = 0; - that.applier.change("someValue", 999); + // that.applier.change("someValue", 999); var elapsed = Date.now() - now; - jqUnit.assertEquals("Change relayed to parent model ", 999, that.model.someValue); + jqUnit.assertEquals("Change relayed to parent model ", 7, that.model.focus.row); fluid.log("Applied change in " + elapsed + "ms"); });