[IMP] tools: rework benchmarks application

closes #130
This commit is contained in:
Géry Debongnie
2019-06-11 14:44:30 +02:00
parent 710f42d4e4
commit 2840794fa2
27 changed files with 1796 additions and 459 deletions
+93 -10
View File
@@ -1,4 +1,9 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js"; import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
odoo.define("app", function(require) { odoo.define("app", function(require) {
const Widget = require("web.Widget"); const Widget = require("web.Widget");
@@ -97,14 +102,23 @@ odoo.define("app", function(require) {
"click .o_btn_msg.10000": function() { "click .o_btn_msg.10000": function() {
this.addMessages(10000); this.addMessages(10000);
}, },
"click .o_btn_msg.50000": function() { "click .o_btn_msg.30000": function() {
this.addMessages(50000); this.addMessages(30000);
}, },
"click .updateSomeMessages": function() { "click .updateSomeMessages": function() {
this.updateSomeMessages(); this.updateSomeMessages();
}, },
"click .clear": function() { "click .clear": function() {
this.clear(); this.clear();
},
"click .o_multiple": function() {
this.multipleFlag = !this.multipleFlag;
},
"click .o_clear": function() {
this.clearFlag = !this.clearFlag;
},
"click .clear-log": function() {
this.$log[0].innerHTML = "";
} }
}, },
custom_events: { custom_events: {
@@ -116,11 +130,15 @@ odoo.define("app", function(require) {
this.widgets = {}; this.widgets = {};
this.isInDom = false; this.isInDom = false;
this.messageCount = 0; this.messageCount = 0;
this.multipleFlag = false;
this.clearFlag = false;
}, },
start: function() { start: function() {
this.$content = this.$(".content"); this.$content = this.$(".content");
this.$msgCount = this.$(".message_count"); this.$msgCount = this.$(".message_count");
this.$log = this.$(".log-content");
this.log("Benchmarking odoo widgets, 12.0");
}, },
on_attach_callback: function() { on_attach_callback: function() {
this.isInDom = true; this.isInDom = true;
@@ -144,8 +162,8 @@ odoo.define("app", function(require) {
}, },
addMessages: function(n) { addMessages: function(n) {
var self = this; const self = this;
startMeasure("add " + n); this.benchmark("add " + n, () => {
const defs = []; const defs = [];
const messages = buildData(n); const messages = buildData(n);
for (let message of messages) { for (let message of messages) {
@@ -153,7 +171,7 @@ odoo.define("app", function(require) {
this.widgets[message.id] = widget; this.widgets[message.id] = widget;
defs.push(widget.appendTo("<div>")); defs.push(widget.appendTo("<div>"));
} }
$.when return $.when
.apply($, defs) .apply($, defs)
.then(function() { .then(function() {
for (let message of messages) { for (let message of messages) {
@@ -167,11 +185,18 @@ odoo.define("app", function(require) {
.then(function() { .then(function() {
self.messageCount += n; self.messageCount += n;
self.updateMessageCount(); self.updateMessageCount();
stopMeasure(); });
}); });
}, },
clear: function() { clear: function() {
startMeasure("clear"); startMeasure("clear");
this._clear();
stopMeasure(info => {
this.log(info.msg);
});
},
_clear: function() {
this.$content.empty(); this.$content.empty();
for (let key in this.widgets) { for (let key in this.widgets) {
this.widgets[key].destroy(); this.widgets[key].destroy();
@@ -179,15 +204,14 @@ odoo.define("app", function(require) {
} }
this.messageCount = 0; this.messageCount = 0;
this.updateMessageCount(); this.updateMessageCount();
stopMeasure();
}, },
updateSomeMessages: function() { updateSomeMessages: function() {
startMeasure("update every 10th"); this.benchmark("update every 10th", () => {
const widgets = Object.values(this.widgets); const widgets = Object.values(this.widgets);
for (let i = 0; i < widgets.length; i += 10) { for (let i = 0; i < widgets.length; i += 10) {
widgets[i].update(); widgets[i].update();
} }
stopMeasure(); });
}, },
_onRemoveMessage: function(ev) { _onRemoveMessage: function(ev) {
startMeasure("remove message"); startMeasure("remove message");
@@ -196,6 +220,65 @@ odoo.define("app", function(require) {
this.messageCount--; this.messageCount--;
this.updateMessageCount(); this.updateMessageCount();
stopMeasure(); stopMeasure();
},
benchmark: function(message, fn, callback) {
if (this.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.clearFlag) {
this._benchmark("clear", this._clear.bind(this), finalize, false);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
},
_benchmark: function(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
const benchmark = fn();
(benchmark && benchmark.then ? benchmark : $.when()).then(() => {
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
});
},
log: function(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.$log[0].appendChild(div);
this.$log[0].scrollTop = this.$log[0].scrollHeight;
} }
}); });
@@ -1,16 +1,33 @@
<templates> <templates>
<div class="main" t-name="root"> <div class="main" t-name="root">
<div class="left-thing"> <div class="left-thing">
<div class="message_count"> <div class="title">Actions</div>
Number of msg: 0 <div class="panel">
</div>
<button class="o_btn_msg 100">Add 100 messages</button> <button class="o_btn_msg 100">Add 100 messages</button>
<button class="o_btn_msg 1000">Add 1000 messages</button> <button class="o_btn_msg 1000">Add 1k messages</button>
<button class="o_btn_msg 10000">Add 10000 messages</button> <button class="o_btn_msg 10000">Add 10k messages</button>
<button class="o_btn_msg 50000">Add 50000 messages</button> <button class="o_btn_msg 30000">Add 30k messages</button>
<button class="updateSomeMessages">Update every 10th message</button> <button class="updateSomeMessages">Update every 10th message</button>
<button class="clear">Clear</button> <button class="clear">Clear</button>
</div> </div>
<div class="flags">
<div>
<input type="checkbox" class="o_multiple" id="multipleflag" />
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" class="o_clear" id="clearFlag" />
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info message_count">
Number of messages: 0
</div>
<div class="title">Log <span class="clear-log">(clear)</span></div>
<div class="log">
<div class="log-content"/>
</div>
</div>
<div class="right-thing"> <div class="right-thing">
<div class="content"> <div class="content">
</div> </div>
+93 -10
View File
@@ -1,4 +1,9 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js"; import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
odoo.define("app", function(require) { odoo.define("app", function(require) {
const Widget = require("web.Widget"); const Widget = require("web.Widget");
@@ -97,14 +102,23 @@ odoo.define("app", function(require) {
"click .o_btn_msg.10000": function() { "click .o_btn_msg.10000": function() {
this.addMessages(10000); this.addMessages(10000);
}, },
"click .o_btn_msg.50000": function() { "click .o_btn_msg.30000": function() {
this.addMessages(50000); this.addMessages(30000);
}, },
"click .updateSomeMessages": function() { "click .updateSomeMessages": function() {
this.updateSomeMessages(); this.updateSomeMessages();
}, },
"click .clear": function() { "click .clear": function() {
this.clear(); this.clear();
},
"click .o_multiple": function() {
this.multipleFlag = !this.multipleFlag;
},
"click .o_clear": function() {
this.clearFlag = !this.clearFlag;
},
"click .clear-log": function() {
this.$log[0].innerHTML = "";
} }
}, },
custom_events: { custom_events: {
@@ -116,11 +130,15 @@ odoo.define("app", function(require) {
this.widgets = {}; this.widgets = {};
this.isInDom = false; this.isInDom = false;
this.messageCount = 0; this.messageCount = 0;
this.multipleFlag = false;
this.clearFlag = false;
}, },
start: function() { start: function() {
this.$content = this.$(".content"); this.$content = this.$(".content");
this.$msgCount = this.$(".message_count"); this.$msgCount = this.$(".message_count");
this.$log = this.$(".log-content");
this.log("Benchmarking odoo widgets, 12.3");
}, },
on_attach_callback: function() { on_attach_callback: function() {
this.isInDom = true; this.isInDom = true;
@@ -144,8 +162,8 @@ odoo.define("app", function(require) {
}, },
addMessages: function(n) { addMessages: function(n) {
var self = this; const self = this;
startMeasure("add " + n); this.benchmark("add " + n, () => {
const defs = []; const defs = [];
const messages = buildData(n); const messages = buildData(n);
for (let message of messages) { for (let message of messages) {
@@ -153,7 +171,7 @@ odoo.define("app", function(require) {
this.widgets[message.id] = widget; this.widgets[message.id] = widget;
defs.push(widget.appendTo("<div>")); defs.push(widget.appendTo("<div>"));
} }
$.when return $.when
.apply($, defs) .apply($, defs)
.then(function() { .then(function() {
for (let message of messages) { for (let message of messages) {
@@ -167,11 +185,18 @@ odoo.define("app", function(require) {
.then(function() { .then(function() {
self.messageCount += n; self.messageCount += n;
self.updateMessageCount(); self.updateMessageCount();
stopMeasure(); });
}); });
}, },
clear: function() { clear: function() {
startMeasure("clear"); startMeasure("clear");
this._clear();
stopMeasure(info => {
this.log(info.msg);
});
},
_clear: function() {
this.$content.empty(); this.$content.empty();
for (let key in this.widgets) { for (let key in this.widgets) {
this.widgets[key].destroy(); this.widgets[key].destroy();
@@ -179,15 +204,14 @@ odoo.define("app", function(require) {
} }
this.messageCount = 0; this.messageCount = 0;
this.updateMessageCount(); this.updateMessageCount();
stopMeasure();
}, },
updateSomeMessages: function() { updateSomeMessages: function() {
startMeasure("update every 10th"); this.benchmark("update every 10th", () => {
const widgets = Object.values(this.widgets); const widgets = Object.values(this.widgets);
for (let i = 0; i < widgets.length; i += 10) { for (let i = 0; i < widgets.length; i += 10) {
widgets[i].update(); widgets[i].update();
} }
stopMeasure(); });
}, },
_onRemoveMessage: function(ev) { _onRemoveMessage: function(ev) {
startMeasure("remove message"); startMeasure("remove message");
@@ -196,6 +220,65 @@ odoo.define("app", function(require) {
this.messageCount--; this.messageCount--;
this.updateMessageCount(); this.updateMessageCount();
stopMeasure(); stopMeasure();
},
benchmark: function(message, fn, callback) {
if (this.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.clearFlag) {
this._benchmark("clear", this._clear.bind(this), finalize, false);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
},
_benchmark: function(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
const benchmark = fn();
(benchmark && benchmark.then ? benchmark : $.when()).then(() => {
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
});
},
log: function(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.$log[0].appendChild(div);
this.$log[0].scrollTop = this.$log[0].scrollHeight;
} }
}); });
@@ -1,16 +1,33 @@
<templates> <templates>
<div class="main" t-name="root"> <div class="main" t-name="root">
<div class="left-thing"> <div class="left-thing">
<div class="message_count"> <div class="title">Actions</div>
Number of msg: 0 <div class="panel">
</div>
<button class="o_btn_msg 100">Add 100 messages</button> <button class="o_btn_msg 100">Add 100 messages</button>
<button class="o_btn_msg 1000">Add 1000 messages</button> <button class="o_btn_msg 1000">Add 1k messages</button>
<button class="o_btn_msg 10000">Add 10000 messages</button> <button class="o_btn_msg 10000">Add 10k messages</button>
<button class="o_btn_msg 50000">Add 50000 messages</button> <button class="o_btn_msg 30000">Add 30k messages</button>
<button class="updateSomeMessages">Update every 10th message</button> <button class="updateSomeMessages">Update every 10th message</button>
<button class="clear">Clear</button> <button class="clear">Clear</button>
</div> </div>
<div class="flags">
<div>
<input type="checkbox" class="o_multiple" id="multipleflag" />
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" class="o_clear" id="clearFlag" />
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info message_count">
Number of messages: 0
</div>
<div class="title">Log <span class="clear-log">(clear)</span></div>
<div class="log">
<div class="log-content"/>
</div>
</div>
<div class="right-thing"> <div class="right-thing">
<div class="content"> <div class="content">
</div> </div>
+103 -14
View File
@@ -1,11 +1,16 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js"; import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Likes Counter Widget // Likes Counter Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class Counter extends owl.Component { class Counter extends owl.Component {
template = "counter";
state = { counter: 0 }; state = { counter: 0 };
template = "Counter";
increment() { increment() {
this.state.counter++; this.state.counter++;
@@ -16,8 +21,8 @@ class Counter extends owl.Component {
// Message Widget // Message Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class Message extends owl.Component { class Message extends owl.Component {
template = "message";
widgets = { Counter }; widgets = { Counter };
template = "Message";
shouldUpdate(nextProps) { shouldUpdate(nextProps) {
return nextProps !== this.props; return nextProps !== this.props;
@@ -33,39 +38,123 @@ class Message extends owl.Component {
// Root Widget // Root Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class App extends owl.Component { class App extends owl.Component {
template = "root";
widgets = { Message }; widgets = { Message };
state = { messages: [] }; state = { messages: [], multipleFlag: false, clearAfterFlag: false };
template = "App";
mounted() {
this.log(
`Benchmarking Owl v${owl._version} (build date: ${
owl._date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) { addMessages(n) {
startMeasure("add " + n); this.benchmark("add " + n, () => {
const newMessages = buildData(n); const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages); this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure(); });
} }
clear() { clear() {
startMeasure("clear"); this._benchmark("clear", () => {
this.state.messages = []; this.state.messages = [];
stopMeasure(); });
} }
updateSomeMessages() { updateSomeMessages() {
startMeasure("update every 10th"); this.benchmark("update every 10th", () => {
const messages = this.state.messages; const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) { for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]); const msg = Object.assign({}, messages[i]);
msg.author += '!!!'; msg.author += "!!!";
this.set(messages, i, msg); this.set(messages, i, msg);
} }
stopMeasure(); });
} }
removeMessage(data) { removeMessage(data) {
startMeasure("remove message"); this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === data.id); const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1); this.state.messages.splice(index, 1);
stopMeasure(); });
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
} }
} }
+27 -9
View File
@@ -1,16 +1,34 @@
<templates> <templates>
<div t-name="root" class="main"> <div t-name="App" class="main">
<div class="left-thing"> <div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div> <div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button> <button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button> <button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button> <button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button> <button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button> <button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button> <button t-on-click="clear">Clear</button>
</div> </div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="'log'"/>
</div>
</div>
<div class="right-thing"> <div class="right-thing">
<div class="content"> <div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message"> <t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/> <t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
</t> </t>
@@ -18,14 +36,14 @@
</div> </div>
</div> </div>
<div t-name="message" class="message"> <div t-name="Message" class="message">
<span class="author"><t t-esc="props.author"/></span> <span class="author"><t t-esc="props.author"/></span>
<span class="msg"><t t-esc="props.msg"/></span> <span class="msg"><t t-esc="props.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button> <button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter"/> <t t-widget="Counter"/>
</div> </div>
<div t-name="counter"> <div t-name="Counter">
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button> <button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
</div> </div>
+100 -14
View File
@@ -1,10 +1,14 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js"; import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Likes Counter Widget // Likes Counter Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class Counter extends owl.Component { class Counter extends owl.Component {
template = "counter";
state = { counter: 0 }; state = { counter: 0 };
increment() { increment() {
@@ -16,7 +20,6 @@ class Counter extends owl.Component {
// Message Widget // Message Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class Message extends owl.Component { class Message extends owl.Component {
template = "message";
widgets = { Counter }; widgets = { Counter };
shouldUpdate(nextProps) { shouldUpdate(nextProps) {
@@ -33,39 +36,122 @@ class Message extends owl.Component {
// Root Widget // Root Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class App extends owl.Component { class App extends owl.Component {
template = "root";
widgets = { Message }; widgets = { Message };
state = { messages: [] }; state = { messages: [], multipleFlag: false, clearAfterFlag: false };
mounted() {
this.log(
`Benchmarking Owl v${owl._version} (build date: ${
owl._date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) { addMessages(n) {
startMeasure("add " + n); this.benchmark("add " + n, () => {
const newMessages = buildData(n); const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages); this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure(); });
} }
clear() { clear() {
startMeasure("clear"); this._benchmark("clear", () => {
this.state.messages = []; this.state.messages = [];
stopMeasure(); });
} }
updateSomeMessages() { updateSomeMessages() {
startMeasure("update every 10th"); this.benchmark("update every 10th", () => {
const messages = this.state.messages; const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) { for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]); const msg = Object.assign({}, messages[i]);
msg.author += '!!!'; msg.author += "!!!";
this.set(messages, i, msg); this.set(messages, i, msg);
} }
stopMeasure(); });
} }
removeMessage(data) { removeMessage(data) {
startMeasure("remove message"); this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === data.id); const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1); this.state.messages.splice(index, 1);
stopMeasure(); });
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
} }
} }
+27 -9
View File
@@ -1,16 +1,34 @@
<templates> <templates>
<div t-name="root" class="main"> <div t-name="App" class="main">
<div class="left-thing"> <div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div> <div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button> <button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button> <button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button> <button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button> <button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button> <button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button> <button t-on-click="clear">Clear</button>
</div> </div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="'log'"/>
</div>
</div>
<div class="right-thing"> <div class="right-thing">
<div class="content"> <div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message"> <t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/> <t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
</t> </t>
@@ -18,14 +36,14 @@
</div> </div>
</div> </div>
<div t-name="message" class="message"> <div t-name="Message" class="message">
<span class="author"><t t-esc="props.author"/></span> <span class="author"><t t-esc="props.author"/></span>
<span class="msg"><t t-esc="props.msg"/></span> <span class="msg"><t t-esc="props.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button> <button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter"/> <t t-widget="Counter"/>
</div> </div>
<div t-name="counter"> <div t-name="Counter">
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button> <button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
</div> </div>
+100 -14
View File
@@ -1,10 +1,14 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js"; import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Likes Counter Widget // Likes Counter Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class Counter extends owl.Component { class Counter extends owl.Component {
template = "counter";
state = { counter: 0 }; state = { counter: 0 };
increment() { increment() {
@@ -16,7 +20,6 @@ class Counter extends owl.Component {
// Message Widget // Message Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class Message extends owl.Component { class Message extends owl.Component {
template = "message";
widgets = { Counter }; widgets = { Counter };
shouldUpdate(nextProps) { shouldUpdate(nextProps) {
@@ -33,39 +36,122 @@ class Message extends owl.Component {
// Root Widget // Root Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class App extends owl.Component { class App extends owl.Component {
template = "root";
widgets = { Message }; widgets = { Message };
state = { messages: [] }; state = { messages: [], multipleFlag: false, clearAfterFlag: false };
mounted() {
this.log(
`Benchmarking Owl v${owl.__info__.version} (build date: ${
owl.__info__.date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) { addMessages(n) {
startMeasure("add " + n); this.benchmark("add " + n, () => {
const newMessages = buildData(n); const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages); this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure(); });
} }
clear() { clear() {
startMeasure("clear"); this.benchmark("clear", () => {
this.state.messages = []; this.state.messages = [];
stopMeasure(); });
} }
updateSomeMessages() { updateSomeMessages() {
startMeasure("update every 10th"); this.benchmark("update every 10th", () => {
const messages = this.state.messages; const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) { for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]); const msg = Object.assign({}, messages[i]);
msg.author += '!!!'; msg.author += "!!!";
this.set(messages, i, msg); this.set(messages, i, msg);
} }
stopMeasure(); });
} }
removeMessage(data) { removeMessage(data) {
startMeasure("remove message"); this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === data.id); const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1); this.state.messages.splice(index, 1);
stopMeasure(); });
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
} }
} }
+27 -9
View File
@@ -1,16 +1,34 @@
<templates> <templates>
<div t-name="root" class="main"> <div t-name="App" class="main">
<div class="left-thing"> <div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div> <div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button> <button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button> <button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button> <button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button> <button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button> <button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button> <button t-on-click="clear">Clear</button>
</div> </div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="log"/>
</div>
</div>
<div class="right-thing"> <div class="right-thing">
<div class="content"> <div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message"> <t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/> <t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
</t> </t>
@@ -18,14 +36,14 @@
</div> </div>
</div> </div>
<div t-name="message" class="message"> <div t-name="Message" class="message">
<span class="author"><t t-esc="props.author"/></span> <span class="author"><t t-esc="props.author"/></span>
<span class="msg"><t t-esc="props.msg"/></span> <span class="msg"><t t-esc="props.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button> <button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter"/> <t t-widget="Counter"/>
</div> </div>
<div t-name="counter"> <div t-name="Counter">
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button> <button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
</div> </div>
+103 -17
View File
@@ -1,10 +1,14 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js"; import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Likes Counter Widget // Likes Counter Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class Counter extends owl.Component { class Counter extends owl.Component {
template = "counter";
state = { counter: 0 }; state = { counter: 0 };
increment() { increment() {
@@ -16,15 +20,14 @@ class Counter extends owl.Component {
// Message Widget // Message Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class Message extends owl.Component { class Message extends owl.Component {
template = "message";
widgets = { Counter }; widgets = { Counter };
shouldUpdate(nextProps) { shouldUpdate(nextProps) {
return nextProps.message !== this.props.message; return nextProps.message !== this.props.message;
} }
removeMessage() { removeMessage() {
this.trigger("remove_message", { this.trigger("remove-message", {
id: this.props.id id: this.props.message.id
}); });
} }
} }
@@ -33,39 +36,122 @@ class Message extends owl.Component {
// Root Widget // Root Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class App extends owl.Component { class App extends owl.Component {
template = "root";
widgets = { Message }; widgets = { Message };
state = { messages: [] }; state = { messages: [], multipleFlag: false, clearAfterFlag: false };
mounted() {
this.log(
`Benchmarking Owl v${owl.__info__.version} (build date: ${
owl.__info__.date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) { addMessages(n) {
startMeasure("add " + n); this.benchmark("add " + n, () => {
const newMessages = buildData(n); const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages); this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure(); });
} }
clear() { clear() {
startMeasure("clear"); this._benchmark("clear", () => {
this.state.messages = []; this.state.messages = [];
stopMeasure(); });
} }
updateSomeMessages() { updateSomeMessages() {
startMeasure("update every 10th"); this.benchmark("update every 10th", () => {
const messages = this.state.messages; const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) { for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]); const msg = Object.assign({}, messages[i]);
msg.author += "!!!"; msg.author += "!!!";
this.set(messages, i, msg); this.set(messages, i, msg);
} }
stopMeasure(); });
} }
removeMessage(data) { removeMessage(event) {
startMeasure("remove message"); this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === data.id); const index = this.state.messages.findIndex(m => m.id === event.detail.id);
this.state.messages.splice(index, 1); this.state.messages.splice(index, 1);
stopMeasure(); });
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
} }
} }
+28 -12
View File
@@ -1,33 +1,49 @@
<templates> <templates>
<div t-name="root" class="main"> <div t-name="App" class="main">
<div class="left-thing"> <div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div> <div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button> <button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button> <button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button> <button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button> <button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button> <button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button> <button t-on-click="clear">Clear</button>
</div> </div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="log"/>
</div>
</div>
<div class="right-thing"> <div class="right-thing">
<div class="content"> <div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message"> <t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" <t t-widget="Message" t-key="message.id" message="message"/>
message="message"
t-on-remove_message="removeMessage"/>
</t> </t>
</div> </div>
</div> </div>
</div> </div>
<div t-name="message" class="message"> <div t-name="Message" class="message">
<span class="author"><t t-esc="props.message.author"/></span> <span class="author"><t t-esc="props.message.author"/></span>
<span class="msg"><t t-esc="props.message.msg"/></span> <span class="msg"><t t-esc="props.message.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button> <button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter"/> <t t-widget="Counter"/>
</div> </div>
<div t-name="counter"> <div t-name="Counter">
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button> <button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
</div> </div>
+103 -15
View File
@@ -1,11 +1,16 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js"; import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Likes Counter Widget // Likes Counter Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class Counter extends owl.Component { class Counter extends owl.Component {
template = "counter";
state = { counter: 0 }; state = { counter: 0 };
template = "Counter";
increment() { increment() {
this.state.counter++; this.state.counter++;
@@ -16,13 +21,12 @@ class Counter extends owl.Component {
// Message Widget // Message Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class Message extends owl.Component { class Message extends owl.Component {
template = "message";
widgets = { Counter }; widgets = { Counter };
template = "Message";
shouldUpdate(nextProps) { shouldUpdate(nextProps) {
return nextProps !== this.props; return nextProps !== this.props;
} }
removeMessage() { removeMessage() {
this.trigger("remove_message", { this.trigger("remove_message", {
id: this.props.id id: this.props.id
@@ -34,39 +38,123 @@ class Message extends owl.Component {
// Root Widget // Root Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class App extends owl.Component { class App extends owl.Component {
template = "root";
widgets = { Message }; widgets = { Message };
state = { messages: [] }; state = { messages: [], multipleFlag: false, clearAfterFlag: false };
template = "App";
mounted() {
this.log(
`Benchmarking Owl v${owl._version} (build date: ${
owl._date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) { addMessages(n) {
startMeasure("add " + n); this.benchmark("add " + n, () => {
const newMessages = buildData(n); const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages); this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure(); });
} }
clear() { clear() {
startMeasure("clear"); this._benchmark("clear", () => {
this.state.messages = []; this.state.messages = [];
stopMeasure(); });
} }
updateSomeMessages() { updateSomeMessages() {
startMeasure("update every 10th"); this.benchmark("update every 10th", () => {
const messages = this.state.messages; const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) { for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]); const msg = Object.assign({}, messages[i]);
msg.author += '!!!'; msg.author += "!!!";
this.set(messages, i, msg); this.set(messages, i, msg);
} }
stopMeasure(); });
} }
removeMessage(data) { removeMessage(data) {
startMeasure('remove message'); this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === data.id); const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1); this.state.messages.splice(index, 1);
stopMeasure(); });
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
} }
} }
+27 -9
View File
@@ -1,16 +1,34 @@
<templates> <templates>
<div t-name="root" class="main"> <div t-name="App" class="main">
<div class="left-thing"> <div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div> <div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button> <button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button> <button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button> <button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button> <button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button> <button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button> <button t-on-click="clear">Clear</button>
</div> </div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="'log'"/>
</div>
</div>
<div class="right-thing"> <div class="right-thing">
<div class="content"> <div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message"> <t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/> <t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
</t> </t>
@@ -18,14 +36,14 @@
</div> </div>
</div> </div>
<div t-name="message" class="message"> <div t-name="Message" class="message">
<span class="author"><t t-esc="props.author"/></span> <span class="author"><t t-esc="props.author"/></span>
<span class="msg"><t t-esc="props.msg"/></span> <span class="msg"><t t-esc="props.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button> <button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter"/> <t t-widget="Counter"/>
</div> </div>
<div t-name="counter"> <div t-name="Counter">
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button> <button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
</div> </div>
+103 -14
View File
@@ -1,11 +1,16 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js"; import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Likes Counter Widget // Likes Counter Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class Counter extends owl.Component { class Counter extends owl.Component {
template = "counter";
state = { counter: 0 }; state = { counter: 0 };
template = "Counter";
increment() { increment() {
this.state.counter++; this.state.counter++;
@@ -16,8 +21,8 @@ class Counter extends owl.Component {
// Message Widget // Message Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class Message extends owl.Component { class Message extends owl.Component {
template = "message";
widgets = { Counter }; widgets = { Counter };
template = "Message";
shouldUpdate(nextProps) { shouldUpdate(nextProps) {
return nextProps !== this.props; return nextProps !== this.props;
@@ -33,39 +38,123 @@ class Message extends owl.Component {
// Root Widget // Root Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class App extends owl.Component { class App extends owl.Component {
template = "root";
widgets = { Message }; widgets = { Message };
state = { messages: [] }; state = { messages: [], multipleFlag: false, clearAfterFlag: false };
template = "App";
mounted() {
this.log(
`Benchmarking Owl v${owl._version} (build date: ${
owl._date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) { addMessages(n) {
startMeasure("add " + n); this.benchmark("add " + n, () => {
const newMessages = buildData(n); const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages); this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure(); });
} }
clear() { clear() {
startMeasure("clear"); this._benchmark("clear", () => {
this.state.messages = []; this.state.messages = [];
stopMeasure(); });
} }
updateSomeMessages() { updateSomeMessages() {
startMeasure("update every 10th"); this.benchmark("update every 10th", () => {
const messages = this.state.messages; const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) { for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]); const msg = Object.assign({}, messages[i]);
msg.author += '!!!'; msg.author += "!!!";
this.set(messages, i, msg); this.set(messages, i, msg);
} }
stopMeasure(); });
} }
removeMessage(data) { removeMessage(data) {
startMeasure("remove message"); this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === data.id); const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1); this.state.messages.splice(index, 1);
stopMeasure(); });
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
} }
} }
+27 -9
View File
@@ -1,16 +1,34 @@
<templates> <templates>
<div t-name="root" class="main"> <div t-name="App" class="main">
<div class="left-thing"> <div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div> <div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button> <button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button> <button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button> <button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button> <button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button> <button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button> <button t-on-click="clear">Clear</button>
</div> </div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="'log'"/>
</div>
</div>
<div class="right-thing"> <div class="right-thing">
<div class="content"> <div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message"> <t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/> <t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
</t> </t>
@@ -18,14 +36,14 @@
</div> </div>
</div> </div>
<div t-name="message" class="message"> <div t-name="Message" class="message">
<span class="author"><t t-esc="props.author"/></span> <span class="author"><t t-esc="props.author"/></span>
<span class="msg"><t t-esc="props.msg"/></span> <span class="msg"><t t-esc="props.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button> <button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter"/> <t t-widget="Counter"/>
</div> </div>
<div t-name="counter"> <div t-name="Counter">
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button> <button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
</div> </div>
+103 -14
View File
@@ -1,11 +1,16 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js"; import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Likes Counter Widget // Likes Counter Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class Counter extends owl.Component { class Counter extends owl.Component {
template = "counter";
state = { counter: 0 }; state = { counter: 0 };
template = "Counter";
increment() { increment() {
this.state.counter++; this.state.counter++;
@@ -16,8 +21,8 @@ class Counter extends owl.Component {
// Message Widget // Message Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class Message extends owl.Component { class Message extends owl.Component {
template = "message";
widgets = { Counter }; widgets = { Counter };
template = "Message";
shouldUpdate(nextProps) { shouldUpdate(nextProps) {
return nextProps !== this.props; return nextProps !== this.props;
@@ -33,39 +38,123 @@ class Message extends owl.Component {
// Root Widget // Root Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class App extends owl.Component { class App extends owl.Component {
template = "root";
widgets = { Message }; widgets = { Message };
state = { messages: [] }; state = { messages: [], multipleFlag: false, clearAfterFlag: false };
template = "App";
mounted() {
this.log(
`Benchmarking Owl v${owl._version} (build date: ${
owl._date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) { addMessages(n) {
startMeasure("add " + n); this.benchmark("add " + n, () => {
const newMessages = buildData(n); const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages); this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure(); });
} }
clear() { clear() {
startMeasure("clear"); this._benchmark("clear", () => {
this.state.messages = []; this.state.messages = [];
stopMeasure(); });
} }
updateSomeMessages() { updateSomeMessages() {
startMeasure("update every 10th"); this.benchmark("update every 10th", () => {
const messages = this.state.messages; const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) { for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]); const msg = Object.assign({}, messages[i]);
msg.author += '!!!'; msg.author += "!!!";
this.set(messages, i, msg); this.set(messages, i, msg);
} }
stopMeasure(); });
} }
removeMessage(data) { removeMessage(data) {
startMeasure("remove message"); this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === data.id); const index = this.state.messages.findIndex(m => m.id === data.id);
this.state.messages.splice(index, 1); this.state.messages.splice(index, 1);
stopMeasure(); });
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
} }
} }
+27 -9
View File
@@ -1,16 +1,34 @@
<templates> <templates>
<div t-name="root" class="main"> <div t-name="App" class="main">
<div class="left-thing"> <div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div> <div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button> <button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button> <button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button> <button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button> <button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button> <button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button> <button t-on-click="clear">Clear</button>
</div> </div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="'log'"/>
</div>
</div>
<div class="right-thing"> <div class="right-thing">
<div class="content"> <div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message"> <t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/> <t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
</t> </t>
@@ -18,14 +36,14 @@
</div> </div>
</div> </div>
<div t-name="message" class="message"> <div t-name="Message" class="message">
<span class="author"><t t-esc="props.author"/></span> <span class="author"><t t-esc="props.author"/></span>
<span class="msg"><t t-esc="props.msg"/></span> <span class="msg"><t t-esc="props.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button> <button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter"/> <t t-widget="Counter"/>
</div> </div>
<div t-name="counter"> <div t-name="Counter">
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button> <button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
</div> </div>
+105 -16
View File
@@ -1,4 +1,9 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js"; import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Likes Counter Widget // Likes Counter Widget
@@ -18,11 +23,11 @@ class Message extends owl.Component {
widgets = { Counter }; widgets = { Counter };
shouldUpdate(nextProps) { shouldUpdate(nextProps) {
return nextProps !== this.props; return nextProps.message !== this.props.message;
} }
removeMessage() { removeMessage() {
this.trigger("remove_message", { this.trigger("remove-message", {
id: this.props.id id: this.props.message.id
}); });
} }
} }
@@ -32,38 +37,122 @@ class Message extends owl.Component {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class App extends owl.Component { class App extends owl.Component {
widgets = { Message }; widgets = { Message };
state = { messages: [] }; state = { messages: [], multipleFlag: false, clearAfterFlag: false };
mounted() {
this.log(
`Benchmarking Owl v${owl.__info__.version} (build date: ${
owl.__info__.date
})`
);
}
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.state.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
addMessages(n) { addMessages(n) {
startMeasure("add " + n); this.benchmark("add " + n, () => {
const newMessages = buildData(n); const newMessages = buildData(n);
this.state.messages.push.apply(this.state.messages, newMessages); this.state.messages.push.apply(this.state.messages, newMessages);
stopMeasure(); });
} }
clear() { clear() {
startMeasure("clear"); this._benchmark("clear", () => {
this.state.messages = []; this.state.messages = [];
stopMeasure(); });
} }
updateSomeMessages() { updateSomeMessages() {
startMeasure("update every 10th"); this.benchmark("update every 10th", () => {
const setState = owl.Observer.set; const setState = owl.Observer.set;
const messages = this.state.messages; const messages = this.state.messages;
for (let i = 0; i < this.state.messages.length; i += 10) { for (let i = 0; i < this.state.messages.length; i += 10) {
const msg = Object.assign({}, messages[i]); const msg = Object.assign({}, messages[i]);
msg.author += '!!!'; msg.author += "!!!";
setState(messages, i, msg); setState(messages, i, msg);
} }
stopMeasure(); });
} }
removeMessage(data) { removeMessage(event) {
startMeasure("remove message"); this.benchmark("remove message", () => {
const index = this.state.messages.findIndex(m => m.id === data.id); const index = this.state.messages.findIndex(m => m.id === event.detail.id);
this.state.messages.splice(index, 1); this.state.messages.splice(index, 1);
stopMeasure(); });
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.log.appendChild(div);
this.refs.log.scrollTop = this.refs.log.scrollHeight;
}
clearLog() {
this.refs.log.innerHTML = "";
}
toggleMultiple() {
this.state.multipleFlag = !this.state.multipleFlag;
}
toggleClear() {
this.state.clearAfterFlag = !this.state.clearAfterFlag;
} }
} }
-1
View File
@@ -7,7 +7,6 @@
<script src='../../owl.js'></script> <script src='../../owl.js'></script>
</head> </head>
<body> <body>
<div id='main'></div>
<script src='app.js' type="module"></script> <script src='app.js' type="module"></script>
</body> </body>
</html> </html>
+25 -7
View File
@@ -1,18 +1,36 @@
<templates> <templates>
<div t-name="App" class="main"> <div t-name="App" class="main">
<div class="left-thing"> <div class="left-thing">
<div>Number of msg: <t t-esc="state.messages.length"/></div> <div class="title">Actions</div>
<div class="panel">
<button t-on-click="addMessages(100)">Add 100 messages</button> <button t-on-click="addMessages(100)">Add 100 messages</button>
<button t-on-click="addMessages(1000)">Add 1000 messages</button> <button t-on-click="addMessages(1000)">Add 1k messages</button>
<button t-on-click="addMessages(10000)">Add 10000 messages</button> <button t-on-click="addMessages(10000)">Add 10k messages</button>
<button t-on-click="addMessages(50000)">Add 50000 messages</button> <button t-on-click="addMessages(30000)">Add 30k messages</button>
<button t-on-click="updateSomeMessages">Update every 10th message</button> <button t-on-click="updateSomeMessages">Update every 10th messages</button>
<button t-on-click="clear">Clear</button> <button t-on-click="clear">Clear</button>
</div> </div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
<hr/>
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" t-ref="log"/>
</div>
</div>
<div class="right-thing"> <div class="right-thing">
<div class="content"> <div class="content" t-on-remove-message="removeMessage">
<t t-foreach="state.messages" t-as="message"> <t t-foreach="state.messages" t-as="message">
<t t-widget="Message" t-key="message.id" message="message" t-on-remove_message="removeMessage"/> <t t-widget="Message" t-key="message.id" message="message"/>
</t> </t>
</div> </div>
</div> </div>
+145 -21
View File
@@ -1,4 +1,9 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js"; import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Counter Widget // Counter Widget
@@ -86,9 +91,33 @@ class Main extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
messages: [] messages: [],
multipleFlag: false,
clearAfterFlag: false
}; };
this.removeMessage = this.removeMessage.bind(this); this.removeMessage = this.removeMessage.bind(this);
this.toggleMultipleFlag = this.toggleMultipleFlag.bind(this);
this.toggleClearAfterFlag = this.toggleClearAfterFlag.bind(this);
}
componentDidMount() {
this.log(`Benchmarking React v${React.version}`);
}
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.refs.logref.appendChild(div);
this.refs.logref.scrollTop = this.refs.logref.scrollHeight;
}
toggleMultipleFlag() {
this.setState({ multipleFlag: !this.state.multipleFlag });
}
toggleClearAfterFlag() {
this.setState({ clearAfterFlag: !this.state.clearAfterFlag });
} }
render() { render() {
@@ -109,12 +138,10 @@ class Main extends React.Component {
{ {
className: "left-thing" className: "left-thing"
}, },
React.createElement("div", { className: "title" }, "Actions"),
React.createElement( React.createElement(
"div", "div",
null, { className: "panel" },
"Number of msg: ",
this.state.messages.length
),
React.createElement( React.createElement(
"button", "button",
{ {
@@ -127,28 +154,28 @@ class Main extends React.Component {
{ {
onClick: _ => this.addMessages(1000) onClick: _ => this.addMessages(1000)
}, },
"Add 1000 messages" "Add 1k messages"
), ),
React.createElement( React.createElement(
"button", "button",
{ {
onClick: _ => this.addMessages(10000) onClick: _ => this.addMessages(10000)
}, },
"Add 10000 messages" "Add 10k messages"
), ),
React.createElement( React.createElement(
"button", "button",
{ {
onClick: _ => this.addMessages(50000) onClick: _ => this.addMessages(30000)
}, },
"Add 50000 messages" "Add 30k messages"
), ),
React.createElement( React.createElement(
"button", "button",
{ {
onClick: _ => this.updateSomeMessages() onClick: _ => this.updateSomeMessages()
}, },
"Update every 10th messags" "Update every 10th message"
), ),
React.createElement( React.createElement(
"button", "button",
@@ -158,6 +185,53 @@ class Main extends React.Component {
"Clear" "Clear"
) )
), ),
React.createElement(
"div",
{ className: "flags" },
React.createElement(
"div",
null,
React.createElement("input", {
type: "checkbox",
id: "multipleFlag",
value: this.state.multipleFlag,
onChange: this.toggleMultipleFlag
}),
React.createElement("label", { for: "multipleFlag" }, " Do it 20x")
),
React.createElement(
"div",
null,
React.createElement("input", {
type: "checkbox",
id: "clearFlag",
value: this.state.clearAfterFlag,
onChange: this.toggleClearAfterFlag
}),
React.createElement("label", { for: "clearFlag" }, " Clear after")
)
),
React.createElement(
"div",
{ className: "info" },
"Number of messages: ",
this.state.messages.length
),
React.createElement(
"div",
{ className: "title" },
"Log ",
React.createElement("span", { className: "clear-log" }, "(clear)")
),
React.createElement(
"div",
{ className: "log" },
React.createElement("div", {
className: "log-content",
ref: "logref"
})
)
),
React.createElement( React.createElement(
"div", "div",
{ {
@@ -175,30 +249,30 @@ class Main extends React.Component {
} }
addMessages(n) { addMessages(n) {
startMeasure("add " + n); this.benchmark("add " + n, () => {
const newMessages = this.state.messages.concat(buildData(n)); const newMessages = this.state.messages.concat(buildData(n));
this.setState({ this.setState({
messages: newMessages messages: newMessages
}); });
stopMeasure(); });
} }
clear() { clear() {
startMeasure("clear"); this._benchmark("clear", () => {
this.setState({ this.setState({
messages: [] messages: []
}); });
stopMeasure(); });
} }
updateSomeMessages() { updateSomeMessages() {
startMeasure("update every 10th"); this.benchmark("update every 10th", () => {
const messages = this.state.messages; const messages = this.state.messages;
for (let i = 0; i < messages.length; i += 10) { for (let i = 0; i < messages.length; i += 10) {
messages[i].author += "!!!"; messages[i].author += "!!!";
} }
this.forceUpdate(); this.forceUpdate();
stopMeasure(); });
} }
removeMessage(id) { removeMessage(id) {
@@ -211,11 +285,61 @@ class Main extends React.Component {
}); });
stopMeasure(); stopMeasure();
} }
benchmark(message, fn, callback) {
if (this.state.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.state.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.setState({ messages: [] });
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
}
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
}
} //----------------------------- } //-----------------------------
// INIT // INIT
//----------------------------- //-----------------------------
ReactDOM.render( ReactDOM.render(React.createElement(Main, null), document.body);
React.createElement(Main, null),
document.getElementById("main")
);
+75 -10
View File
@@ -1,26 +1,91 @@
.main { html,
position: absolute; body {
left: 0; height: 100%;
right: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
top: 0; Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Arial,
bottom: 0; sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
body {
margin: 0;
}
.main {
height: 100%;
font-size: 14px;
display: grid; display: grid;
grid-template-columns: 220px 1fr; grid-template-columns: 640px 1fr;
} }
.left-thing { .left-thing {
background-color: gray; background-color: gray;
padding: 20px;
color: white; color: white;
display: flex;
flex-direction: column;
} }
.left-thing button { .left-thing hr {
width: 100%; width: 100%;
flex: 0 0 auto;
}
.title {
padding: 10px;
font-size: 20px;
font-weight: bold;
flex: 0 0 auto;
}
.panel {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
flex: 0 0 auto;
}
.panel button {
flex: 0 0 32%;
height: 32px;
margin-top: 4px;
margin-bottom: 4px;
font-size: 14px;
margin-left: 3px;
margin-right: 3px;
}
.info {
padding: 10px;
flex: 0 0 auto;
}
.flags {
padding: 10px;
}
.log {
flex: 1 1 auto;
position: relative;
}
.log-content {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: auto;
}
.bold {
font-weight: bold;
}
.clear-log {
font-size: 14px;
color: #eeeeee;
cursor: pointer;
} }
.right-thing { .right-thing {
padding: 20px;
overflow: auto; overflow: auto;
} }
+11 -2
View File
@@ -31,19 +31,28 @@ export function buildData(n = 1000) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Measuring helpers // Measuring helpers
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
export function formatNumber(n) {
return Number(n).toFixed();
}
let startTime; let startTime;
let lastMeasure; let lastMeasure;
export function startMeasure(descr) { export function startMeasure(descr) {
startTime = performance.now(); startTime = performance.now();
lastMeasure = descr; lastMeasure = descr;
} }
export function stopMeasure() { export function stopMeasure(cb) {
let last = lastMeasure; let last = lastMeasure;
if (lastMeasure) { if (lastMeasure) {
window.setTimeout(function() { window.setTimeout(function() {
lastMeasure = null; lastMeasure = null;
const stop = performance.now(); const stop = performance.now();
console.log(`[${last}] took ${Number(stop - startTime).toFixed()}ms`); const delta = stop - startTime;
const msg = `[${last}] took ${formatNumber(delta)}ms`;
console.log(msg);
if (cb) {
cb({msg, delta});
}
}, 0); }, 0);
} }
} }
+104 -12
View File
@@ -1,4 +1,9 @@
import { buildData, startMeasure, stopMeasure } from "../shared/utils.js"; import {
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Counter Widget // Counter Widget
@@ -45,29 +50,36 @@ Vue.component("my-message", {
const App = { const App = {
name: "App", name: "App",
data() { data() {
return { messages: [] }; return { messages: [], multipleFlag: false, clearAfterFlag: false };
},
mounted() {
this.log(`Benchmarking Vue v${Vue.version}`);
}, },
methods: { methods: {
addMessages(n) { addMessages(n) {
startMeasure("add " + n); this.benchmark("add " + n, () => {
const newMessages = buildData(n); const newMessages = buildData(n);
this.messages.push.apply(this.messages, newMessages); this.messages.push.apply(this.messages, newMessages);
stopMeasure(); });
}, },
clear() { clear() {
startMeasure("clear"); this._benchmark("clear", () => {
this.messages = []; this.messages = [];
stopMeasure(); });
},
clearLog() {
this.$refs.log.innerHTML = "";
}, },
updateSomeMessages() { updateSomeMessages() {
startMeasure("update every 10th"); this.benchmark("update every 10th", () => {
const messages = this.messages; const messages = this.messages;
for (let i = 0; i < this.messages.length; i += 10) { for (let i = 0; i < this.messages.length; i += 10) {
messages[i].author += "!!!"; messages[i].author += "!!!";
} }
stopMeasure(); });
}, },
removeMessage(id) { removeMessage(id) {
@@ -75,19 +87,99 @@ const App = {
const index = this.messages.findIndex(m => m.id === id); const index = this.messages.findIndex(m => m.id === id);
this.messages.splice(index, 1); this.messages.splice(index, 1);
stopMeasure(); stopMeasure();
},
log(str, isBold) {
const div = document.createElement("div");
if (isBold) {
div.classList.add("bold");
}
div.textContent = `> ${str}`;
this.$refs.log.appendChild(div);
this.$refs.log.scrollTop = this.$refs.log.scrollHeight;
},
benchmark(message, fn, callback) {
if (this.multipleFlag) {
const N = 20;
let n = N;
let total = 0;
let cb = info => {
let finalize = () => {
n--;
total += info.delta;
if (n === 0) {
const avg = total / N;
this.log(`Average: ${formatNumber(avg)}ms`, true);
if (callback) {
callback();
}
} else {
this._benchmark(message, fn, cb);
}
};
if (this.clearAfterFlag) {
this._benchmark(
"clear",
() => {
this.messages = [];
},
finalize,
false
);
} else {
finalize();
}
};
this._benchmark(message, fn, cb);
} else {
this._benchmark(message, fn, callback);
}
},
_benchmark(message, fn, cb, log = true) {
setTimeout(() => {
startMeasure(message);
fn();
stopMeasure(info => {
if (log) {
this.log(info.msg);
}
if (cb) {
cb(info);
}
});
}, 10);
} }
}, },
template: ` template: `
<div class="main"> <div class="main">
<div class="left-thing"> <div class="left-thing">
<div>Number of msg: {{messages.length}}</div> <div class="title">Actions</div>
<div class="panel">
<button @click="addMessages(100)">Add 100 messages</button> <button @click="addMessages(100)">Add 100 messages</button>
<button @click="addMessages(1000)">Add 1000 messages</button> <button @click="addMessages(1000)">Add 1k messages</button>
<button @click="addMessages(10000)">Add 10000 messages</button> <button @click="addMessages(10000)">Add 10k messages</button>
<button @click="addMessages(50000)">Add 50000 messages</button> <button @click="addMessages(30000)">Add 30k messages</button>
<button @click="updateSomeMessages">Update every 10th message</button> <button @click="updateSomeMessages">Update every 10th message</button>
<button @click="clear">Clear</button> <button @click="clear">Clear</button>
</div> </div>
<div class="flags">
<div>
<input type="checkbox" id="multipleflag" v-model="multipleFlag"/>
<label for="multipleflag">Do it 20x</label>
</div>
<div>
<input type="checkbox" id="clearFlag" v-model="clearAfterFlag"/>
<label for="clearFlag">Clear after</label>
</div>
</div>
<div class="info">Number of messages: {{messages.length}}</div>
<hr/>
<div class="title">Log <span class="clear-log" @click="clearLog">(clear)</span></div>
<div class="log">
<div class="log-content" ref="log"/>
</div>
</div>
<div class="right-thing"> <div class="right-thing">
<div class="content"> <div class="content">
<my-message v-for="msg in messages" v-bind:key="msg.id" v-bind:msg="msg" @removeMessage="removeMessage"></my-message> <my-message v-for="msg in messages" v-bind:key="msg.id" v-bind:msg="msg" @removeMessage="removeMessage"></my-message>
+1 -1
View File
@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>OWL Extras</title> <title>OWL Tools</title>
<link rel="stylesheet" href="main.css"> <link rel="stylesheet" href="main.css">
</head> </head>
<body> <body>
+5 -1
View File
@@ -1,5 +1,9 @@
html,
body { body {
font-family: sans-serif; height: 100%;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Arial,
sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
} }
.title, .title,