Merge "trace_processor: don't create a sorted cache table for sorted cols"
diff --git a/src/trace_processor/sqlite/db_sqlite_table.cc b/src/trace_processor/sqlite/db_sqlite_table.cc
index 09c113d..e42f3bc 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.cc
+++ b/src/trace_processor/sqlite/db_sqlite_table.cc
@@ -258,6 +258,45 @@
 DbSqliteTable::Cursor::Cursor(DbSqliteTable* table)
     : SqliteTable::Cursor(table), initial_db_table_(table->table_) {}
 
+void DbSqliteTable::Cursor::TryCacheCreateSortedTable(
+    const QueryConstraints& qc,
+    FilterHistory history) {
+  if (history == FilterHistory::kDifferent) {
+    // Every time we get a new constraint set, reset the state of any caching
+    // structures.
+    sorted_cache_table_ = base::nullopt;
+    repeated_cache_count_ = 0;
+    return;
+  }
+
+  PERFETTO_DCHECK(history == FilterHistory::kSame);
+
+  // Only try and create the cached table on exactly the third time we see this
+  // constraint set.
+  constexpr uint32_t kRepeatedThreshold = 3;
+  if (repeated_cache_count_++ != kRepeatedThreshold)
+    return;
+
+  // If we have more than one constraint, we can't cache the table using
+  // this method.
+  if (qc.constraints().size() != 1)
+    return;
+
+  // If the constraing is not an equality constraint, there's little
+  // benefit to caching
+  const auto& c = qc.constraints().front();
+  if (!sqlite_utils::IsOpEq(c.op))
+    return;
+
+  // If the column is already sorted, we don't need to cache at all.
+  uint32_t col = static_cast<uint32_t>(c.column);
+  if (initial_db_table_->GetColumn(col).IsSorted())
+    return;
+
+  // Create the cached table, sorting on the column which has the constraint.
+  sorted_cache_table_ = initial_db_table_->Sort({Order{col, false}});
+}
+
 int DbSqliteTable::Cursor::Filter(const QueryConstraints& qc,
                                   sqlite3_value** argv,
                                   FilterHistory history) {
@@ -265,23 +304,9 @@
   // before the table's destructor.
   iterator_ = base::nullopt;
 
-  if (history == FilterHistory::kSame && qc.constraints().size() == 1 &&
-      sqlite_utils::IsOpEq(qc.constraints().front().op)) {
-    // If we've seen the same constraint set with a single equality constraint
-    // more than |kRepeatedThreshold| times, we assume we will see it more
-    // in the future and thus cache a table sorted on the column. That way,
-    // future equality constraints can binary search for the value instead of
-    // doing a full table scan.
-    constexpr uint32_t kRepeatedThreshold = 3;
-    if (!sorted_cache_table_ && repeated_cache_count_++ > kRepeatedThreshold) {
-      const auto& c = qc.constraints().front();
-      uint32_t col = static_cast<uint32_t>(c.column);
-      sorted_cache_table_ = initial_db_table_->Sort({Order{col, false}});
-    }
-  } else {
-    sorted_cache_table_ = base::nullopt;
-    repeated_cache_count_ = 0;
-  }
+  // Tries to create a sorted cached table which can be used to speed up
+  // filters below.
+  TryCacheCreateSortedTable(qc, history);
 
   // We reuse this vector to reduce memory allocations on nested subqueries.
   constraints_.resize(qc.constraints().size());
diff --git a/src/trace_processor/sqlite/db_sqlite_table.h b/src/trace_processor/sqlite/db_sqlite_table.h
index 1e39653..4a02c2c 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.h
+++ b/src/trace_processor/sqlite/db_sqlite_table.h
@@ -47,6 +47,10 @@
       kTable,
     };
 
+    // Tries to create a sorted table to cache in |sorted_cache_table_| if the
+    // constraint set matches the requirements.
+    void TryCacheCreateSortedTable(const QueryConstraints&, FilterHistory);
+
     const Table* SourceTable() const {
       // Try and use the sorted cache table (if it exists) to speed up the
       // sorting. Otherwise, just use the original table.