Last active
September 6, 2019 12:39
-
-
Save lrhn/701db852e0a0c001786d82f04c87357c to your computer and use it in GitHub Desktop.
Dart Trim Leading Whitespace Benchmark
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
final RegExp _commonLeadingWhitespaceRE = | |
RegExp(r"(?=[^]*^([ \t]+)$(?![^]))(?![^]*^(?!\1))", multiLine: true); | |
String trimLeadingWhitespaceRE(String text) { | |
var commonWhitespace = _commonLeadingWhitespaceRE.matchAsPrefix(text); | |
if (commonWhitespace != null) { | |
return text.replaceAll( | |
RegExp("^${commonWhitespace[1]}", multiLine: true), ""); | |
} | |
return text; | |
} | |
final RegExp _commonLeadingWhitespaceRE2 = | |
RegExp(r"([ \t]+)(?![^]*^(?!\1))", multiLine: true); | |
String trimLeadingWhitespaceRE2(String text) { | |
var commonWhitespace = _commonLeadingWhitespaceRE2.matchAsPrefix(text); | |
if (commonWhitespace != null) { | |
return text.replaceAll( | |
RegExp("^${commonWhitespace[1]}", multiLine: true), ""); | |
} | |
return text; | |
} | |
final RegExp _commonLeadingWhitespaceRE3 = | |
RegExp(r"(?=[^]*^([ \t]+)$(?![^]))(?![^]*^(?!\1))^\1", multiLine: true); | |
String trimLeadingWhitespaceRE3(String text) { | |
return text.replaceAll(_commonLeadingWhitespaceRE3, ""); | |
} | |
String trimLeadingWhitespaceDirect(String text) { | |
// Last line must contain only space and tab. | |
int lastLineStart = text.length; | |
int char = 0; | |
while (lastLineStart > 0 && | |
(char = text.codeUnitAt(lastLineStart - 1)) == 0x20 || | |
char == 0x09) { | |
lastLineStart--; | |
} | |
if (lastLineStart == 0) return ""; | |
StringBuffer buffer = StringBuffer(); | |
if (char != 0x0d && char != 0x0a) return text; | |
int i = 0; | |
do { | |
for (var j = lastLineStart; j < text.length; j++) { | |
if (text.codeUnitAt(i) != text.codeUnitAt(j)) return text; | |
i++; | |
} | |
int lineStart = i; | |
int char = 0; | |
while ((char = text.codeUnitAt(i++)) != 0x0a && char != 0x0d) {} | |
if (char == 0x0d && i < lastLineStart && text.codeUnitAt(i) == 0x0a) { | |
i++; | |
} | |
buffer.write(text.substring(lineStart, i)); | |
} while (i < lastLineStart); | |
return buffer.toString(); | |
} | |
const List<String> samples = <String>[ | |
""" | |
All indented | |
The same way | |
As the last line. | |
""", | |
""" | |
All indented | |
More than | |
The last line. | |
""", | |
""" | |
Long indents and long lines and long text on each line. | |
Long indents and long lines and long text on each line. | |
Long indents and long lines and long text on each line. | |
Long indents and long lines and long text on each line. | |
Long indents and long lines and long text on each line. | |
Long indents and long lines and long text on each line. | |
Long indents and long lines and long text on each line. | |
Long indents and long lines and long text on each line. | |
Long indents and long lines and long text on each line. | |
And many lines. | |
""" | |
]; | |
double bench(String Function(String) trim, int limitMS) { | |
int c = 0; | |
int e = 0; | |
var sw = Stopwatch()..start(); | |
do { | |
for (int i = 0; i < 100; i++) { | |
for (var sample in samples) { | |
var result = trim(sample); | |
if (result.isEmpty) throw "This won't happen"; | |
c++; | |
} | |
} | |
e = sw.elapsedMilliseconds; | |
} while (e < limitMS); | |
return c / e; // Operations per milliseconds. | |
} | |
main(List args) { | |
int count = 1; | |
if (args.isNotEmpty) count = int.tryParse(args[0]) ?? 1; | |
// Sanity check. | |
bool sane = true; | |
for (var sample in samples) { | |
var direct = trimLeadingWhitespaceDirect(sample); | |
var re1 = trimLeadingWhitespaceRE(sample); | |
var re2 = trimLeadingWhitespaceRE2(sample); | |
var re3 = trimLeadingWhitespaceRE3(sample); | |
if (direct != re1 || direct != re2 || direct != re3) { | |
sane = false; | |
print("Sanity check failed, algorithm is not correct"); | |
print("Sample:\n$sample"); | |
print("Direct:\n$direct"); | |
print("RegExp1:\n$re1"); | |
print("RegExp2:\n$re2"); | |
print("RegExp3:\n$re3"); | |
} | |
} | |
if (!sane) return; | |
// Warmup. | |
bench(trimLeadingWhitespaceDirect, 250); | |
bench(trimLeadingWhitespaceRE, 250); | |
bench(trimLeadingWhitespaceRE2, 250); | |
bench(trimLeadingWhitespaceRE3, 250); | |
for (var i = 0; i < count; i++) { | |
print( | |
"RegExp1: ${bench(trimLeadingWhitespaceRE, 2000).toStringAsFixed(3)} /ms"); | |
print( | |
"RegExp2: ${bench(trimLeadingWhitespaceRE2, 2000).toStringAsFixed(3)} /ms"); | |
print( | |
"RegExp3: ${bench(trimLeadingWhitespaceRE3, 2000).toStringAsFixed(3)} /ms"); | |
print( | |
"Direct : ${bench(trimLeadingWhitespaceDirect, 2000).toStringAsFixed(3)} /ms"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment