Browse Source

Perf: faster `string[]` join

SukkaW 1 year ago
parent
commit
1d8c991baf
4 changed files with 27 additions and 6 deletions
  1. 3 2
      Build/lib/cache-filesystem.ts
  2. 2 1
      Build/lib/create-file.ts
  3. 17 0
      Build/lib/misc.ts
  4. 5 3
      Build/lib/trie.ts

+ 3 - 2
Build/lib/cache-filesystem.ts

@@ -4,6 +4,7 @@ import os from 'os';
 import path from 'path';
 import path from 'path';
 import { mkdirSync } from 'fs';
 import { mkdirSync } from 'fs';
 import picocolors from 'picocolors';
 import picocolors from 'picocolors';
+import { fastStringArrayJoin } from './misc';
 
 
 const identity = (x: any) => x;
 const identity = (x: any) => x;
 
 
@@ -215,7 +216,7 @@ const separator = '\u0000';
 // const textDecoder = new TextDecoder();
 // const textDecoder = new TextDecoder();
 // export const serializeString = (str: string) => textEncoder.encode(str);
 // export const serializeString = (str: string) => textEncoder.encode(str);
 // export const deserializeString = (str: string) => textDecoder.decode(new Uint8Array(str.split(separator).map(Number)));
 // export const deserializeString = (str: string) => textDecoder.decode(new Uint8Array(str.split(separator).map(Number)));
-export const serializeSet = (set: Set<string>) => Array.from(set).join(separator);
+export const serializeSet = (set: Set<string>) => fastStringArrayJoin(Array.from(set), separator);
 export const deserializeSet = (str: string) => new Set(str.split(separator));
 export const deserializeSet = (str: string) => new Set(str.split(separator));
-export const serializeArray = (arr: string[]) => arr.join(separator);
+export const serializeArray = (arr: string[]) => fastStringArrayJoin(arr, separator);
 export const deserializeArray = (str: string) => str.split(separator);
 export const deserializeArray = (str: string) => str.split(separator);

+ 2 - 1
Build/lib/create-file.ts

@@ -5,6 +5,7 @@ import picocolors from 'picocolors';
 import type { Span } from '../trace';
 import type { Span } from '../trace';
 import path from 'path';
 import path from 'path';
 import { sort } from './timsort';
 import { sort } from './timsort';
+import { fastStringArrayJoin } from './misc';
 
 
 export async function compareAndWriteFile(span: Span, linesA: string[], filePath: string) {
 export async function compareAndWriteFile(span: Span, linesA: string[], filePath: string) {
   let isEqual = true;
   let isEqual = true;
@@ -69,7 +70,7 @@ export async function compareAndWriteFile(span: Span, linesA: string[], filePath
 
 
   await span.traceChildAsync(`writing ${filePath}`, async () => {
   await span.traceChildAsync(`writing ${filePath}`, async () => {
     // if (linesALen < 10000) {
     // if (linesALen < 10000) {
-    return Bun.write(file, `${linesA.join('\n')}\n`);
+    return Bun.write(file, fastStringArrayJoin(linesA, '\n') + '\n');
     // }
     // }
     // const writer = file.writer();
     // const writer = file.writer();
 
 

+ 17 - 0
Build/lib/misc.ts

@@ -1 +1,18 @@
 export const isTruthy = <T>(i: T | 0 | '' | false | null | undefined): i is T => !!i;
 export const isTruthy = <T>(i: T | 0 | '' | false | null | undefined): i is T => !!i;
+
+export const fastStringArrayJoin = (arr: string[], sep: string) => {
+  let result = '';
+  for (let i = 0, len = arr.length; i < len; i++) {
+    if (i !== 0) {
+      result += sep;
+    }
+    result += arr[i];
+  }
+  return result;
+};
+
+export const fastStringArrayJoin2 = (arr: string[], sep: string) => {
+  return arr.reduce((acc, cur, index) => {
+    return index === 0 ? cur : acc + sep + cur;
+  }, '');
+};

+ 5 - 3
Build/lib/trie.ts

@@ -2,6 +2,8 @@
  * Suffix Trie based on Mnemonist Trie
  * Suffix Trie based on Mnemonist Trie
  */
  */
 
 
+import { fastStringArrayJoin } from './misc';
+
 // const { Error, Bun, JSON, Symbol } = globalThis;
 // const { Error, Bun, JSON, Symbol } = globalThis;
 
 
 const noop = () => { /** noop */ };
 const noop = () => { /** noop */ };
@@ -262,7 +264,7 @@ export const createTrie = (from?: string[] | Set<string> | null, hostnameMode =
       inputTokens
       inputTokens
     );
     );
 
 
-    return hostnameMode ? matches.map((m) => (m as string[]).join('')) : matches as string[];
+    return hostnameMode ? matches.map((m) => fastStringArrayJoin(m as string[], '')) : matches as string[];
   };
   };
 
 
   /**
   /**
@@ -279,7 +281,7 @@ export const createTrie = (from?: string[] | Set<string> | null, hostnameMode =
     if (res === null) return;
     if (res === null) return;
 
 
     const onMatches = hostnameMode
     const onMatches = hostnameMode
-      ? (suffix: string[]) => set.delete(suffix.join(''))
+      ? (suffix: string[]) => set.delete(fastStringArrayJoin(suffix, ''))
       : (suffix: string) => set.delete(suffix);
       : (suffix: string) => set.delete(suffix);
 
 
     walk(
     walk(
@@ -327,7 +329,7 @@ export const createTrie = (from?: string[] | Set<string> | null, hostnameMode =
 
 
     walk(suffix => {
     walk(suffix => {
       results.push(
       results.push(
-        isHostnameMode(suffix) ? suffix.join('') : suffix
+        isHostnameMode(suffix) ? fastStringArrayJoin(suffix, '') : suffix
       );
       );
     });
     });