mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
test: add benchmark for select querybuilder (#8955)
Motivation: the query builder (and within it, replacePropertyNames and associated functions) is pretty CPU intensive. For our workload, it's one of the hottest functions in our entire stack. While improved in https://github.com/typeorm/typeorm/pull/4760, There are still outstanding issues relating to perf e.g. https://github.com/typeorm/typeorm/issues/3857 As we all know though, the first step in optimization is to measure systematically ;) https://wiki.c2.com/?ProfileBeforeOptimizing On my machine, this benchmark runs in ~3500ms or about 0.35ms/query. This tells us there's a way to go - on my stack, that's about 1/3 of a typical query's latency!
This commit is contained in:
parent
ea176b27d4
commit
22570f51f3
38
test/benchmark/multiple-joins-querybuilder/entity/Eight.ts
Normal file
38
test/benchmark/multiple-joins-querybuilder/entity/Eight.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Entity } from "../../../../src/decorator/entity/Entity"
|
||||
import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn"
|
||||
import { Column } from "../../../../src/decorator/columns/Column"
|
||||
import { One } from "./One"
|
||||
import { ManyToOne } from "../../../../src"
|
||||
|
||||
@Entity()
|
||||
export class Eight {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@ManyToOne((type) => One)
|
||||
one: One
|
||||
|
||||
@Column({ type: "text" })
|
||||
aaaaa: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
bbbbb: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ccccc: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ddddd: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
eeeee: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
fffff: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ggggg: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
hhhhh: string
|
||||
}
|
||||
38
test/benchmark/multiple-joins-querybuilder/entity/Five.ts
Normal file
38
test/benchmark/multiple-joins-querybuilder/entity/Five.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Entity } from "../../../../src/decorator/entity/Entity"
|
||||
import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn"
|
||||
import { Column } from "../../../../src/decorator/columns/Column"
|
||||
import { One } from "./One"
|
||||
import { ManyToOne } from "../../../../src"
|
||||
|
||||
@Entity()
|
||||
export class Five {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@ManyToOne((type) => One)
|
||||
one: One
|
||||
|
||||
@Column({ type: "text" })
|
||||
aaaaa: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
bbbbb: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ccccc: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ddddd: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
eeeee: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
fffff: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ggggg: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
hhhhh: string
|
||||
}
|
||||
38
test/benchmark/multiple-joins-querybuilder/entity/Four.ts
Normal file
38
test/benchmark/multiple-joins-querybuilder/entity/Four.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Entity } from "../../../../src/decorator/entity/Entity"
|
||||
import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn"
|
||||
import { Column } from "../../../../src/decorator/columns/Column"
|
||||
import { One } from "./One"
|
||||
import { ManyToOne } from "../../../../src"
|
||||
|
||||
@Entity()
|
||||
export class Four {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@ManyToOne((type) => One)
|
||||
one: One
|
||||
|
||||
@Column({ type: "text" })
|
||||
aaaaa: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
bbbbb: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ccccc: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ddddd: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
eeeee: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
fffff: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ggggg: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
hhhhh: string
|
||||
}
|
||||
38
test/benchmark/multiple-joins-querybuilder/entity/Nine.ts
Normal file
38
test/benchmark/multiple-joins-querybuilder/entity/Nine.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Entity } from "../../../../src/decorator/entity/Entity"
|
||||
import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn"
|
||||
import { Column } from "../../../../src/decorator/columns/Column"
|
||||
import { One } from "./One"
|
||||
import { ManyToOne } from "../../../../src"
|
||||
|
||||
@Entity()
|
||||
export class Nine {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@ManyToOne((type) => One)
|
||||
one: One
|
||||
|
||||
@Column({ type: "text" })
|
||||
aaaaa: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
bbbbb: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ccccc: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ddddd: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
eeeee: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
fffff: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ggggg: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
hhhhh: string
|
||||
}
|
||||
73
test/benchmark/multiple-joins-querybuilder/entity/One.ts
Normal file
73
test/benchmark/multiple-joins-querybuilder/entity/One.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import { Entity } from "../../../../src/decorator/entity/Entity"
|
||||
import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn"
|
||||
import { Column } from "../../../../src/decorator/columns/Column"
|
||||
import { OneToOne } from "../../../../src"
|
||||
import { Two } from "./Two"
|
||||
import { Three } from "./Three"
|
||||
import { Four } from "./Four"
|
||||
import { Five } from "./Five"
|
||||
import { Six } from "./Six"
|
||||
import { Seven } from "./Seven"
|
||||
import { Eight } from "./Eight"
|
||||
import { Nine } from "./Nine"
|
||||
import { Ten } from "./Ten"
|
||||
|
||||
@Entity()
|
||||
export class One {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@OneToOne((type) => Two, (two) => two.one)
|
||||
two: Two
|
||||
|
||||
@OneToOne((type) => Three, (three) => three.one)
|
||||
three: Three
|
||||
|
||||
@OneToOne((type) => Four, (four) => four.one)
|
||||
four: Four
|
||||
|
||||
@OneToOne((type) => Five, (five) => five.one)
|
||||
five: Five
|
||||
|
||||
@OneToOne((type) => Six, (six) => six.one)
|
||||
six: Six
|
||||
|
||||
@OneToOne((type) => Seven, (seven) => seven.one)
|
||||
seven: Seven
|
||||
|
||||
@OneToOne((type) => Eight, (eight) => eight.one)
|
||||
eight: Eight
|
||||
|
||||
@OneToOne((type) => Nine, (nine) => nine.one)
|
||||
nine: Nine
|
||||
|
||||
@OneToOne((type) => Ten, (ten) => ten.one)
|
||||
ten: Ten
|
||||
|
||||
@Column({ type: "text" })
|
||||
aaaaa: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
bbbbb: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ccccc: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ddddd: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
eeeee: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
fffff: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ggggg: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
hhhhh: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
iiiii: string
|
||||
}
|
||||
38
test/benchmark/multiple-joins-querybuilder/entity/Seven.ts
Normal file
38
test/benchmark/multiple-joins-querybuilder/entity/Seven.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Entity } from "../../../../src/decorator/entity/Entity"
|
||||
import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn"
|
||||
import { Column } from "../../../../src/decorator/columns/Column"
|
||||
import { One } from "./One"
|
||||
import { ManyToOne } from "../../../../src"
|
||||
|
||||
@Entity()
|
||||
export class Seven {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@ManyToOne((type) => One)
|
||||
one: One
|
||||
|
||||
@Column({ type: "text" })
|
||||
aaaaa: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
bbbbb: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ccccc: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ddddd: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
eeeee: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
fffff: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ggggg: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
hhhhh: string
|
||||
}
|
||||
38
test/benchmark/multiple-joins-querybuilder/entity/Six.ts
Normal file
38
test/benchmark/multiple-joins-querybuilder/entity/Six.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Entity } from "../../../../src/decorator/entity/Entity"
|
||||
import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn"
|
||||
import { Column } from "../../../../src/decorator/columns/Column"
|
||||
import { One } from "./One"
|
||||
import { ManyToOne } from "../../../../src"
|
||||
|
||||
@Entity()
|
||||
export class Six {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@ManyToOne((type) => One)
|
||||
one: One
|
||||
|
||||
@Column({ type: "text" })
|
||||
aaaaa: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
bbbbb: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ccccc: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ddddd: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
eeeee: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
fffff: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ggggg: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
hhhhh: string
|
||||
}
|
||||
38
test/benchmark/multiple-joins-querybuilder/entity/Ten.ts
Normal file
38
test/benchmark/multiple-joins-querybuilder/entity/Ten.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Entity } from "../../../../src/decorator/entity/Entity"
|
||||
import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn"
|
||||
import { Column } from "../../../../src/decorator/columns/Column"
|
||||
import { One } from "./One"
|
||||
import { ManyToOne } from "../../../../src"
|
||||
|
||||
@Entity()
|
||||
export class Ten {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@ManyToOne((type) => One)
|
||||
one: One
|
||||
|
||||
@Column({ type: "text" })
|
||||
aaaaa: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
bbbbb: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ccccc: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ddddd: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
eeeee: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
fffff: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ggggg: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
hhhhh: string
|
||||
}
|
||||
38
test/benchmark/multiple-joins-querybuilder/entity/Three.ts
Normal file
38
test/benchmark/multiple-joins-querybuilder/entity/Three.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Entity } from "../../../../src/decorator/entity/Entity"
|
||||
import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn"
|
||||
import { Column } from "../../../../src/decorator/columns/Column"
|
||||
import { One } from "./One"
|
||||
import { ManyToOne } from "../../../../src"
|
||||
|
||||
@Entity()
|
||||
export class Three {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@ManyToOne((type) => One)
|
||||
one: One
|
||||
|
||||
@Column({ type: "text" })
|
||||
aaaaa: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
bbbbb: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ccccc: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ddddd: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
eeeee: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
fffff: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ggggg: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
hhhhh: string
|
||||
}
|
||||
38
test/benchmark/multiple-joins-querybuilder/entity/Two.ts
Normal file
38
test/benchmark/multiple-joins-querybuilder/entity/Two.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Entity } from "../../../../src/decorator/entity/Entity"
|
||||
import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn"
|
||||
import { Column } from "../../../../src/decorator/columns/Column"
|
||||
import { One } from "./One"
|
||||
import { ManyToOne } from "../../../../src"
|
||||
|
||||
@Entity()
|
||||
export class Two {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@ManyToOne((type) => One)
|
||||
one: One
|
||||
|
||||
@Column({ type: "text" })
|
||||
aaaaa: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
bbbbb: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ccccc: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ddddd: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
eeeee: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
fffff: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
ggggg: string
|
||||
|
||||
@Column({ type: "text" })
|
||||
hhhhh: string
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
import "reflect-metadata"
|
||||
import { DataSource } from "../../../src/data-source/DataSource"
|
||||
import {
|
||||
closeTestingConnections,
|
||||
createTestingConnections,
|
||||
} from "../../utils/test-utils"
|
||||
import { One } from "./entity/One"
|
||||
|
||||
/**
|
||||
* This test attempts to benchmark the raw CPU usage/latency of the query builder's
|
||||
* SQL string generation. We intentionally don't migrate the database or perform
|
||||
* any actual queries.
|
||||
*/
|
||||
describe("benchmark > QueryBuilder > wide join", () => {
|
||||
let connections: DataSource[]
|
||||
before(
|
||||
async () =>
|
||||
(connections = await createTestingConnections({
|
||||
__dirname,
|
||||
enabledDrivers: ["postgres"],
|
||||
})),
|
||||
)
|
||||
after(() => closeTestingConnections(connections))
|
||||
|
||||
it("testing query builder with join to 10 relations with 10 columns each", () => {
|
||||
for (let i = 1; i <= 10_000; i++) {
|
||||
connections.map((connection) =>
|
||||
connection.manager
|
||||
.createQueryBuilder(One, "ones")
|
||||
.setFindOptions({
|
||||
where: { id: 1 },
|
||||
relations: [
|
||||
"two",
|
||||
"three",
|
||||
"four",
|
||||
"five",
|
||||
"six",
|
||||
"seven",
|
||||
"eight",
|
||||
"nine",
|
||||
"ten",
|
||||
],
|
||||
})
|
||||
.getQuery(),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* On a M1 macbook air, 5 runs:
|
||||
* 3501ms
|
||||
* 3574ms
|
||||
* 3575ms
|
||||
* 3563ms
|
||||
* 3567ms
|
||||
*/
|
||||
})
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user