trace_processor: optimize performance of sched, span and window tables

This makes span joining betweeen the window table and the span table
significantly faster (fast enough for the UI to render at 60fps)

Change-Id: I25f017c01d7c650477e47437342d74ecea27604b
diff --git a/src/trace_processor/sched_slice_table.cc b/src/trace_processor/sched_slice_table.cc
index fad7754..ab01a6d 100644
--- a/src/trace_processor/sched_slice_table.cc
+++ b/src/trace_processor/sched_slice_table.cc
@@ -176,6 +176,13 @@
     const QueryConstraints& query_constraints,
     sqlite3_value** argv)
     : order_by_(query_constraints.order_by()), storage_(storage) {
+  // Remove ordering on timestamp if it is the only ordering as we are already
+  // sorted on TS. This makes span joining significantly faster.
+  if (order_by_.size() == 1 && order_by_[0].iColumn == Column::kTimestamp &&
+      !order_by_[0].desc) {
+    order_by_.clear();
+  }
+
   std::bitset<base::kMaxCpus> cpu_filter;
   cpu_filter.set();
 
diff --git a/src/trace_processor/span_operator_table.cc b/src/trace_processor/span_operator_table.cc
index caa67ec..55ee520 100644
--- a/src/trace_processor/span_operator_table.cc
+++ b/src/trace_processor/span_operator_table.cc
@@ -22,12 +22,15 @@
 #include <set>
 
 #include "perfetto/base/logging.h"
+#include "src/trace_processor/sqlite_utils.h"
 
 namespace perfetto {
 namespace trace_processor {
 
 namespace {
 
+using namespace sqlite_utils;
+
 constexpr uint64_t kU64Max = std::numeric_limits<uint64_t>::max();
 
 std::vector<SpanOperatorTable::ColumnDefinition> GetColumnsForTable(
@@ -156,34 +159,79 @@
   return SQLITE_OK;
 }
 
+std::pair<bool, size_t> SpanOperatorTable::GetTableAndColumnIndex(
+    int joined_column_idx) {
+  PERFETTO_CHECK(joined_column_idx >= kReservedColumns);
+
+  size_t table_1_col =
+      static_cast<size_t>(joined_column_idx - kReservedColumns);
+  if (table_1_col < t1_defn_.cols.size()) {
+    return std::make_pair(true, table_1_col);
+  }
+  size_t table_2_col = table_1_col - t1_defn_.cols.size();
+  PERFETTO_CHECK(table_2_col < t2_defn_.cols.size());
+  return std::make_pair(false, table_2_col);
+}
+
 SpanOperatorTable::Cursor::Cursor(SpanOperatorTable* table, sqlite3* db)
     : db_(db), table_(table) {}
 
 SpanOperatorTable::Cursor::~Cursor() {}
 
-int SpanOperatorTable::Cursor::PrepareRawStmt(const TableDefinition& def,
+int SpanOperatorTable::Cursor::PrepareRawStmt(const QueryConstraints& qc,
+                                              sqlite3_value** argv,
+                                              const TableDefinition& def,
+                                              bool is_t1,
                                               sqlite3_stmt** stmt) {
   // TODO(lalitm): pass through constraints on other tables to those tables.
-  std::string t1_sql;
-  t1_sql += "SELECT ts, dur, " + table_->join_col_;
+  std::string sql;
+  sql += "SELECT ts, dur, " + table_->join_col_;
   for (const auto& col : def.cols) {
-    t1_sql += ", " + col.name;
+    sql += ", " + col.name;
   }
-  t1_sql += " FROM " + def.name + " ORDER BY ts;";
-  int t1_size = static_cast<int>(t1_sql.size());
-  return sqlite3_prepare_v2(db_, t1_sql.c_str(), t1_size, stmt, nullptr);
+  sql += " FROM " + def.name;
+  sql += " WHERE 1";
+
+  for (size_t i = 0; i < qc.constraints().size(); i++) {
+    const auto& constraint = qc.constraints()[i];
+    int c = constraint.iColumn;
+    std::string col_name;
+    if (c == Column::kTimestamp) {
+      col_name = "ts";
+    } else if (c == Column::kDuration) {
+      col_name = "dur";
+    } else if (c == Column::kJoinValue) {
+      col_name = table_->join_col_;
+    } else {
+      auto index_pair = table_->GetTableAndColumnIndex(c);
+      bool is_constraint_in_current_table = index_pair.first == is_t1;
+      if (is_constraint_in_current_table) {
+        col_name = def.cols[index_pair.second].name;
+      }
+    }
+
+    if (!col_name.empty()) {
+      sql += " AND " + col_name + OpToString(constraint.op) +
+             reinterpret_cast<const char*>(sqlite3_value_text(argv[i]));
+    }
+  }
+  sql += " ORDER BY ts;";
+
+  PERFETTO_DLOG("%s", sql.c_str());
+  int t1_size = static_cast<int>(sql.size());
+  return sqlite3_prepare_v2(db_, sql.c_str(), t1_size, stmt, nullptr);
 }
 
-int SpanOperatorTable::Cursor::Filter(const QueryConstraints&,
-                                      sqlite3_value**) {
+int SpanOperatorTable::Cursor::Filter(const QueryConstraints& qc,
+                                      sqlite3_value** argv) {
   sqlite3_stmt* t1_raw = nullptr;
-  int err = PrepareRawStmt(table_->t1_defn_, &t1_raw);
+  int err = PrepareRawStmt(qc, argv, table_->t1_defn_, true, &t1_raw);
   ScopedStmt t1_stmt(t1_raw);
   if (err != SQLITE_OK)
     return err;
 
   sqlite3_stmt* t2_raw = nullptr;
-  err = PrepareRawStmt(table_->t2_defn_, &t2_raw);
+  err = PrepareRawStmt(qc, argv, table_->t2_defn_, false, &t2_raw);
   ScopedStmt t2_stmt(t2_raw);
   if (err != SQLITE_OK)
     return err;
@@ -380,14 +428,9 @@
       sqlite3_result_int64(context, static_cast<sqlite3_int64>(ret.join_val));
       break;
     default: {
-      size_t table_1_col = static_cast<size_t>(N - kReservedColumns);
-      if (table_1_col < table_->t1_defn_.cols.size()) {
-        ReportSqliteResult(context, ret.t1_span.values[table_1_col]);
-      } else {
-        size_t table_2_col = table_1_col - table_->t1_defn_.cols.size();
-        PERFETTO_CHECK(table_2_col < table_->t2_defn_.cols.size());
-        ReportSqliteResult(context, ret.t2_span.values[table_2_col]);
-      }
+      auto index_pair = table_->GetTableAndColumnIndex(N);
+      const auto& row = index_pair.first ? ret.t1_span : ret.t2_span;
+      ReportSqliteResult(context, row.values[index_pair.second]);
     }
   }
   return SQLITE_OK;
diff --git a/src/trace_processor/span_operator_table.h b/src/trace_processor/span_operator_table.h
index 721d01c..24d21ac 100644
--- a/src/trace_processor/span_operator_table.h
+++ b/src/trace_processor/span_operator_table.h
@@ -185,13 +185,24 @@
     int Column(sqlite3_context* context, int N) override;
 
    private:
-    int PrepareRawStmt(const TableDefinition& def, sqlite3_stmt**);
+    int PrepareRawStmt(const QueryConstraints& qc,
+                       sqlite3_value** argv,
+                       const TableDefinition& def,
+                       bool is_t1,
+                       sqlite3_stmt**);
 
     sqlite3* const db_;
     SpanOperatorTable* const table_;
     std::unique_ptr<FilterState> filter_state_;
   };
 
+  // Converts a joined column index into an index on the columns of the child
+  // tables.
+  // Returns a (bool, index) pair with the bool indicating whether the index is
+  // into table 1 and the index being the offset into the relevant table's
+  // columns.
+  std::pair<bool, size_t> GetTableAndColumnIndex(int joined_column_idx);
+
   TableDefinition t1_defn_;
   TableDefinition t2_defn_;
   std::string join_col_;
diff --git a/src/trace_processor/sqlite_utils.h b/src/trace_processor/sqlite_utils.h
index 5a8b8fb..b1e0499 100644
--- a/src/trace_processor/sqlite_utils.h
+++ b/src/trace_processor/sqlite_utils.h
@@ -43,6 +43,25 @@
   return op == SQLITE_INDEX_CONSTRAINT_LT;
 }
 
+inline std::string OpToString(int op) {
+  switch (op) {
+    case SQLITE_INDEX_CONSTRAINT_EQ:
+      return "=";
+    case SQLITE_INDEX_CONSTRAINT_NE:
+      return "!=";
+    case SQLITE_INDEX_CONSTRAINT_GE:
+      return ">=";
+    case SQLITE_INDEX_CONSTRAINT_GT:
+      return ">";
+    case SQLITE_INDEX_CONSTRAINT_LE:
+      return "<=";
+    case SQLITE_INDEX_CONSTRAINT_LT:
+      return "<";
+    default:
+      PERFETTO_FATAL("Operator to string conversion not impemented for %d", op);
+  }
+}
+
 }  // namespace sqlite_utils
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/window_operator_table.cc b/src/trace_processor/window_operator_table.cc
index ae85dc4..3648081 100644
--- a/src/trace_processor/window_operator_table.cc
+++ b/src/trace_processor/window_operator_table.cc
@@ -55,7 +55,14 @@
       new Cursor(this, window_start_, window_end, step_size));
 }
 
-int WindowOperatorTable::BestIndex(const QueryConstraints&, BestIndexInfo*) {
+int WindowOperatorTable::BestIndex(const QueryConstraints& qc,
+                                   BestIndexInfo* info) {
+  // Remove ordering on timestamp if it is the only ordering as we are already
+  // sorted on TS. This makes span joining significantly faster.
+  if (qc.order_by().size() == 1 && qc.order_by()[0].iColumn == Column::kTs &&
+      !qc.order_by()[0].desc) {
+    info->order_by_consumed = true;
+  }
   return SQLITE_OK;
 }