前言
// RUNTIME VALIDATORS
export function is<T>(input: unknown | T): input is T; // returns boolean
export function assert<T>(input: unknown | T): T; // throws TypeGuardError
export function validate<T>(input: unknown | T): IValidation<T>; // detailed
export const customValidators: CustomValidatorMap; // customizable
// ENHANCED JSON
export function application<...Args>(): IJsonApplication; // JSON schema
export function assertParse<T>(input: string): T; // type safe parser
export function assertStringify<T>(input: T): string; // safe and faster
// +) isParse, validateParse
// +) stringify, isStringify, validateStringify
// RANDOM DATA GENERATOR
export function random<T>(g?: Partial<IRandomGenerator>): Primitive<T>;
让我们研究为什么typia
比class-validator
快得多。
- github存储库:https://github.com/samchon/typia
- 指南文件:https://typia.io/docs
我撰写了一些文章,介绍了我的打字稿运行时验证器库typia在此处的dev.to
社区中。在这些先前的文章中,我经常解释说,typia
提高了通过AOT(提前)补充的验证,并且比class-validator
快20,000倍。
顺便说一句,描述了AOT汇编,我只是告诉typia
为Target T
类型生成静态验证代码,而此类静态代码使程序更快。我从来没有解释过这种静态验证代码比class-validator
的动态验证代码快得多的原因。
今天,我将描述原因。
// TYPESCRIPT SOURCE CODE
typia.is<number>(3);
typia.createIs<Date>();
// COMPILED JAVASCRIPT CODE
((input) => "number" === typeof input)(3);
(input) => input instanceof Date;
每个对象都是HashMap
为了帮助您的理解,我将使用许多形象表达式和伪代码。但是,这些形象表达式和伪代码可能与实际的计算机工程理论有所不同。请阅读本文考虑此类方面。
您是否听说过动态语言中的每个对象都是HashMap
?
要知道typia
如何优化性能,您可以理解这个概念。为什么动态语言使用HashMap
,以及HashMap
如何使程序较慢。
// in dynamic language like JavaScript,
const obj = {};
// you can any properties at any time
obj.a = "A";
obj.wafdfaf = 3;
obj[RandomGenerator.string()] = true;
// removing properties are also same
delete obj.a;
delete obj.wafdfaf;
起初,JavaScript是一种动态语言。
因此,可以将任何类型的值分配给变量。在对象情况下,甚至可以随时添加或删除属性。
为了支持动态属性的插入和删除,必须将对象实现为HashMap
类型。
这就是动态语言将HashMap
用于对象的原因,而动态语言中没有任何例外情况。 python,ruby,php等,所有这些都将HashMap
用于对象。
HashMap
类
export class HashMap<Key, T> {
private hasher_: (key: Key) => number;
private equal_: (x: Key, y: Key) => number;
// HashMap stores its key-value pairs into a Linked List
private data_: List<Entry<Key, T>>;
// Instead, have bucket array of linked list nodes, for fast key finding
private buckets_: List.Node<Entry<Key, T>>[][];
private find(key: Key): List.Node<Entry<Key, T>> {
const hash: number = this.hasher_(key);
const index: number = hash % this.buckets_.length;
const bucket: List.Node<Entry<Key, T>>[] = this.buckets_[index] ?? [];
return bucket.find(
(node) => this.equal_(node.value.key, key)
) ?? this.data_.end();
}
// Therefore, single key access could be faster
public has(key: Key): boolean {
this.find(key) !== this.data_.end();
}
public get(key: Key): T | undefined {
const it: List.Node<Entry<Key, T>> = this.find(key);
return it !== this.data_.end()
? it.value.value
: undefined;
}
// However, full iteration is slower as well as linked list
public entries(callback: (value: [Key, T]) => void): void {
let it: List.Node<Entry<Key, T>> = this.data_.begin();
while (it !== this.data_.end()) {
callback([it.value.key, it.value.value]);
it = it.next();
}
}
public size(): number { return this.data_.size(); }
}
export class List<T> {
private begin_: List<T>;
private end_: List<T>;
private size_: number;
public size(): number;
public begin(): List.Node<T>;
public end(): List.Node<T>;
}
export namespace List {
export class Node<T> {
public next: Node<T> | null;
public prev: Node<T> | null;
public value: T;
}
}
- 实际代码:
顺便说一句,您知道如何实现HashMap
吗? HashMap
将其键值对存储到Linked List
(C ++ STL Case中的双连接列表)中。另外,对于快速钥匙发现,它将Linked List
的节点存储到Bucket
数组中。
这就是为什么HashMap
使动态语言较慢的原因。如您所知,静态语言将对象属性包含在固定和测序的内存空间中,例如Array
。比较Linked List
与Array
的迭代速度,哪个更快?当然,Array
要快得多。这是动态语言比静态语言慢的主要原因之一。
- 动态语言将属性存储到
Linked List
- 静态语言将属性存储到
Array
(测序的内存空间)
如果我的解释不容易理解,请阅读上面的伪代码。这不是
HashMap
和Linked List
的实际实现,而是了解该概念的帮助。
隐藏的类优化
现在,我们已经了解到动态语言正在利用HashMap
进行动态密钥组成,但是由于其存储Linked List
,它使程序较慢。顺便说一句,V8引擎具有针对此问题的特殊优化技术。
当V8引擎检测具有固定属性组成的对象类型时,它为对象类型提供了隐藏的类定义。而且,隐藏的类定义不是使用HashMap
,而是将属性安排到固定和测序的内存空间(如静态语言)中。
这称为“隐藏类优化”。如果您听说Google Chrome或nodejs比其他动态或Java(例如Java)快得多,可以假设隐藏的类优化是主要原因之一。
结论
让我们总结一下我们在此处学到的文章中所学的内容:
- 动态语言正在使用
HashMap
来支持动态属性的插入和删除。 -
HashMap
将其键值对存储到Linked List
容器中,并使rpgoram慢慢 - 此外,静态语言将属性存储到像
Array
一样的固定和测序的内存空间中,它使程序更快 - 在V8引擎案例中,它具有一种称为“隐藏类优化”的特殊优化技术,可以从
HashMap
逃脱并将属性存储到固定和测序的内存空间(如静态语言)
typia
比class-validator
快的原因与上面总结的理论内容也没有太大不同。我已经设计了typia
来生成有利于V8引擎的代码,实际上,由typia
生成的代码受益于隐藏的类优化。
此外,class-validator
和class-transform
始终通过动态属性的访问来构成其序列化和验证逻辑,尤其是由Reflect
表示。因此,class-validator
和class-transform
永远无法从隐藏的类优化中受益,并且始终迭代慢速Linked List
节点。
20,000x速度差距也来自这种差异。通常,数组和链接列表的遍历速度大约相差10。因为class-validator
和class-transformer
具有双重到四倍的嵌套到动态密钥访问的迭代,因此10x间隙可为最大值(10 4 sup> = 10,000)x理论上。