Skip to content

Commit 63cf1e3

Browse files
committed
Optimize AbstractStatementParser.statementStartsWith
I found this was taking ~25% of the CPU of pgadapter when running the TPCC benchmark loader, which seems to generate very large DMLs. Previously, it would call split() over the whole string with a limit. Now, it uses Guava's lazy splitter so that it doesn't have to copy the remainder of the string following the second match. For whatever reason, it seems like the previous implementation was doing something much more expensive than just copying the tail. For 100kb long query text, this new implementation is 1600x faster. For short queries it's only a few times faster. Before: Benchmark Mode Cnt Score Error Units StatementParserBenchmark.isQueryTest thrpt 5 1461962.835 ± 340237.573 ops/s StatementParserBenchmark.longQueryTest thrpt 5 2873.150 ± 490.611 ops/s After: Benchmark Mode Cnt Score Error Units StatementParserBenchmark.isQueryTest thrpt 5 4765215.378 ± 132661.232 ops/s StatementParserBenchmark.longQueryTest thrpt 5 4671884.683 ± 486566.506 ops/s
1 parent 27f0f15 commit 63cf1e3

File tree

1 file changed

+16
-10
lines changed

1 file changed

+16
-10
lines changed

google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.google.cloud.spanner.connection.UnitOfWork.CallType;
3232
import com.google.common.annotations.VisibleForTesting;
3333
import com.google.common.base.Preconditions;
34+
import com.google.common.base.Splitter;
3435
import com.google.common.cache.Cache;
3536
import com.google.common.cache.CacheBuilder;
3637
import com.google.common.cache.CacheStats;
@@ -41,6 +42,7 @@
4142
import java.util.Collection;
4243
import java.util.Collections;
4344
import java.util.HashMap;
45+
import java.util.Iterator;
4446
import java.util.Map;
4547
import java.util.Objects;
4648
import java.util.Set;
@@ -614,16 +616,20 @@ public boolean isUpdateStatement(String sql) {
614616

615617
private boolean statementStartsWith(String sql, Iterable<String> checkStatements) {
616618
Preconditions.checkNotNull(sql);
617-
String[] tokens = sql.split("\\s+", 2);
618-
int checkIndex = 0;
619-
if (supportsExplain() && tokens[0].equalsIgnoreCase("EXPLAIN")) {
620-
checkIndex = 1;
621-
}
622-
if (tokens.length > checkIndex) {
623-
for (String check : checkStatements) {
624-
if (tokens[checkIndex].equalsIgnoreCase(check)) {
625-
return true;
626-
}
619+
Iterator<String> tokens = Splitter.onPattern("\\s+").split(sql).iterator();
620+
if (!tokens.hasNext()) {
621+
return false;
622+
}
623+
String token = tokens.next();
624+
if (supportsExplain() && token.equalsIgnoreCase("EXPLAIN")) {
625+
if (!tokens.hasNext()) {
626+
return false;
627+
}
628+
token = tokens.next();
629+
}
630+
for (String check : checkStatements) {
631+
if (token.equalsIgnoreCase(check)) {
632+
return true;
627633
}
628634
}
629635
return false;

0 commit comments

Comments
 (0)