Skip to content

Instantly share code, notes, and snippets.

@lrhn
Last active September 6, 2019 12:39
Show Gist options
  • Save lrhn/701db852e0a0c001786d82f04c87357c to your computer and use it in GitHub Desktop.
Save lrhn/701db852e0a0c001786d82f04c87357c to your computer and use it in GitHub Desktop.
Dart Trim Leading Whitespace Benchmark
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