perf(compiler): speed up i18n digest computations (#34332)

Avoids the usage of array destructuring, as it introduces calls to
a `__values` helper function in ES5 that has a relatively high
performance impact. This shaves off roughly 130ms of CPU time for a
large compilation with big templates that uses i18n.

PR Close #34332
This commit is contained in:
JoostK 2019-12-08 20:37:45 +01:00 committed by Kara Erickson
parent 014a7137f4
commit ff0a91422a
1 changed files with 36 additions and 17 deletions

View File

@ -110,13 +110,13 @@ export function sha1(str: string): string {
const len = utf8.length * 8; const len = utf8.length * 8;
const w = newArray(80); const w = newArray(80);
let [a, b, c, d, e]: number[] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; let a = 0x67452301, b = 0xefcdab89, c = 0x98badcfe, d = 0x10325476, e = 0xc3d2e1f0;
words32[len >> 5] |= 0x80 << (24 - len % 32); words32[len >> 5] |= 0x80 << (24 - len % 32);
words32[((len + 64 >> 9) << 4) + 15] = len; words32[((len + 64 >> 9) << 4) + 15] = len;
for (let i = 0; i < words32.length; i += 16) { for (let i = 0; i < words32.length; i += 16) {
const [h0, h1, h2, h3, h4]: number[] = [a, b, c, d, e]; const h0 = a, h1 = b, h2 = c, h3 = d, h4 = e;
for (let j = 0; j < 80; j++) { for (let j = 0; j < 80; j++) {
if (j < 16) { if (j < 16) {
@ -125,12 +125,21 @@ export function sha1(str: string): string {
w[j] = rol32(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); w[j] = rol32(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
} }
const [f, k] = fk(j, b, c, d); const fkVal = fk(j, b, c, d);
const f = fkVal[0];
const k = fkVal[1];
const temp = [rol32(a, 5), f, e, k, w[j]].reduce(add32); const temp = [rol32(a, 5), f, e, k, w[j]].reduce(add32);
[e, d, c, b, a] = [d, c, rol32(b, 30), a, temp]; e = d;
d = c;
c = rol32(b, 30);
b = a;
a = temp;
} }
a = add32(a, h0);
[a, b, c, d, e] = [add32(a, h0), add32(b, h1), add32(c, h2), add32(d, h3), add32(e, h4)]; b = add32(b, h1);
c = add32(c, h2);
d = add32(d, h3);
e = add32(e, h4);
} }
return byteStringToHexString(words32ToByteString([a, b, c, d, e])); return byteStringToHexString(words32ToByteString([a, b, c, d, e]));
@ -163,7 +172,8 @@ function fk(index: number, b: number, c: number, d: number): [number, number] {
export function fingerprint(str: string): [number, number] { export function fingerprint(str: string): [number, number] {
const utf8 = utf8Encode(str); const utf8 = utf8Encode(str);
let [hi, lo] = [hash32(utf8, 0), hash32(utf8, 102072)]; let hi = hash32(utf8, 0);
let lo = hash32(utf8, 102072);
if (hi == 0 && (lo == 0 || lo == 1)) { if (hi == 0 && (lo == 0 || lo == 1)) {
hi = hi ^ 0x130f9bef; hi = hi ^ 0x130f9bef;
@ -174,18 +184,21 @@ export function fingerprint(str: string): [number, number] {
} }
export function computeMsgId(msg: string, meaning: string = ''): string { export function computeMsgId(msg: string, meaning: string = ''): string {
let [hi, lo] = fingerprint(msg); let msgFingerprint = fingerprint(msg);
if (meaning) { if (meaning) {
const [him, lom] = fingerprint(meaning); const meaningFingerprint = fingerprint(meaning);
[hi, lo] = add64(rol64([hi, lo], 1), [him, lom]); msgFingerprint = add64(rol64(msgFingerprint, 1), meaningFingerprint);
} }
const hi = msgFingerprint[0];
const lo = msgFingerprint[1];
return byteStringToDecString(words32ToByteString([hi & 0x7fffffff, lo])); return byteStringToDecString(words32ToByteString([hi & 0x7fffffff, lo]));
} }
function hash32(str: string, c: number): number { function hash32(str: string, c: number): number {
let [a, b] = [0x9e3779b9, 0x9e3779b9]; let a = 0x9e3779b9, b = 0x9e3779b9;
let i: number; let i: number;
const len = str.length; const len = str.length;
@ -194,7 +207,8 @@ function hash32(str: string, c: number): number {
a = add32(a, wordAt(str, i, Endian.Little)); a = add32(a, wordAt(str, i, Endian.Little));
b = add32(b, wordAt(str, i + 4, Endian.Little)); b = add32(b, wordAt(str, i + 4, Endian.Little));
c = add32(c, wordAt(str, i + 8, Endian.Little)); c = add32(c, wordAt(str, i + 8, Endian.Little));
[a, b, c] = mix([a, b, c]); const res = mix(a, b, c);
a = res[0], b = res[1], c = res[2];
} }
a = add32(a, wordAt(str, i, Endian.Little)); a = add32(a, wordAt(str, i, Endian.Little));
@ -203,11 +217,11 @@ function hash32(str: string, c: number): number {
c = add32(c, len); c = add32(c, len);
c = add32(c, wordAt(str, i + 8, Endian.Little) << 8); c = add32(c, wordAt(str, i + 8, Endian.Little) << 8);
return mix([a, b, c])[2]; return mix(a, b, c)[2];
} }
// clang-format off // clang-format off
function mix([a, b, c]: [number, number, number]): [number, number, number] { function mix(a: number, b: number, c: number): [number, number, number] {
a = sub32(a, b); a = sub32(a, c); a ^= c >>> 13; a = sub32(a, b); a = sub32(a, c); a ^= c >>> 13;
b = sub32(b, c); b = sub32(b, a); b ^= a << 8; b = sub32(b, c); b = sub32(b, a); b ^= a << 8;
c = sub32(c, a); c = sub32(c, b); c ^= b >>> 13; c = sub32(c, a); c = sub32(c, b); c ^= b >>> 13;
@ -238,8 +252,12 @@ function add32to64(a: number, b: number): [number, number] {
return [high >>> 16, (high << 16) | (low & 0xffff)]; return [high >>> 16, (high << 16) | (low & 0xffff)];
} }
function add64([ah, al]: [number, number], [bh, bl]: [number, number]): [number, number] { function add64(a: [number, number], b: [number, number]): [number, number] {
const [carry, l] = add32to64(al, bl); const ah = a[0], al = a[1];
const bh = b[0], bl = b[1];
const result = add32to64(al, bl);
const carry = result[0];
const l = result[1];
const h = add32(add32(ah, bh), carry); const h = add32(add32(ah, bh), carry);
return [h, l]; return [h, l];
} }
@ -256,7 +274,8 @@ function rol32(a: number, count: number): number {
} }
// Rotate a 64b number left `count` position // Rotate a 64b number left `count` position
function rol64([hi, lo]: [number, number], count: number): [number, number] { function rol64(num: [number, number], count: number): [number, number] {
const hi = num[0], lo = num[1];
const h = (hi << count) | (lo >>> (32 - count)); const h = (hi << count) | (lo >>> (32 - count));
const l = (lo << count) | (hi >>> (32 - count)); const l = (lo << count) | (hi >>> (32 - count));
return [h, l]; return [h, l];