Skip to content

Commit 3e54e0d

Browse files
committed
Improve performance more by computing array size
1 parent 399a28c commit 3e54e0d

6 files changed

Lines changed: 102 additions & 53 deletions

File tree

benchmarks/index.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import sql, { Sql } from "../src/index";
2+
import bytes from "bytes";
3+
4+
const before = process.memoryUsage();
5+
console.log(`before: ${bytes(before.heapUsed)} / ${bytes(before.heapTotal)}`);
6+
const start = process.hrtime();
7+
8+
const queries: Sql[] = [];
9+
10+
for (let i = 0; i < 1_000_000; i++) {
11+
const subQuery = `something ${"goes"} here and ${"there"}`;
12+
const query = sql`this is ${"the"} query: ${i}, ${subQuery}`;
13+
queries.push(query);
14+
15+
// Compute properties for perf testing.
16+
query.text;
17+
query.values;
18+
}
19+
20+
const end = process.hrtime(start);
21+
const after = process.memoryUsage();
22+
console.log(`after: ${bytes(after.heapUsed)} / ${bytes(after.heapTotal)}`);
23+
console.log(`difference: ${bytes(after.heapUsed - before.heapUsed)}`);
24+
console.log(`time: ${end[0]}s ${~~(end[1] / 1000000)}ms`);

package-lock.json

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"dist/"
99
],
1010
"scripts": {
11+
"benchmark": "ts-node benchmarks/index.ts",
1112
"prettier": "prettier --write",
1213
"lint": "tslint \"src/**/*.ts\" --project tsconfig.json",
1314
"format": "npm run prettier -- README.md \"{.,src/**/}*.{js,jsx,json,ts,tsx,css,md,yml,yaml}\"",
@@ -76,14 +77,17 @@
7677
"node": ">=6"
7778
},
7879
"devDependencies": {
80+
"@types/bytes": "^3.1.0",
7981
"@types/jest": "^25.2.3",
8082
"@types/node": "^14.0.5",
83+
"bytes": "^3.1.0",
8184
"husky": "^4.2.5",
8285
"jest": "^26.0.1",
8386
"lint-staged": "^10.2.6",
8487
"prettier": "^2.0.5",
8588
"rimraf": "^3.0.0",
8689
"ts-jest": "^26.0.0",
90+
"ts-node": "^8.10.1",
8791
"tslint": "^6.1.2",
8892
"tslint-config-prettier": "^1.18.0",
8993
"tslint-config-standard": "^9.0.0",

src/index.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ describe("sql template tag", () => {
7272

7373
for (const key in query) keys.push(key);
7474

75-
expect(keys).toEqual(["rawStrings", "rawValues", "values", "text", "sql"]);
75+
expect(keys).toEqual(["values", "strings", "text", "sql"]);
7676
});
7777

7878
describe("join", () => {

src/index.ts

Lines changed: 33 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ export type RawValue = Value | Sql;
77
* A SQL instance can be nested within each other to build SQL strings.
88
*/
99
export class Sql {
10-
private rawStrings: Array<string>;
11-
private rawValues: Array<RawValue>;
10+
values: Value[];
11+
strings: string[];
12+
1213
constructor(
1314
rawStrings: ReadonlyArray<string>,
1415
rawValues: ReadonlyArray<RawValue>
@@ -25,65 +26,47 @@ export class Sql {
2526
);
2627
}
2728

28-
this.rawStrings = [];
29-
this.rawValues = [];
29+
let valuesLength = rawValues.length;
30+
let stringsLength = rawStrings.length;
3031

31-
if (rawValues.length === 0) {
32-
this.rawStrings = rawStrings.slice(0);
33-
return;
32+
for (const child of rawValues) {
33+
if (child instanceof Sql) {
34+
valuesLength += child.values.length - 1;
35+
stringsLength += child.strings.length - 2;
36+
}
3437
}
3538

36-
this.rawStrings.length = rawStrings.length;
37-
38-
if (rawValues.length) {
39-
this.rawValues.length = rawValues.length;
39+
this.values = new Array(valuesLength);
40+
this.strings = new Array(stringsLength);
4041

41-
for (let child of rawValues) {
42-
if (child instanceof Sql) {
43-
this.rawStrings.length += child.strings.length;
44-
this.rawValues.length += child.values.length - 1;
45-
}
46-
}
47-
}
48-
this.rawStrings[0] = rawStrings[0];
42+
this.strings[0] = rawStrings[0];
4943

50-
let i = 1;
51-
let strIn = 1;
52-
for (; i < rawStrings.length; ++i) {
53-
const rawString = rawStrings[i];
54-
const child = rawValues[i - 1];
44+
// Iterate over raw values, strings, and children. The value is always
45+
// positioned between two strings, e.g. `index + 1`.
46+
let index = 1;
47+
let position = 0;
48+
while (index < rawStrings.length) {
49+
const child = rawValues[index - 1];
50+
const rawString = rawStrings[index++];
5551

56-
// check for type
52+
// Check for nested `sql` queries.
5753
if (child instanceof Sql) {
58-
const len = child.values.length;
59-
// concat beginning
60-
this.rawStrings[strIn - 1] += child.strings[0];
61-
62-
for (let d = 0; d < len; ++d) {
63-
this.rawStrings[strIn] = child.strings[d + 1];
64-
this.rawValues[strIn - 1] = child.values[d];
65-
strIn++;
54+
// Append child prefix text to current string.
55+
this.strings[position] += child.strings[0];
56+
57+
let childIndex = 0;
58+
while (childIndex < child.values.length) {
59+
this.values[position++] = child.values[childIndex++];
60+
this.strings[position] = child.strings[childIndex];
6661
}
6762

68-
// set current
69-
this.rawStrings[strIn - 1] += rawString;
63+
// Append raw string to current string.
64+
this.strings[position] += rawString;
7065
} else {
71-
this.rawStrings[strIn] = rawString;
72-
this.rawValues[strIn - 1] = child;
73-
++strIn;
66+
this.values[position++] = child;
67+
this.strings[position] = rawString;
7468
}
7569
}
76-
77-
this.rawStrings.length = strIn;
78-
this.rawValues.length = strIn - 1;
79-
}
80-
81-
get values(): Value[] {
82-
return this.rawValues;
83-
}
84-
85-
get strings(): string[] {
86-
return this.rawStrings;
8770
}
8871

8972
get text() {
@@ -106,9 +89,8 @@ export class Sql {
10689
}
10790

10891
// Work around MySQL enumerable keys in issue #2.
109-
Object.defineProperty(Sql.prototype, "text", { enumerable: true });
110-
Object.defineProperty(Sql.prototype, "values", { enumerable: true });
11192
Object.defineProperty(Sql.prototype, "sql", { enumerable: true });
93+
Object.defineProperty(Sql.prototype, "text", { enumerable: true });
11294

11395
/**
11496
* Create a SQL query for a list of values.

tsconfig.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
"declaration": true,
1010
"sourceMap": true,
1111
"inlineSources": true,
12+
"esModuleInterop": true,
1213
"experimentalDecorators": true
13-
}
14+
},
15+
"include": ["src/**/*"]
1416
}

0 commit comments

Comments
 (0)