Skip to content

Commit 482c598

Browse files
committed
multithread dictionary parsing
1 parent 718a869 commit 482c598

4 files changed

Lines changed: 65 additions & 15 deletions

File tree

deno.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"DEBUG_CHUNKS"
1414
],
1515
"read": [
16-
"./dictionary.txt"
16+
"./dictionary.txt",
17+
"./src/"
1718
],
1819
"write": [
1920
"./src/dictionary/global_dictionary.ts"
@@ -27,7 +28,7 @@
2728
],
2829
"read": [
2930
"./dictionary.txt",
30-
"./src/dictionary/global_dictionary.ts"
31+
"./src/"
3132
],
3233
"write": [
3334
"./src/dictionary/global_dictionary.ts"

src/dictionary/build.ts

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// this code is Deno only
22

3+
import { unreachable } from "@std/assert/unreachable";
34
import { extractResultError, ResultError } from "../compound.ts";
45
import { PositionedError } from "../parser/parser_lib.ts";
5-
import { parseDictionary } from "./parser.ts";
6+
import { HEADS, parseDictionary } from "./parser.ts";
67
import { Dictionary } from "./type.ts";
78

89
const SOURCE = new URL("../../dictionary.txt", import.meta.url);
@@ -27,28 +28,69 @@ export const dictionary: Dictionary = new Map(Object.entries(json));
2728
`;
2829
await Deno.writeTextFile(DESTINATION, code);
2930
}
31+
function buildOffloaded(src: string): Promise<Dictionary> {
32+
return new Promise((resolve, reject) => {
33+
const worker = new Worker(
34+
new URL("./worker.ts", import.meta.url),
35+
{ type: "module" },
36+
);
37+
worker.postMessage(src);
38+
worker.onmessage = (event) => {
39+
resolve(event.data as Dictionary);
40+
worker.terminate();
41+
};
42+
worker.onerror = (event) => {
43+
reject(event.error);
44+
};
45+
});
46+
}
3047
export async function build(): Promise<boolean> {
3148
// deno-lint-ignore no-console
32-
console.log("Building dictionary...");
49+
console.log(
50+
`Building dictionary with ${navigator.hardwareConcurrency} threads...`,
51+
);
3352
const start = performance.now();
3453
const text = await Deno.readTextFile(SOURCE);
35-
const startDictionary = performance.now();
36-
let dictionary: Dictionary;
54+
const heads = [...text.matchAll(HEADS)].map((match) => match.index);
55+
const regionIndices = [...new Array(navigator.hardwareConcurrency).keys()]
56+
.map((index) => {
57+
const start = index * text.length / navigator.hardwareConcurrency;
58+
for (const head of heads) {
59+
if (start <= head) {
60+
return head;
61+
}
62+
}
63+
});
64+
const regions = regionIndices.map((index, i) =>
65+
text.slice(index, regionIndices[i + 1] ?? text.length)
66+
);
67+
const dictionary: Dictionary = new Map();
3768
try {
38-
dictionary = parseDictionary(text);
39-
} catch (error) {
40-
displayError(text, extractResultError(error));
41-
return false;
69+
const entries = await Promise.all(
70+
regions.map((region) => buildOffloaded(region)),
71+
);
72+
for (const entry of entries) {
73+
for (const [name, definition] of entry) {
74+
if (dictionary.has(name)) {
75+
throw new Error();
76+
}
77+
dictionary.set(name, definition);
78+
}
79+
}
80+
} catch (_) {
81+
try {
82+
parseDictionary(text);
83+
} catch (error) {
84+
displayError(`${SOURCE}`, extractResultError(error));
85+
return false;
86+
}
87+
unreachable();
4288
}
43-
const endDictionary = performance.now();
4489
await buildWithDictionary(dictionary);
4590
const end = performance.now();
4691
const total = Math.floor(end - start);
47-
const parsing = Math.floor(endDictionary - startDictionary);
4892
// deno-lint-ignore no-console
49-
console.log(
50-
`Building dictionary done in ${total}ms (parsing dictionary took ${parsing}ms)`,
51-
);
93+
console.log(`Building dictionary done in ${total}ms`);
5294
return true;
5395
}
5496
function displayError(source: string, errors: ReadonlyArray<ResultError>) {

src/dictionary/parser.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ import {
4040
VerbAccessory,
4141
} from "./type.ts";
4242

43+
export const HEADS = /^\s*(?:[a-z][a-zA-Z]*\s*,\s*)*[a-z][a-zA-Z]*\s*:/mg;
44+
4345
const RESERVED_SYMBOLS = "#()*+/:;<=>@[\\]^`{|}~";
4446

4547
const hashSign = matchString("#", "hash sign");

src/dictionary/worker.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { parseDictionary } from "./parser.ts";
2+
3+
onmessage = (message) => {
4+
postMessage(parseDictionary(message.data as string));
5+
};

0 commit comments

Comments
 (0)