Skip to content

Commit

Permalink
tp: C++ implementation of counter_leading_intervals
Browse files Browse the repository at this point in the history
Time for `SELECT COUNT() FROM counter_leading_intervals!(counter)` decreased from 2.3s to 180 ms on a 20 MB trace.

Change-Id: Iba5085ce1089a951b213c3d23d9fabd864daf580
  • Loading branch information
aMayzner committed Sep 11, 2024
1 parent 3a7f8ce commit 821f9c3
Show file tree
Hide file tree
Showing 14 changed files with 408 additions and 50 deletions.
1 change: 1 addition & 0 deletions Android.bp
Original file line number Diff line number Diff line change
Expand Up @@ -13249,6 +13249,7 @@ filegroup {
name: "perfetto_src_trace_processor_perfetto_sql_intrinsics_functions_functions",
srcs: [
"src/trace_processor/perfetto_sql/intrinsics/functions/base64.cc",
"src/trace_processor/perfetto_sql/intrinsics/functions/counter_intervals.cc",
"src/trace_processor/perfetto_sql/intrinsics/functions/create_function.cc",
"src/trace_processor/perfetto_sql/intrinsics/functions/create_view_function.cc",
"src/trace_processor/perfetto_sql/intrinsics/functions/dominator_tree.cc",
Expand Down
3 changes: 3 additions & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -2438,6 +2438,8 @@ perfetto_filegroup(
"src/trace_processor/perfetto_sql/intrinsics/functions/base64.cc",
"src/trace_processor/perfetto_sql/intrinsics/functions/base64.h",
"src/trace_processor/perfetto_sql/intrinsics/functions/clock_functions.h",
"src/trace_processor/perfetto_sql/intrinsics/functions/counter_intervals.cc",
"src/trace_processor/perfetto_sql/intrinsics/functions/counter_intervals.h",
"src/trace_processor/perfetto_sql/intrinsics/functions/create_function.cc",
"src/trace_processor/perfetto_sql/intrinsics/functions/create_function.h",
"src/trace_processor/perfetto_sql/intrinsics/functions/create_view_function.cc",
Expand Down Expand Up @@ -2567,6 +2569,7 @@ perfetto_filegroup(
name = "src_trace_processor_perfetto_sql_intrinsics_types_types",
srcs = [
"src/trace_processor/perfetto_sql/intrinsics/types/array.h",
"src/trace_processor/perfetto_sql/intrinsics/types/counter.h",
"src/trace_processor/perfetto_sql/intrinsics/types/node.h",
"src/trace_processor/perfetto_sql/intrinsics/types/partitioned_intervals.h",
"src/trace_processor/perfetto_sql/intrinsics/types/row_dataframe.h",
Expand Down
4 changes: 4 additions & 0 deletions src/trace_processor/db/column_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ class ColumnStorage<std::optional<T>> final : public ColumnStorageBase {
data_.insert(data_.end(), count, val);
valid_.Resize(valid_.size() + static_cast<uint32_t>(count), true);
}
void Append(const std::vector<T>& vals) {
data_.insert(data_.end(), vals.begin(), vals.end());
valid_.Resize(valid_.size() + static_cast<uint32_t>(vals.size()), true);
}
void Set(uint32_t idx, T val) {
if (mode_ == Mode::kDense) {
valid_.Set(idx);
Expand Down
63 changes: 49 additions & 14 deletions src/trace_processor/db/runtime_table.cc
Original file line number Diff line number Diff line change
Expand Up @@ -274,27 +274,31 @@ base::Status RuntimeTable::Builder::AddText(uint32_t idx, const char* ptr) {
}

base::Status RuntimeTable::Builder::AddIntegers(uint32_t idx,
int64_t res,
int64_t val,
uint32_t count) {
auto* col = storage_[idx].get();
if (auto* leading_nulls_ptr = std::get_if<uint32_t>(col)) {
*col = Fill<NullIntStorage>(*leading_nulls_ptr, std::nullopt);
}
if (auto* doubles = std::get_if<NullDoubleStorage>(col)) {
if (!IsPerfectlyRepresentableAsDouble(res)) {
if (!IsPerfectlyRepresentableAsDouble(val)) {
return base::ErrStatus("Column %s contains %" PRId64
" which cannot be represented as a double",
col_names_[idx].c_str(), res);
col_names_[idx].c_str(), val);
}
doubles->AppendMultiple(static_cast<double>(res), count);
doubles->AppendMultiple(static_cast<double>(val), count);
return base::OkStatus();
}
auto* ints = std::get_if<NullIntStorage>(col);
if (auto* null_ints = std::get_if<NullIntStorage>(col)) {
null_ints->AppendMultiple(val, count);
return base::OkStatus();
}
auto* ints = std::get_if<IntStorage>(col);
if (!ints) {
return base::ErrStatus("Column %s does not have consistent types",
col_names_[idx].c_str());
}
ints->AppendMultiple(res, count);
ints->AppendMultiple(val, count);
return base::OkStatus();
}

Expand Down Expand Up @@ -369,9 +373,28 @@ void RuntimeTable::Builder::AddNonNullIntegersUnchecked(
std::get<IntStorage>(*storage_[idx]).Append(res);
}

void RuntimeTable::Builder::AddNullIntegersUnchecked(
uint32_t idx,
const std::vector<int64_t>& res) {
std::get<NullIntStorage>(*storage_[idx]).Append(res);
}

void RuntimeTable::Builder::AddNonNullDoublesUnchecked(
uint32_t idx,
const std::vector<double>& vals) {
std::get<DoubleStorage>(*storage_[idx]).Append(vals);
}

void RuntimeTable::Builder::AddNullDoublesUnchecked(
uint32_t idx,
const std::vector<double>& vals) {
std::get<NullDoubleStorage>(*storage_[idx]).Append(vals);
}

base::StatusOr<std::unique_ptr<RuntimeTable>> RuntimeTable::Builder::Build(
uint32_t rows) && {
std::vector<RefPtr<column::StorageLayer>> storage_layers(col_names_.size() + 1);
std::vector<RefPtr<column::StorageLayer>> storage_layers(col_names_.size() +
1);
std::vector<RefPtr<column::OverlayLayer>> null_layers(col_names_.size() + 1);

std::vector<ColumnLegacy> legacy_columns;
Expand Down Expand Up @@ -422,12 +445,24 @@ base::StatusOr<std::unique_ptr<RuntimeTable>> RuntimeTable::Builder::Build(
i, col_names_[i].c_str(), std::get_if<IntStorage>(col),
storage_layers, overlay_layers, legacy_columns, legacy_overlays);

} else if (auto* doubles = std::get_if<NullDoubleStorage>(col)) {
// The doubles column.
} else if (auto* doubles = std::get_if<DoubleStorage>(col)) {
// The `doubles` column for tables where column types was provided before.
PERFETTO_CHECK(doubles->size() == rows);
if (doubles->non_null_size() == doubles->size()) {
bool is_sorted =
std::is_sorted(doubles->vector().begin(), doubles->vector().end());
uint32_t flags =
is_sorted ? ColumnLegacy::Flag::kNonNull | ColumnLegacy::Flag::kSorted
: ColumnLegacy::Flag::kNonNull;
legacy_columns.emplace_back(col_names_[i].c_str(), doubles, flags, i, 0);
storage_layers[i].reset(new column::NumericStorage<double>(
&doubles->vector(), ColumnType::kDouble, is_sorted));

} else if (auto* null_doubles = std::get_if<NullDoubleStorage>(col)) {
// The doubles column.
PERFETTO_CHECK(null_doubles->size() == rows);
if (null_doubles->non_null_size() == null_doubles->size()) {
// The column is not nullable.
*col = DoubleStorage::CreateFromAssertNonNull(std::move(*doubles));
*col = DoubleStorage::CreateFromAssertNonNull(std::move(*null_doubles));

auto* non_null_doubles = std::get_if<DoubleStorage>(col);
bool is_sorted = std::is_sorted(non_null_doubles->vector().begin(),
Expand All @@ -441,12 +476,12 @@ base::StatusOr<std::unique_ptr<RuntimeTable>> RuntimeTable::Builder::Build(
&non_null_doubles->vector(), ColumnType::kDouble, is_sorted));
} else {
// The column is nullable.
legacy_columns.emplace_back(col_names_[i].c_str(), doubles,
legacy_columns.emplace_back(col_names_[i].c_str(), null_doubles,
ColumnLegacy::Flag::kNoFlag, i, 0);
storage_layers[i].reset(new column::NumericStorage<double>(
&doubles->non_null_vector(), ColumnType::kDouble, false));
&null_doubles->non_null_vector(), ColumnType::kDouble, false));
null_layers[i].reset(
new column::NullOverlay(&doubles->non_null_bit_vector()));
new column::NullOverlay(&null_doubles->non_null_bit_vector()));
}

} else if (auto* strings = std::get_if<StringStorage>(col)) {
Expand Down
6 changes: 4 additions & 2 deletions src/trace_processor/db/runtime_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ class RuntimeTable : public Table {
void AddNonNullIntegerUnchecked(uint32_t idx, int64_t res) {
std::get<IntStorage>(*storage_[idx]).Append(res);
}
void AddNonNullIntegersUnchecked(uint32_t idx,
const std::vector<int64_t>& res);
void AddNonNullIntegersUnchecked(uint32_t idx, const std::vector<int64_t>&);
void AddNullIntegersUnchecked(uint32_t idx, const std::vector<int64_t>&);
void AddNonNullDoublesUnchecked(uint32_t idx, const std::vector<double>&);
void AddNullDoublesUnchecked(uint32_t idx, const std::vector<double>&);

base::StatusOr<std::unique_ptr<RuntimeTable>> Build(uint32_t rows) &&;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ source_set("functions") {
"base64.cc",
"base64.h",
"clock_functions.h",
"counter_intervals.cc",
"counter_intervals.h",
"create_function.cc",
"create_function.h",
"create_view_function.cc",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "src/trace_processor/perfetto_sql/intrinsics/functions/counter_intervals.h"

#include <algorithm>
#include <cinttypes>
#include <cstdint>
#include <iterator>
#include <memory>
#include <numeric>
#include <string>
#include <string_view>
#include <utility>
#include <variant>
#include <vector>

#include "perfetto/base/compiler.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/status.h"
#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/ext/base/status_or.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/containers/string_pool.h"
#include "src/trace_processor/db/runtime_table.h"
#include "src/trace_processor/perfetto_sql/engine/function_util.h"
#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
#include "src/trace_processor/perfetto_sql/intrinsics/types/counter.h"
#include "src/trace_processor/perfetto_sql/intrinsics/types/partitioned_intervals.h"
#include "src/trace_processor/sqlite/bindings/sqlite_bind.h"
#include "src/trace_processor/sqlite/bindings/sqlite_column.h"
#include "src/trace_processor/sqlite/bindings/sqlite_function.h"
#include "src/trace_processor/sqlite/bindings/sqlite_result.h"
#include "src/trace_processor/sqlite/bindings/sqlite_stmt.h"
#include "src/trace_processor/sqlite/bindings/sqlite_type.h"
#include "src/trace_processor/sqlite/bindings/sqlite_value.h"
#include "src/trace_processor/sqlite/sqlite_utils.h"
#include "src/trace_processor/util/status_macros.h"

namespace perfetto::trace_processor::perfetto_sql {
namespace {

struct CounterIntervals : public SqliteFunction<CounterIntervals> {
static constexpr char kName[] = "__intrinsic_counter_intervals";
static constexpr int kArgCount = 3;

struct UserDataContext {
PerfettoSqlEngine* engine;
StringPool* pool;
};

static void Step(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
PERFETTO_DCHECK(argc == kArgCount);
const char* leading_str = sqlite::value::Text(argv[0]);
if (!leading_str) {
return sqlite::result::Error(
ctx, "interval intersect: column list cannot be null");
}

// TODO(mayzner): Support 'lagging'.
if (base::CaseInsensitiveEqual("lagging", leading_str)) {
return sqlite::result::Error(
ctx, "interval intersect: 'lagging' is not implemented");
}
if (!base::CaseInsensitiveEqual("leading", leading_str)) {
return sqlite::result::Error(ctx,
"interval intersect: second argument has to "
"be either 'leading' or 'lagging");
}

int64_t trace_end = sqlite::value::Int64(argv[1]);

// Get column names of return columns.
std::vector<std::string> ret_col_names{
"id", "ts", "dur", "track_id", "value", "next_value", "delta_value"};
std::vector<RuntimeTable::BuilderColumnType> col_types{
RuntimeTable::kInt, // id
RuntimeTable::kInt, // ts,
RuntimeTable::kInt, // dur
RuntimeTable::kInt, // track_id
RuntimeTable::kDouble, // value
RuntimeTable::kNullDouble, // next_value
RuntimeTable::kNullDouble, // delta_value
};

auto partitioned_counter = sqlite::value::Pointer<PartitionedCounter>(
argv[2], PartitionedCounter::kName);
if (!partitioned_counter) {
SQLITE_ASSIGN_OR_RETURN(
ctx, std::unique_ptr<RuntimeTable> ret_table,
RuntimeTable::Builder(GetUserData(ctx)->pool, ret_col_names)
.Build(0));
return sqlite::result::UniquePointer(ctx, std::move(ret_table), "TABLE");
}

RuntimeTable::Builder builder(GetUserData(ctx)->pool, ret_col_names,
col_types);

uint32_t rows_count = 0;
for (auto track_counter = partitioned_counter->partitions_map.GetIterator();
track_counter; ++track_counter) {
int64_t track_id = track_counter.key();
const auto& cols = track_counter.value();
size_t r_count = cols.id.size();
rows_count += r_count;

// Id
builder.AddNonNullIntegersUnchecked(0, cols.id);
// Ts
builder.AddNonNullIntegersUnchecked(1, cols.ts);

// Dur
std::vector<int64_t> dur(r_count);
for (size_t i = 0; i < r_count - 1; i++) {
dur[i] = cols.ts[i + 1] - cols.ts[i];
}
dur[r_count - 1] = trace_end - cols.ts.back();
builder.AddNonNullIntegersUnchecked(2, dur);

// Track id
builder.AddIntegers(3, track_id, static_cast<uint32_t>(r_count));
// Value
builder.AddNonNullDoublesUnchecked(4, cols.val);

// Next value
std::vector<double> next_vals(cols.val.begin() + 1, cols.val.end());
builder.AddNullDoublesUnchecked(5, next_vals);
builder.AddNull(5);

// Delta value
std::vector<double> deltas(r_count - 1);
for (size_t i = 0; i < r_count - 1; i++) {
deltas[i] = cols.val[i + 1] - cols.val[i];
}
builder.AddNull(6);
builder.AddNullDoublesUnchecked(6, deltas);
}

SQLITE_ASSIGN_OR_RETURN(ctx, std::unique_ptr<RuntimeTable> ret_tab,
std::move(builder).Build(rows_count));

return sqlite::result::UniquePointer(ctx, std::move(ret_tab), "TABLE");
}
};

} // namespace

base::Status RegisterCounterIntervalsFunctions(PerfettoSqlEngine& engine,
StringPool* pool) {
return engine.RegisterSqliteFunction<CounterIntervals>(
std::make_unique<CounterIntervals::UserDataContext>(
CounterIntervals::UserDataContext{&engine, pool}));
}

} // namespace perfetto::trace_processor::perfetto_sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_COUNTER_INTERVALS_H_
#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_COUNTER_INTERVALS_H_

#include "perfetto/base/status.h"
#include "src/trace_processor/containers/string_pool.h"
#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"

namespace perfetto::trace_processor::perfetto_sql {

// Registers all interval intersect related functions with |engine|.
base::Status RegisterCounterIntervalsFunctions(PerfettoSqlEngine& engine,
StringPool* pool);

} // namespace perfetto::trace_processor::perfetto_sql

#endif // SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_COUNTER_INTERVALS_H_
Loading

0 comments on commit 821f9c3

Please sign in to comment.