Skip to content

Commit 052a23f

Browse files
committed
test(core/protocols): add shape serde perf baselines
1 parent 64e9c61 commit 052a23f

File tree

6 files changed

+281
-33
lines changed

6 files changed

+281
-33
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { CborCodec } from "@smithy/core/cbor";
2+
import { describe, test as it } from "vitest";
3+
4+
import { createNestingWidget, nestingWidget } from "../test-schema.spec";
5+
6+
describe("performance baseline indicator", () => {
7+
const codec = new CborCodec();
8+
const serializer = codec.createSerializer();
9+
const deserializer = codec.createDeserializer();
10+
11+
it("should serialize objects", () => {
12+
const timings: string[] = [];
13+
const objects = [];
14+
15+
// warmup
16+
for (let i = 0; i < 13; ++i) {
17+
const o = createNestingWidget(2 ** i);
18+
objects.push(o);
19+
serializer.write(nestingWidget, o);
20+
serializer.flush();
21+
}
22+
23+
for (let i = 0; i < objects.length; ++i) {
24+
const o = objects[i];
25+
26+
const A = performance.now();
27+
serializer.write(nestingWidget, o);
28+
const serialization = serializer.flush();
29+
const B = performance.now();
30+
31+
timings.push(
32+
`${B - A} (byte length = ${serialization.byteLength}, ${serialization.byteLength / 1024 / (B - A)} kb/ms)`
33+
);
34+
}
35+
36+
/**
37+
* No assertion here.
38+
* In the initial dual-pass implementation,
39+
* par time is 0 to 10ms for up to 233,000 bytes of CBOR. Up to 30 kb/ms. (kuhe's computer)
40+
*/
41+
console.log("CborShapeSerializer performance timings", timings);
42+
});
43+
44+
it("should deserialize bytes", async () => {
45+
const timings: string[] = [];
46+
const strings = [];
47+
48+
// warmup
49+
for (let i = 0; i < 12; ++i) {
50+
const o = createNestingWidget(2 ** i);
51+
serializer.write(nestingWidget, o);
52+
const json = serializer.flush();
53+
strings.push(json);
54+
await deserializer.read(nestingWidget, json);
55+
}
56+
57+
for (const s of strings) {
58+
const A = performance.now();
59+
await deserializer.read(nestingWidget, s);
60+
const B = performance.now();
61+
62+
timings.push(`${B - A} (byte length = ${s.byteLength}, ${s.byteLength / 1024 / (B - A)} kb/ms)`);
63+
}
64+
65+
/**
66+
* No assertion here.
67+
* In the initial dual-pass implementation,
68+
* par time is 0 to 3ms for up to 116000 bytes of CBOR. Up to 45 kb/ms. (kuhe's computer)
69+
*/
70+
console.log("CborShapeDeserializer performance timings", timings);
71+
});
72+
});

packages/core/src/submodules/protocols/json/JsonShapeDeserializer.spec.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { NumericValue } from "@smithy/core/serde";
22
import type { TimestampEpochSecondsSchema } from "@smithy/types";
33
import { describe, expect, test as it } from "vitest";
44

5-
import { widget } from "../test-schema.spec";
5+
import { createNestingWidget, nestingWidget, widget } from "../test-schema.spec";
66
import { JsonShapeDeserializer } from "./JsonShapeDeserializer";
7+
import { JsonShapeSerializer } from "./JsonShapeSerializer";
78

89
describe(JsonShapeDeserializer.name, () => {
910
let contextSourceAvailable = false;
@@ -153,4 +154,45 @@ describe(JsonShapeDeserializer.name, () => {
153154
expect(await deserializer.read(widget, JSON.stringify({ scalar: "-Infinity" }))).toEqual({ scalar: -Infinity });
154155
expect(await deserializer.read(widget, JSON.stringify({ scalar: "NaN" }))).toEqual({ scalar: NaN });
155156
});
157+
158+
describe("performance baseline indicator", () => {
159+
const serializer = new JsonShapeSerializer({
160+
jsonName: true,
161+
timestampFormat: { default: 7 satisfies TimestampEpochSecondsSchema, useTrait: true },
162+
});
163+
serializer.setSerdeContext({
164+
base64Encoder: (input: Uint8Array) => {
165+
return Buffer.from(input).toString("base64");
166+
},
167+
} as any);
168+
169+
it("should deserialize JSON strings", async () => {
170+
const timings: string[] = [];
171+
const strings = [];
172+
173+
// warmup
174+
for (let i = 0; i < 12; ++i) {
175+
const o = createNestingWidget(2 ** i);
176+
serializer.write(nestingWidget, o);
177+
const json = serializer.flush();
178+
strings.push(json);
179+
await deserializer.read(nestingWidget, json);
180+
}
181+
182+
for (const s of strings) {
183+
const A = performance.now();
184+
await deserializer.read(nestingWidget, s);
185+
const B = performance.now();
186+
187+
timings.push(`${B - A} (JSON length = ${s.length}, ${s.length / 1024 / (B - A)} kb/ms)`);
188+
}
189+
190+
/**
191+
* No assertion here.
192+
* In the initial dual-pass implementation,
193+
* par time is 0 to 10ms for up to 161,000 chars of JSON. Up to 20 kb/ms. (kuhe's computer)
194+
*/
195+
console.log("JsonShapeDeserializer performance timings", timings);
196+
});
197+
});
156198
});

packages/core/src/submodules/protocols/json/JsonShapeSerializer.spec.ts

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,21 @@ import { NumericValue } from "@smithy/core/serde";
22
import type { TimestampEpochSecondsSchema } from "@smithy/types";
33
import { describe, expect, test as it } from "vitest";
44

5-
import { widget } from "../test-schema.spec";
5+
import { createNestingWidget, nestingWidget, widget } from "../test-schema.spec";
66
import { JsonShapeSerializer } from "./JsonShapeSerializer";
77

88
describe(JsonShapeSerializer.name, () => {
9-
it("serializes data to JSON", async () => {
10-
const serializer = new JsonShapeSerializer({
11-
jsonName: true,
12-
timestampFormat: { default: 7 satisfies TimestampEpochSecondsSchema, useTrait: true },
13-
});
14-
serializer.setSerdeContext({
15-
base64Encoder: (input: Uint8Array) => {
16-
return Buffer.from(input).toString("base64");
17-
},
18-
} as any);
9+
const serializer = new JsonShapeSerializer({
10+
jsonName: true,
11+
timestampFormat: { default: 7 satisfies TimestampEpochSecondsSchema, useTrait: true },
12+
});
13+
serializer.setSerdeContext({
14+
base64Encoder: (input: Uint8Array) => {
15+
return Buffer.from(input).toString("base64");
16+
},
17+
} as any);
1918

19+
it("serializes data to JSON", async () => {
2020
const data = {
2121
timestamp: new Date(0),
2222
bigint: 10000000000000000000000054321n,
@@ -29,4 +29,39 @@ describe(JsonShapeSerializer.name, () => {
2929
`{"blob":"AAAAAQ==","timestamp":0,"bigint":10000000000000000000000054321,"bigdecimal":0.10000000000000000000000054321}`
3030
);
3131
});
32+
33+
describe("performance baseline indicator", () => {
34+
it("should serialize objects", () => {
35+
const timings: string[] = [];
36+
const objects = [];
37+
38+
// warmup
39+
for (let i = 0; i < 13; ++i) {
40+
const o = createNestingWidget(2 ** i);
41+
objects.push(o);
42+
serializer.write(nestingWidget, o);
43+
serializer.flush();
44+
}
45+
46+
for (let i = 0; i < objects.length; ++i) {
47+
const o = objects[i];
48+
49+
const A = performance.now();
50+
serializer.write(nestingWidget, o);
51+
const serialization = serializer.flush();
52+
const B = performance.now();
53+
54+
timings.push(
55+
`${B - A} (JSON length = ${serialization.length}, ${serialization.length / 1024 / (B - A)} kb/ms)`
56+
);
57+
}
58+
59+
/**
60+
* No assertion here.
61+
* In the initial dual-pass implementation,
62+
* par time is 0 to 30ms for up to 323,000 chars of JSON. Up to 20 kb/ms. (kuhe's computer)
63+
*/
64+
console.log("JsonShapeSerializer performance timings", timings);
65+
});
66+
});
3267
});

packages/core/src/submodules/protocols/test-schema.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import type {
66
StaticListSchema,
77
StaticOperationSchema,
88
StaticStructureSchema,
9+
StringSchema,
10+
TimestampDefaultSchema,
911
TimestampEpochSecondsSchema,
1012
} from "@smithy/types";
1113
import { describe, test as it } from "vitest";
@@ -34,6 +36,28 @@ export const widget = [
3436
],
3537
] satisfies StaticStructureSchema;
3638

39+
export const nestingWidget: StaticStructureSchema = [
40+
3,
41+
"ns",
42+
"Struct",
43+
0,
44+
["string", "date", "blob", "nested"],
45+
[0 satisfies StringSchema, 4 satisfies TimestampDefaultSchema, 21 satisfies BlobSchema, () => nestingWidget],
46+
];
47+
48+
export function createNestingWidget(nesting = 0) {
49+
const object = {
50+
string: "hello, world",
51+
date: new Date(),
52+
blob: new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]),
53+
nested: undefined,
54+
} as any;
55+
if (nesting > 0) {
56+
object.nested = createNestingWidget(nesting - 1);
57+
}
58+
return object;
59+
}
60+
3761
export const deleteObjects: StaticOperationSchema = [
3862
9,
3963
"ns",

packages/core/src/submodules/protocols/xml/XmlShapeDeserializer.spec.ts

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,35 @@ import { NumericValue } from "@smithy/core/serde";
22
import type { TimestampDateTimeSchema } from "@smithy/types";
33
import { describe, expect, test as it } from "vitest";
44

5-
import { widget } from "../test-schema.spec";
5+
import { createNestingWidget, nestingWidget, widget } from "../test-schema.spec";
66
import { XmlShapeDeserializer } from "./XmlShapeDeserializer";
7+
import { XmlShapeSerializer } from "./XmlShapeSerializer";
8+
9+
describe(XmlShapeDeserializer.name, () => {
10+
const deserializer = new XmlShapeDeserializer({
11+
httpBindings: true,
12+
serviceNamespace: "namespace",
13+
timestampFormat: { default: 5 satisfies TimestampDateTimeSchema, useTrait: true },
14+
xmlNamespace: "namespace",
15+
});
16+
const serializer = new XmlShapeSerializer({
17+
xmlNamespace: "namespace",
18+
serviceNamespace: "namespace",
19+
timestampFormat: { default: 5 satisfies TimestampDateTimeSchema, useTrait: true },
20+
});
21+
serializer.setSerdeContext({
22+
base64Encoder: (input: Uint8Array) => {
23+
return Buffer.from(input).toString("base64");
24+
},
25+
} as any);
726

8-
describe("", () => {
927
it("placeholder", async () => {
1028
const xml = `<Struct xmlns="namespace">
1129
<blob>QUFBQQ==</blob>
1230
<timestamp>0</timestamp>
1331
<bigint>10000000000000000000000054321</bigint>
1432
<bigdecimal>0.10000000000000000000000054321</bigdecimal>
1533
</Struct>`;
16-
const deserializer = new XmlShapeDeserializer({
17-
httpBindings: true,
18-
serviceNamespace: "namespace",
19-
timestampFormat: { default: 5 satisfies TimestampDateTimeSchema, useTrait: true },
20-
xmlNamespace: "namespace",
21-
});
22-
2334
const result = await deserializer.read(widget, xml);
2435
expect(result).toEqual({
2536
blob: new Uint8Array([65, 65, 65, 65]),
@@ -28,4 +39,35 @@ describe("", () => {
2839
bigdecimal: new NumericValue("0.10000000000000000000000054321", "bigDecimal"),
2940
});
3041
});
42+
43+
describe("performance baseline indicator", () => {
44+
it("should deserialize XML strings", async () => {
45+
const timings: string[] = [];
46+
const strings = [];
47+
48+
// warmup
49+
for (let i = 0; i < 13; ++i) {
50+
const o = createNestingWidget(2 ** i);
51+
serializer.write(nestingWidget, o);
52+
const json = serializer.flush();
53+
strings.push(json);
54+
await deserializer.read(nestingWidget, json);
55+
}
56+
57+
for (const s of strings) {
58+
const A = performance.now();
59+
await deserializer.read(nestingWidget, s);
60+
const B = performance.now();
61+
62+
timings.push(`${B - A} (XML length = ${s.length}, ${s.length / 1024 / (B - A)} kb/ms)`);
63+
}
64+
65+
/**
66+
* No assertion here.
67+
* In the initial dual-pass implementation,
68+
* par time is 0 to 45ms for up to 442000 chars of XML. Up to 10 kb/ms. (kuhe's computer)
69+
*/
70+
console.log("XmlShapeDeserializer performance timings", timings);
71+
});
72+
});
3173
});

packages/core/src/submodules/protocols/xml/XmlShapeSerializer.spec.ts

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@ import { NumericValue } from "@smithy/core/serde";
22
import type { TimestampDateTimeSchema } from "@smithy/types";
33
import { describe, expect, test as it } from "vitest";
44

5-
import { widget } from "../test-schema.spec";
5+
import { createNestingWidget, nestingWidget, widget } from "../test-schema.spec";
66
import { simpleFormatXml } from "./simpleFormatXml";
77
import { XmlShapeSerializer } from "./XmlShapeSerializer";
88

99
describe(XmlShapeSerializer.name, () => {
10-
it("serializes data to Query", async () => {
11-
const serializer = new XmlShapeSerializer({
12-
xmlNamespace: "namespace",
13-
serviceNamespace: "namespace",
14-
timestampFormat: { default: 5 satisfies TimestampDateTimeSchema, useTrait: true },
15-
});
16-
serializer.setSerdeContext({
17-
base64Encoder: (input: Uint8Array) => {
18-
return Buffer.from(input).toString("base64");
19-
},
20-
} as any);
10+
const serializer = new XmlShapeSerializer({
11+
xmlNamespace: "namespace",
12+
serviceNamespace: "namespace",
13+
timestampFormat: { default: 5 satisfies TimestampDateTimeSchema, useTrait: true },
14+
});
15+
serializer.setSerdeContext({
16+
base64Encoder: (input: Uint8Array) => {
17+
return Buffer.from(input).toString("base64");
18+
},
19+
} as any);
2120

21+
it("serializes data to Query", async () => {
2222
const data = {
2323
timestamp: new Date(0),
2424
bigint: 10000000000000000000000054321n,
@@ -42,4 +42,37 @@ describe(XmlShapeSerializer.name, () => {
4242
</bigdecimal>
4343
</Struct>`);
4444
});
45+
46+
describe("performance baseline indicator", () => {
47+
it("should serialize objects", () => {
48+
const timings: string[] = [];
49+
const objects = [];
50+
51+
// warmup
52+
for (let i = 0; i < 13; ++i) {
53+
const o = createNestingWidget(2 ** i);
54+
objects.push(o);
55+
serializer.write(nestingWidget, o);
56+
serializer.flush();
57+
}
58+
59+
for (let i = 0; i < objects.length; ++i) {
60+
const o = objects[i];
61+
62+
const A = performance.now();
63+
serializer.write(nestingWidget, o);
64+
const serialization = serializer.flush();
65+
const B = performance.now();
66+
67+
timings.push(`${B - A} (XML length = ${serialization.length}, ${serialization.length / 1024 / (B - A)} kb/ms)`);
68+
}
69+
70+
/**
71+
* No assertion here.
72+
* In the initial dual-pass implementation,
73+
* par time is 0 to 30ms for up to 442,000 chars of XML. Up to 25 kb/ms. (kuhe's computer)
74+
*/
75+
console.log("XmlShapeSerializer performance timings", timings);
76+
});
77+
});
4578
});

0 commit comments

Comments
 (0)