ajv-compiler icon indicating copy to clipboard operation
ajv-compiler copied to clipboard

Avoid JSON.stringify calls

Open Eomm opened this issue 4 years ago • 3 comments

Yes, it is so right now.

I would open an issue for it and leave it as-is right now

The solutions I see:

  • track the ajv options to write a fast-json-stringify function that serialize them instead of JSON.stringify
  • apply a new logic like:
    • check the number of keys
    • check iteratively the values (I see only primitive types as values)

Originally posted by @Eomm in https://github.com/fastify/ajv-compiler/pull/1#discussion_r588874508

Eomm avatar Apr 19 '21 21:04 Eomm

Benchmark Script

const { Benchmark } = require("benchmark");
const FJS = require("fast-json-stringify");

const stringify = FJS({
  type: "object",
  properties: {
    coerceTypes: {
      type: "boolean",
    },
    useDefaults: {
      type: "boolean",
    },
    removeAdditional: {
      type: "boolean",
    },
    allErrors: {
      type: "boolean",
    },
    nullable: {
      type: "boolean",
    },
  },
});

const ajvConfig = {
  coerceTypes: true,
  useDefaults: true,
  removeAdditional: true,
  allErrors: false,
  nullable: true,
};

const externalSchema = {};

const suite = new Benchmark.Suite();
suite.add("JSON.stringify", function () {
  const a = JSON.stringify(ajvConfig);
  const b = JSON.stringify(externalSchema);
  const key = `${a}${b}`;
});
suite.add("FJS", function () {
  const a = stringify(ajvConfig);
  const b = JSON.stringify(externalSchema);
  const key = `${a}${b}`;
});

suite.on("cycle", function (event) {
  console.log(String(event.target));
});
suite.on("complete", function () {
  console.log("Fastest is " + this.filter("fastest").map("name"));
});
suite.run();

Result

JSON.stringify x 1,130,049 ops/sec ±3.60% (79 runs sampled)
FJS x 6,217,771 ops/sec ±2.15% (82 runs sampled)
Fastest is FJS

Environment Node: v14.16.0 CPU: [email protected]

climba03003 avatar Apr 22 '21 04:04 climba03003

The ajv options are quite a lot: https://ajv.js.org/options.html

We should keep track of all new addition if we write a schema of those settings

Eomm avatar Apr 22 '21 20:04 Eomm

The type below is created by referencing the type from https://github.com/ajv-validator/ajv/blob/master/lib/core.ts

Note: RemovedOptions is not added in below which contain nullable.

If we go for the schema approach, should we include all the options including RemovedOptions ?

Schema for AJV options with primitive value only

{
  type: "object",
  properties: {
    // strict mode options (NEW)
    strict: {
      $ref: "#/definitions/strictOption",
    },
    strictSchema: {
      $ref: "#/definitions/strictOption",
    },
    strictNumbers: {
      $ref: "#/definitions/strictOption",
    },
    strictTypes: {
      $ref: "#/definitions/strictOption",
    },
    strictTuples: {
      $ref: "#/definitions/strictOption",
    },
    strictRequired: {
      $ref: "#/definitions/strictOption",
    },
    allowMatchingProperties: {
      type: "boolean",
    },
    allowUnionTypes: {
      type: "boolean",
    },
    validateFormats: {
      type: "boolean",
    },
    // validation and reporting options:
    $data: {
      type: "boolean",
    },
    allErrors: {
      type: "boolean",
    },
    verbose: {
      type: "boolean",
    },
    discriminator: {
      type: "boolean",
    },
    unicodeRegExp: {
      type: "boolean",
    },
    // options to modify validated data:
    removeAdditional: {
      oneOf: [
        { type: "boolean" },
        { type: "string", enum: ["all", "failing"] },
      ],
    },
    useDefaults: {
      oneOf: [{ type: "boolean" }, { type: "string", enum: ["empty"] }],
    },
    useDefaults: {
      oneOf: [{ type: "boolean" }, { type: "string", enum: ["array"] }],
    },
    // advanced options:
    next: {
      type: "boolean",
    },
    unevaluated: {
      type: "boolean",
    },
    dynamicRef: {
      type: "boolean",
    },
    jtd: {
      type: "boolean",
    },
    validateSchema: {
      $ref: "#/definitions/strictOption",
    },
    addUsedSchema: {
      type: "boolean",
    },
    inlineRefs: {
      oneOf: [{ type: "boolean" }, { type: "number" }],
    },
    passContext: {
      type: "boolean",
    },
    loopRequired: {
      type: "number",
    },
    loopEnum: {
      type: "number",
    },
    ownProperties: {
      type: "boolean",
    },
    multipleOfPrecision: {
      type: "number",
    },
    messages: {
      type: "boolean",
    },
    code: {
      $ref: "#/definitions/codeOption",
    },
  },
  definitions: {
    strictOption: {
      oneOf: [{ type: "boolean" }, { type: "string", enum: ["log"] }],
    },
    codeOption: {
      type: "object",
      properties: {
        es5: {
          type: "boolean",
        },
        lines: {
          type: "boolean",
        },
        optimize: {
          oneOf: [{ type: "boolean" }, { type: "number" }],
        },
        source: {
          type: "boolean",
        },
      },
    },
  },
}

climba03003 avatar Apr 23 '21 05:04 climba03003