Skip to content

Instantly share code, notes, and snippets.

@H4ad
Last active February 2, 2023 23:02
Show Gist options
  • Save H4ad/c43fd3556ab1e241e4db24443003d395 to your computer and use it in GitHub Desktop.
Save H4ad/c43fd3556ab1e241e4db24443003d395 to your computer and use it in GitHub Desktop.
MetadataScanner Benchmark
var Benchmark = require('benchmark');
import {
isConstructor,
isFunction,
} from '@nestjs/common/utils/shared.utils';
import { MetadataScanner } from '@nestjs/core';
import { Injectable } from '@nestjs/common/interfaces/injectable.interface';
var suite = new Benchmark.Suite();
class Test1 {
constructor() {}
bata() {}
bata1() {}
bata2() {}
bata3() {}
bata4() {}
}
class Test2 extends Test1 {
constructor() {
super();
}
bata5() {}
bata6() {}
bata7() {}
bata8() {}
bata9() {}
}
class Test3 extends Test2 {
constructor() {
super();
}
bata10() {}
bata11() {}
bata12() {}
bata13() {}
bata14() {}
}
export class BetterMetadataScanner {
private readonly cachedScannedPrototypes: Map<object, string[]> = new Map();
public scanFromPrototype<T extends Injectable>(
instance: T,
prototype: object | null,
): string[] {
if (!prototype) return [];
if (this.cachedScannedPrototypes.has(prototype)) {
return this.cachedScannedPrototypes.get(prototype);
}
let tempPrototype = prototype;
const visitedNames = new Map<string, boolean>();
const result: string[] = [];
do {
for (const property of Object.getOwnPropertyNames(tempPrototype)) {
if (visitedNames.has(property)) continue;
visitedNames.set(property, true);
// reason: https://github.com/nestjs/nest/pull/10821#issuecomment-1411916533
const descriptor = Object.getOwnPropertyDescriptor(
tempPrototype,
property,
);
if (
descriptor.set ||
descriptor.get ||
isConstructor(property) ||
!isFunction(tempPrototype[property])
) {
continue;
}
result.push(property);
}
} while (
(tempPrototype = Reflect.getPrototypeOf(tempPrototype)) &&
tempPrototype !== Object.prototype
);
this.cachedScannedPrototypes.set(prototype, result);
return result;
}
getAllFilteredMethodNames(prototype: object | null): string[] {
if (!prototype) return [];
const visitedNames = new Map<string, boolean>();
const result: string[] = [];
do {
for (const property of Object.getOwnPropertyNames(prototype)) {
if (visitedNames.has(property)) continue;
visitedNames.set(property, true);
if (isConstructor(property) || !isFunction(prototype[property])) {
continue;
}
result.push(property);
}
} while (
(prototype = Reflect.getPrototypeOf(prototype)) &&
prototype !== Object.prototype
);
return result;
}
}
suite.add('MetadataScanner#scanFromPrototype', async function () {
new MetadataScanner().scanFromPrototype(null, Test3.prototype, (method) => method[0]);
});
suite.add('BetterMetadataScanner#scanFromPrototype', async function () {
new BetterMetadataScanner().scanFromPrototype(null, Test3.prototype)
.map(m => m[0]);
});
suite.add('BetterMetadataScanner#scanFromPrototype without map', async function () {
new BetterMetadataScanner().scanFromPrototype(null, Test3.prototype);
});
suite
// add listeners
.on('cycle', function (event) {
console.log(String(event.target));
})
.on('complete', function () {
console.log('Fastest is ' + this.filter('fastest').map('name'));
})
.run({
async: true,
});
// this file I use to get the profiler information
// to run this file, compile to JavaScript first,
// then you run: node --prof dist/nestjs-profiler.js
// this will generate a file called: isolate-0xnnnnnnnnnnnn-v8.log
// to be able to read the information inside this file, run:
// node --prof-process isolate-0xnnnnnnnnnnnn-v8.log > processed.txt
import { AppModule } from '../src/app.module';
import { NestFactory } from '@nestjs/core';
import * as microtime from 'microtime';
async function main() {
const start = microtime.now();
for (let i = 0; i < 1e4; i++) await NestFactory.create(AppModule, { logger: false });
const end = microtime.now();
console.log(`Diff: ${end - start}`);
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment