Skip to content

Commit f9065fc

Browse files
authored
[llvm][mustache] Avoid excessive hash lookups in EscapeStringStream (#160166)
The naive char-by-char lookup performed OK, but we can skip ahead to the next match, avoiding all the extra hash lookups in the key map. Likely there is a faster method than this, but its already a 42% win in the BM_Mustache_StringRendering/Escaped benchmark, and an order of magnitude improvement for BM_Mustache_LargeOutputString. | Benchmark | Before (ns) | After (ns) | Speedup | | :--- | ---: | ---: | ---: | | `StringRendering/Escaped` | 29,440,922 | 16,583,603 | ~44% | | `LargeOutputString` | 15,139,251 | 929,891 | ~94% | | `HugeArrayIteration` | 102,148,245 | 95,943,960 | ~6% | | `PartialsRendering` | 308,330,014 | 303,556,563 | ~1.6% | Unreported benchmarks, like those for parsing, had no significant change.
1 parent 220ad03 commit f9065fc

File tree

1 file changed

+22
-8
lines changed

1 file changed

+22
-8
lines changed

llvm/lib/Support/Mustache.cpp

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -397,26 +397,40 @@ class EscapeStringStream : public raw_ostream {
397397
public:
398398
explicit EscapeStringStream(llvm::raw_ostream &WrappedStream,
399399
EscapeMap &Escape)
400-
: Escape(Escape), WrappedStream(WrappedStream) {
400+
: Escape(Escape), EscapeChars(Escape.keys().begin(), Escape.keys().end()),
401+
WrappedStream(WrappedStream) {
401402
SetUnbuffered();
402403
}
403404

404405
protected:
405406
void write_impl(const char *Ptr, size_t Size) override {
406-
llvm::StringRef Data(Ptr, Size);
407-
for (char C : Data) {
408-
auto It = Escape.find(C);
409-
if (It != Escape.end())
410-
WrappedStream << It->getSecond();
411-
else
412-
WrappedStream << C;
407+
StringRef Data(Ptr, Size);
408+
size_t Start = 0;
409+
while (Start < Size) {
410+
// Find the next character that needs to be escaped.
411+
size_t Next = Data.find_first_of(EscapeChars.str(), Start);
412+
413+
// If no escapable characters are found, write the rest of the string.
414+
if (Next == StringRef::npos) {
415+
WrappedStream << Data.substr(Start);
416+
return;
417+
}
418+
419+
// Write the chunk of text before the escapable character.
420+
if (Next > Start)
421+
WrappedStream << Data.substr(Start, Next - Start);
422+
423+
// Look up and write the escaped version of the character.
424+
WrappedStream << Escape[Data[Next]];
425+
Start = Next + 1;
413426
}
414427
}
415428

416429
uint64_t current_pos() const override { return WrappedStream.tell(); }
417430

418431
private:
419432
EscapeMap &Escape;
433+
SmallString<8> EscapeChars;
420434
llvm::raw_ostream &WrappedStream;
421435
};
422436

0 commit comments

Comments
 (0)