Skip to content

Commit 0647dba

Browse files
committed
Replace strings using a slightly faster method.
Improves performance of the demangler considerably, taking one example case from 30s compilation down to 10s (on my laptop). With much help from @apmorton Should help with compiler-explorer#1336, though we still need more work to push stuff onto another thread to really help with that (per Austin's comments).
1 parent c1761ed commit 0647dba

File tree

3 files changed

+54
-17
lines changed

3 files changed

+54
-17
lines changed

Diff for: lib/demangler.js

+8-17
Original file line numberDiff line numberDiff line change
@@ -165,27 +165,18 @@ class Demangler extends AsmRegex {
165165
for (let i = 0; i < lines.length; ++i)
166166
this.addTranslation(this.input[i], lines[i]);
167167

168-
let translations = [];
169-
translations = translations.concat(this.symbolstore.listTranslations());
170-
translations = translations.concat(this.othersymbols.listTranslations());
168+
const translations = [...this.symbolstore.listTranslations(), ...this.othersymbols.listTranslations()];
171169

172-
for (let i = 0; i < this.result.asm.length; ++i) {
173-
let line = this.result.asm[i].text;
174-
for (let j = 0; j < translations.length; ++j) {
175-
const oldValue = translations[j][0];
176-
const newValue = translations[j][1];
177-
178-
line = line.replace(oldValue, newValue);
179-
line = line.replace(oldValue, newValue);
180-
181-
this.demangleLabels(this.result.asm[i].labels,
182-
oldValue, newValue);
170+
for (const asm of this.result.asm) {
171+
let line = asm.text;
172+
for (const [oldValue, newValue] of translations) {
173+
line = utils.replaceAll(line, oldValue, newValue);
174+
this.demangleLabels(asm.labels, oldValue, newValue);
183175
}
184-
this.result.asm[i].text = line;
176+
asm.text = line;
185177
}
186178

187-
this.demangleLabelDefinitions(
188-
this.result.labelDefinitions, translations);
179+
this.demangleLabelDefinitions(this.result.labelDefinitions, translations);
189180

190181
return this.result;
191182
}

Diff for: lib/utils.js

+15
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,18 @@ exports.toProperty = function toProperty(prop) {
205205
if (prop.match(/^-?[0-9]*\.[0-9]+$/)) return parseFloat(prop);
206206
return prop;
207207
};
208+
209+
// This function replaces all the "oldValues" in line with "newValue". It handles overlapping string replacement cases,
210+
// and is careful to return the exact same line object if there's no matches. This turns out to be super important for
211+
// performance.
212+
exports.replaceAll = function replaceAll(line, oldValue, newValue) {
213+
if (oldValue.length === 0) return line;
214+
let startPoint = 0;
215+
for (; ;) {
216+
const index = line.indexOf(oldValue, startPoint);
217+
if (index === -1) break;
218+
line = line.substr(0, index) + newValue + line.substr(index + oldValue.length);
219+
startPoint = index + newValue.length;
220+
}
221+
return line;
222+
};

Diff for: test/utils-tests.js

+31
Original file line numberDiff line numberDiff line change
@@ -319,3 +319,34 @@ describe('squashes horizontal whitespace', () => {
319319
utils.squashHorizontalWhitespace(' abc abc').should.equals(' abc abc');
320320
});
321321
});
322+
323+
describe('replaces all substrings', () => {
324+
it('works with no substitutions', () => {
325+
const string = "This is a line with no replacements";
326+
utils.replaceAll(string, "not present", "won't be substituted").should.equal(string);
327+
});
328+
it('handles odd cases', () => {
329+
utils.replaceAll("", "", "").should.equal("");
330+
utils.replaceAll("Hello", "", "").should.equal("Hello");
331+
});
332+
it('works with single replacement', () => {
333+
utils.replaceAll("This is a line with a mistook in it", "mistook", "mistake")
334+
.should.equal("This is a line with a mistake in it");
335+
utils.replaceAll("This is a line with a mistook", "mistook", "mistake")
336+
.should.equal("This is a line with a mistake");
337+
utils.replaceAll("Mistooks were made", "Mistooks", "Mistakes")
338+
.should.equal("Mistakes were made");
339+
});
340+
341+
it('works with multiple replacements', () => {
342+
utils.replaceAll("A mistook is a mistook", "mistook", "mistake")
343+
.should.equal("A mistake is a mistake");
344+
utils.replaceAll("aaaaaaaaaaaaaaaaaaaaaaaaaaa", "a", "b")
345+
.should.equal("bbbbbbbbbbbbbbbbbbbbbbbbbbb");
346+
});
347+
348+
it('works with overlapping replacements', () => {
349+
utils.replaceAll("aaaaaaaa", "a", "ba")
350+
.should.equal("babababababababa");
351+
});
352+
});

0 commit comments

Comments
 (0)