Skip to content

Commit 92913c0

Browse files
committed
Don't use structuredClone to copy LR(1) automata
Also add a very basic manual performance test that uses randomly-generated grammars. Replacing structuredClone appears to improve the performance of the LALR(1) automaton calculation a little bit. However, the apparent performance improvement on smaller grammars is misleading since structuredClone should only be called once per analysis. See #55.
1 parent 80a604e commit 92913c0

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

src/grammar/calculations/parsing/lr/lalr1_automaton.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
import { collapseLookaheads, mergeItems } from "./helpers.js";
22
import * as build from "./build/lr0.js";
33

4+
function copyLR1Automaton(automaton) {
5+
return automaton.map(state => {
6+
return {
7+
kernel: state.kernel.map(item => Object.assign({}, item)),
8+
items: state.items.map(item => Object.assign({}, item)),
9+
transitions: Object.assign({}, state.transitions)
10+
};
11+
});
12+
}
13+
414
export default function({ lr1Automaton }) {
515

616
var i, j;
717

818
// Copy the LR(1) automaton.
919

10-
const automaton = structuredClone(lr1Automaton);
20+
const automaton = copyLR1Automaton(lr1Automaton);
1121

1222
// Collapse lookaheads.
1323

test/manual/performance.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import Grammar from "../../src/grammar/index.js";
2+
3+
function measure(operation, timeLimit) {
4+
let callCount = 0;
5+
6+
const startTime = performance.now();
7+
8+
while (performance.now() - startTime < timeLimit) {
9+
operation();
10+
callCount++;
11+
}
12+
13+
const stopTime = performance.now();
14+
const duration = (stopTime - startTime) / 1000;
15+
const speed = callCount / duration;
16+
17+
return `${callCount} in ${duration.toFixed(2)} s, ${speed.toFixed(2)} calls/s`
18+
}
19+
20+
function randomProductions({ productionCount, maximumProductionLength, symbolCount }) {
21+
const productions = [];
22+
23+
for (let i = 0; i < productionCount; i++) {
24+
const productionLength = 1 + Math.floor(Math.random() * maximumProductionLength);
25+
const production = [];
26+
27+
for (let j = 0; j < productionLength; j++) {
28+
production.push(`s${Math.floor(Math.random() * symbolCount)}`);
29+
}
30+
31+
productions.push(production);
32+
}
33+
34+
return productions;
35+
}
36+
37+
const tests = [
38+
{ productionCount: 10, maximumProductionLength: 10, symbolCount: 20 },
39+
{ productionCount: 20, maximumProductionLength: 10, symbolCount: 20 },
40+
{ productionCount: 50, maximumProductionLength: 10, symbolCount: 20 },
41+
{ productionCount: 100, maximumProductionLength: 10, symbolCount: 20 },
42+
{ productionCount: 200, maximumProductionLength: 10, symbolCount: 20 },
43+
{ productionCount: 500, maximumProductionLength: 10, symbolCount: 20 }
44+
];
45+
46+
const timeLimit = 5000;
47+
48+
for (const params of tests) {
49+
const result = measure(() => {
50+
const productions = randomProductions(params);
51+
new Grammar(productions).calculations.classification
52+
}, timeLimit);
53+
console.log(`productionCount=${params.productionCount}: ${result}`);
54+
}

0 commit comments

Comments
 (0)