samchon / typia

Super-fast/easy runtime validators and serializers via transformation
https://typia.io/
MIT License
4.59k stars 157 forks source link

Typia Generates Unused Functions in Validation Code Paths #515

Closed sinclairzx81 closed 1 year ago

sinclairzx81 commented 1 year ago

Bug Report

Typia seems to be generating unused functions in validation code paths

Repro

import typia from 'typia'

interface Point3D {
  x: number
  y: number
  z: number
}

interface Simple {
  scale: Point3D,
  position: Point3D,
  rotate: Point3D,
  pivot: Point3D
}

const X = typia.is<Simple>({})

Emit

Note the function $io1 is emitted but never used.

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const typia_1 = __importDefault(require("typia"));
const X = (input => {
    const $io0 = input => "object" === typeof input.scale && null !== input.scale && ("number" === typeof input.scale.x && "number" === typeof input.scale.y && "number" === typeof input.scale.z) && ("object" === typeof input.position && null !== input.position && ("number" === typeof input.position.x && "number" === typeof input.position.y && "number" === typeof input.position.z)) && ("object" === typeof input.rotate && null !== input.rotate && ("number" === typeof input.rotate.x && "number" === typeof input.rotate.y && "number" === typeof input.rotate.z)) && ("object" === typeof input.pivot && null !== input.pivot && ("number" === typeof input.pivot.x && "number" === typeof input.pivot.y && "number" === typeof input.pivot.z));
    const $io1 = input => "number" === typeof input.x && "number" === typeof input.y && "number" === typeof input.z;
     // ^ $io1 function is defined in the validation HOT path, but never used.
    return "object" === typeof input && null !== input && $io0(input);
})({});

Performance

As per https://github.com/samchon/typia/issues/513#issuecomment-1437097704, I've followed up and run a few more performance tests on Typia comparing NaN and non-NaN assertions for JIT (which is how I've noticed this issue). The following is the results for the non-NaN checking case for TypeBox.

image

In theory, AOT should perform much faster than JIT here (and in pretty much all cases). In local testing I've run, I usually see around 10-20% performance degrade for using JIT. So in theory, Typia should be seeing upwards 20,000x (estimate)

Micro Benchmark

You can run the following script to test performance degradation for function definitions in hot code paths.

{ // with unused function
    const S = Date.now()
    for(let i = 0; i < 1_000_000_000; i++) {
        const f = () => {} // unused
        const x = typeof 10 === 'number'
    }
    console.log(Date.now() - S) // 1166ms
}
{ // without unused function
    const S = Date.now()
    for(let i = 0; i < 1_000_000_000; i++) {
        const x = typeof 10 === 'number'
    }
    console.log(Date.now() - S) // 360ms
}

Notes

Comparing the validation routines for JIT / AOT, I don't see a lot of difference in terms of assertion logic. I think if you omit the unused $io1 function you're likely going to see an immediate performance boost. Also, you may get a bit more of a performance by inlining the $io0 function as a single conditional expression (this to avoid the function call / new stack frame).

In either case, it's probably going to be good to omit the unused function (if only to reduce JS output) Anyway, Hope this helps S

PS: Feel free to keep the AllowNaN = true in the TypeBox benchmarks!

samchon commented 1 year ago

You're right. I can't publish https://github.com/samchon/typia/issues/509 because of unused local variables and parameters.

I have to fix it.