@testn we did have some initial discussion when the original change was made in #1402 but wanted to review and benchmark again. I think the fix should be simple, if the performance numbers are visible enough worth adding.
A bit of context: previously, generated parser class was used to create an instance of row object. V8 caches access to fields, so in theory doing
function row() {
this.field1 = 1;
this.field2 = 2;
// etc
}
new row() // repeat many times
is much faster then
function row() {
const result = {};
result.field1 = 1;
result.field2 = 2;
// etc
}
We could potentially generate some proxy RowConstructor class that assigns all fields from passed from the constructor.
For example, currently generated code for connection.query("show tables) is
(function () {
return class TextRow {
constructor(fields) {
}
next(packet, fields, options) {
this.packet = packet;
const result = {};
// "Tables_in_mysql": VAR_STRING
result["Tables_in_mysql"] = packet.readLengthCodedString("utf8");
return result;
}
};
})()
we could make it something like that?
(function () {
return class TextRow {
constructor(fields) {
this.RowConstructor = function(field1) {
// "Tables_in_mysql": VAR_STRING
this["Tables_in_mysql"] = field1;
}
}
next(packet, fields, options) {
this.packet = packet;
return new this.RowConstructor(
packet.readLengthCodedString("utf8")
);
}
};
})()
Simple benchmark shows 50-60x time difference in row construction, not sure how big the penalty is in the total mix but might be still visible ( constructing an object with 7 fields 10m times and then accessing all fields )
const parse1 = function (base) {
const result = {};
result.id = 1;
result.int_0 = base + 0;
result.int_1 = base + 1;
result.int_2 = base + 2;
result.int_3 = base + 3;
result.int_4 = base + 4;
result.int_5 = base + 5;
result.int_6 = base + 6;
return result;
};
const parse2 = function (base) {
this.id = 1;
this.int_0 = base + 0;
this.int_1 = base + 1;
this.int_2 = base + 2;
this.int_3 = base + 3;
this.int_4 = base + 4;
this.int_5 = base + 5;
this.int_6 = base + 6;
return this;
};
// benchmark 2 versions: 1) parse1, 2) parse2
const NUM_ITEMS = 10000000;
const benchmark = function () {
let results = new Array(NUM_ITEMS);
const start = Date.now();
let sum = 0;
for (let i = 0; i < NUM_ITEMS; i++) {
let result = parse1(i % 1000);
results[i] = result;
sum +=
result.id +
result.int_0 +
result.int_1 +
result.int_2 +
result.int_3 +
result.int_4 +
result.int_5 +
result.int_6;
}
const end = Date.now();
console.log(`parse1: ${end - start} ms; sum: ${sum}`);
results = new Array(NUM_ITEMS);
const start2 = Date.now();
sum = 0;
for (let i = 0; i < NUM_ITEMS; i++) {
let result = parse2(i % 1000);
results[i] = result;
sum +=
result.id +
result.int_0 +
result.int_1 +
result.int_2 +
result.int_3 +
result.int_4 +
result.int_5 +
result.int_6;
}
const end2 = Date.now();
console.log(`parse2: ${end2 - start2} ms, sum: ${sum}`);
};
for (var i = 0; i < 10; i++) {
benchmark();
}
parse1 runs approx 1600ms while parse2 approx 30ms
@testn we did have some initial discussion when the original change was made in #1402 but wanted to review and benchmark again. I think the fix should be simple, if the performance numbers are visible enough worth adding.
A bit of context: previously, generated parser class was used to create an instance of row object. V8 caches access to fields, so in theory doing
is much faster then
We could potentially generate some proxy
RowConstructorclass that assigns all fields from passed from the constructor.For example, currently generated code for
connection.query("show tables)iswe could make it something like that?
Simple benchmark shows 50-60x time difference in row construction, not sure how big the penalty is in the total mix but might be still visible ( constructing an object with 7 fields 10m times and then accessing all fields )
parse1 runs approx 1600ms while parse2 approx 30ms