Skip to content
Permalink
Browse files

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
  • Loading branch information
JoostK authored and kara committed Dec 8, 2019
1 parent 940e62b commit adb0663d58de70c191f6f63499bd5457207e306e
Showing with 36 additions and 17 deletions.
  1. +36 −17 packages/compiler/src/i18n/digest.ts
@@ -110,13 +110,13 @@ export function sha1(str: string): string {
const len = utf8.length * 8;

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 + 64 >> 9) << 4) + 15] = len;

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++) {
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);
}

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);
[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, b, c, d, e] = [add32(a, h0), add32(b, h1), add32(c, h2), add32(d, h3), add32(e, h4)];
a = add32(a, h0);
b = add32(b, h1);
c = add32(c, h2);
d = add32(d, h3);
e = add32(e, h4);
}

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] {
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)) {
hi = hi ^ 0x130f9bef;
@@ -174,18 +184,21 @@ export function fingerprint(str: string): [number, number] {
}

export function computeMsgId(msg: string, meaning: string = ''): string {
let [hi, lo] = fingerprint(msg);
let msgFingerprint = fingerprint(msg);

if (meaning) {
const [him, lom] = fingerprint(meaning);
[hi, lo] = add64(rol64([hi, lo], 1), [him, lom]);
const meaningFingerprint = fingerprint(meaning);
msgFingerprint = add64(rol64(msgFingerprint, 1), meaningFingerprint);
}

const hi = msgFingerprint[0];
const lo = msgFingerprint[1];

return byteStringToDecString(words32ToByteString([hi & 0x7fffffff, lo]));
}

function hash32(str: string, c: number): number {
let [a, b] = [0x9e3779b9, 0x9e3779b9];
let a = 0x9e3779b9, b = 0x9e3779b9;
let i: number;

const len = str.length;
@@ -194,7 +207,8 @@ function hash32(str: string, c: number): number {
a = add32(a, wordAt(str, i, Endian.Little));
b = add32(b, wordAt(str, i + 4, 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));
@@ -203,11 +217,11 @@ function hash32(str: string, c: number): number {
c = add32(c, len);
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
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;
b = sub32(b, c); b = sub32(b, a); b ^= a << 8;
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)];
}

function add64([ah, al]: [number, number], [bh, bl]: [number, number]): [number, number] {
const [carry, l] = add32to64(al, bl);
function add64(a: [number, number], b: [number, number]): [number, number] {
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);
return [h, l];
}
@@ -256,7 +274,8 @@ function rol32(a: number, count: number): number {
}

// 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 l = (lo << count) | (hi >>> (32 - count));
return [h, l];

0 comments on commit adb0663

Please sign in to comment.
You can’t perform that action at this time.