Skip to content

Commit

Permalink
Use Set.has instead of Array.indexOf for enum comparison (perf improv…
Browse files Browse the repository at this point in the history
…ement) (#2659)

* perf: use Set.has instead of Array.indexOf for enum comparison

* chore: add long enum to benchmark suite

* feat: cache set at create time for better performance

* Switch to #cache

* CtrlC in bench

---------

Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
  • Loading branch information
jmike and colinhacks committed Apr 16, 2024
1 parent 485abbf commit dae761d
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 4 deletions.
6 changes: 6 additions & 0 deletions deno/lib/benchmarks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,9 @@ if (!argv.length) {
for (const suite of suites) {
suite.run();
}

// exit on Ctrl-C
process.on("SIGINT", function () {
console.log("Exiting...");
process.exit();
});
35 changes: 35 additions & 0 deletions deno/lib/benchmarks/primitives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,40 @@ enumSuite
console.log(`z.enum: ${e.target}`);
});

const longEnumSuite = new Benchmark.Suite("long z.enum");
const longEnumSchema = z.enum([
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"eleven",
"twelve",
"thirteen",
"fourteen",
"fifteen",
"sixteen",
"seventeen",
]);

longEnumSuite
.add("valid", () => {
longEnumSchema.parse("five");
})
.add("invalid", () => {
try {
longEnumSchema.parse("invalid");
} catch (e) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`long z.enum: ${e.target}`);
});

const undefinedSuite = new Benchmark.Suite("z.undefined");
const undefinedSchema = z.undefined();

Expand Down Expand Up @@ -129,6 +163,7 @@ symbolSuite
export default {
suites: [
enumSuite,
longEnumSuite,
undefinedSuite,
literalSuite,
numberSuite,
Expand Down
15 changes: 13 additions & 2 deletions deno/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4128,6 +4128,8 @@ export class ZodEnum<T extends [string, ...string[]]> extends ZodType<
T[number],
ZodEnumDef<T>
> {
#cache: Set<T[number]> | undefined;

_parse(input: ParseInput): ParseReturnType<this["_output"]> {
if (typeof input.data !== "string") {
const ctx = this._getOrReturnCtx(input);
Expand All @@ -4140,7 +4142,11 @@ export class ZodEnum<T extends [string, ...string[]]> extends ZodType<
return INVALID;
}

if (this._def.values.indexOf(input.data) === -1) {
if (!this.#cache) {
this.#cache = new Set(this._def.values);
}

if (!this.#cache.has(input.data)) {
const ctx = this._getOrReturnCtx(input);
const expectedValues = this._def.values;

Expand Down Expand Up @@ -4232,6 +4238,7 @@ export class ZodNativeEnum<T extends EnumLike> extends ZodType<
T[keyof T],
ZodNativeEnumDef<T>
> {
#cache: Set<T[keyof T]> | undefined;
_parse(input: ParseInput): ParseReturnType<T[keyof T]> {
const nativeEnumValues = util.getValidEnumValues(this._def.values);

Expand All @@ -4249,7 +4256,11 @@ export class ZodNativeEnum<T extends EnumLike> extends ZodType<
return INVALID;
}

if (nativeEnumValues.indexOf(input.data) === -1) {
if (!this.#cache) {
this.#cache = new Set(util.getValidEnumValues(this._def.values));
}

if (!this.#cache.has(input.data)) {
const expectedValues = util.objectValues(nativeEnumValues);

addIssueToContext(ctx, {
Expand Down
6 changes: 6 additions & 0 deletions src/benchmarks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,9 @@ if (!argv.length) {
for (const suite of suites) {
suite.run();
}

// exit on Ctrl-C
process.on("SIGINT", function () {
console.log("Exiting...");
process.exit();
});
35 changes: 35 additions & 0 deletions src/benchmarks/primitives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,40 @@ enumSuite
console.log(`z.enum: ${e.target}`);
});

const longEnumSuite = new Benchmark.Suite("long z.enum");
const longEnumSchema = z.enum([
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"eleven",
"twelve",
"thirteen",
"fourteen",
"fifteen",
"sixteen",
"seventeen",
]);

longEnumSuite
.add("valid", () => {
longEnumSchema.parse("five");
})
.add("invalid", () => {
try {
longEnumSchema.parse("invalid");
} catch (e) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`long z.enum: ${e.target}`);
});

const undefinedSuite = new Benchmark.Suite("z.undefined");
const undefinedSchema = z.undefined();

Expand Down Expand Up @@ -129,6 +163,7 @@ symbolSuite
export default {
suites: [
enumSuite,
longEnumSuite,
undefinedSuite,
literalSuite,
numberSuite,
Expand Down
15 changes: 13 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4128,6 +4128,8 @@ export class ZodEnum<T extends [string, ...string[]]> extends ZodType<
T[number],
ZodEnumDef<T>
> {
#cache: Set<T[number]> | undefined;

_parse(input: ParseInput): ParseReturnType<this["_output"]> {
if (typeof input.data !== "string") {
const ctx = this._getOrReturnCtx(input);
Expand All @@ -4140,7 +4142,11 @@ export class ZodEnum<T extends [string, ...string[]]> extends ZodType<
return INVALID;
}

if (this._def.values.indexOf(input.data) === -1) {
if (!this.#cache) {
this.#cache = new Set(this._def.values);
}

if (!this.#cache.has(input.data)) {
const ctx = this._getOrReturnCtx(input);
const expectedValues = this._def.values;

Expand Down Expand Up @@ -4232,6 +4238,7 @@ export class ZodNativeEnum<T extends EnumLike> extends ZodType<
T[keyof T],
ZodNativeEnumDef<T>
> {
#cache: Set<T[keyof T]> | undefined;
_parse(input: ParseInput): ParseReturnType<T[keyof T]> {
const nativeEnumValues = util.getValidEnumValues(this._def.values);

Expand All @@ -4249,7 +4256,11 @@ export class ZodNativeEnum<T extends EnumLike> extends ZodType<
return INVALID;
}

if (nativeEnumValues.indexOf(input.data) === -1) {
if (!this.#cache) {
this.#cache = new Set(util.getValidEnumValues(this._def.values));
}

if (!this.#cache.has(input.data)) {
const expectedValues = util.objectValues(nativeEnumValues);

addIssueToContext(ctx, {
Expand Down

0 comments on commit dae761d

Please sign in to comment.