mirror of
https://github.com/scylladb/scylladb.git
synced 2026-04-20 00:20:47 +00:00
To prevent large memory allocations.
This series shows over 3% improvement in perf-simple-query throughput.
```
$ build/release/scylla perf-simple-query --default-log-level=error --smp=1 --random-seed=1855519715
random-seed=1855519715
enable-cache=1
Running test with config: {partitions=10000, concurrency=100, mode=read, query_single_key=no, counters=no}
Disabling auto compaction
Creating 10000 partitions...
Before:
random-seed=1775976514
enable-cache=1
enable-index-cache=1
sstable-summary-ratio=0.0005
sstable-format=me
Running test with config: {partitions=10000, concurrency=100, mode=read, query_single_key=no, counters=no}
Disabling auto compaction
Creating 10000 partitions...
336345.11 tps ( 58.1 allocs/op, 0.0 logallocs/op, 14.1 tasks/op, 32788 insns/op, 12430 cycles/op, 0 errors)
348748.14 tps ( 58.1 allocs/op, 0.0 logallocs/op, 14.1 tasks/op, 32794 insns/op, 12335 cycles/op, 0 errors)
349012.63 tps ( 58.1 allocs/op, 0.0 logallocs/op, 14.1 tasks/op, 32800 insns/op, 12326 cycles/op, 0 errors)
350629.97 tps ( 58.1 allocs/op, 0.0 logallocs/op, 14.1 tasks/op, 32770 insns/op, 12270 cycles/op, 0 errors)
348585.00 tps ( 58.1 allocs/op, 0.0 logallocs/op, 14.1 tasks/op, 32804 insns/op, 12338 cycles/op, 0 errors)
throughput:
mean= 346664.17 standard-deviation=5825.77
median= 348748.14 median-absolute-deviation=2348.46
maximum=350629.97 minimum=336345.11
instructions_per_op:
mean= 32791.35 standard-deviation=13.60
median= 32794.47 median-absolute-deviation=8.65
maximum=32804.45 minimum=32769.57
cpu_cycles_per_op:
mean= 12340.05 standard-deviation=57.57
median= 12335.05 median-absolute-deviation=13.94
maximum=12430.42 minimum=12270.28
After:
random-seed=1775976514
enable-cache=1
enable-index-cache=1
sstable-summary-ratio=0.0005
sstable-format=me
Running test with config: {partitions=10000, concurrency=100, mode=read, query_single_key=no, counters=no}
Disabling auto compaction
Creating 10000 partitions...
353770.85 tps ( 58.1 allocs/op, 0.0 logallocs/op, 14.1 tasks/op, 32762 insns/op, 11893 cycles/op, 0 errors)
364447.98 tps ( 58.1 allocs/op, 0.0 logallocs/op, 14.1 tasks/op, 32738 insns/op, 11818 cycles/op, 0 errors)
365268.97 tps ( 58.1 allocs/op, 0.0 logallocs/op, 14.1 tasks/op, 32734 insns/op, 11788 cycles/op, 0 errors)
344304.87 tps ( 58.1 allocs/op, 0.0 logallocs/op, 14.1 tasks/op, 32746 insns/op, 12506 cycles/op, 0 errors)
362263.57 tps ( 58.1 allocs/op, 0.0 logallocs/op, 14.1 tasks/op, 32756 insns/op, 11888 cycles/op, 0 errors)
throughput:
mean= 358011.25 standard-deviation=8916.76
median= 362263.57 median-absolute-deviation=6436.74
maximum=365268.97 minimum=344304.87
instructions_per_op:
mean= 32747.06 standard-deviation=11.85
median= 32745.80 median-absolute-deviation=9.36
maximum=32762.18 minimum=32734.01
cpu_cycles_per_op:
mean= 11978.65 standard-deviation=298.06
median= 11887.96 median-absolute-deviation=160.96
maximum=12505.72 minimum=11788.49
```
Refs #28511
(Refs rather than Fixes for the lack of a reproducer unit test)
* No backport needed as the issue is rare and not severe
Closes scylladb/scylladb#28631
* github.com:scylladb/scylladb:
query: result_set: change row member to a chunked vector
query: result_set_row: make noexcept
query: non_null_data_value: assert is_nothrow_move_constructible and assignable
types: data_value: assert is_nothrow_move_constructible and assignable
224 lines
8.1 KiB
C++
224 lines
8.1 KiB
C++
/*
|
|
* Copyright (C) 2015-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.1
|
|
*/
|
|
|
|
#include "query-result-set.hh"
|
|
#include "query-result-reader.hh"
|
|
#include "partition_slice_builder.hh"
|
|
#include "mutation/mutation.hh"
|
|
#include "types/map.hh"
|
|
#include "mutation_query.hh"
|
|
|
|
#include <fmt/format.h>
|
|
|
|
namespace query {
|
|
|
|
static_assert(std::is_nothrow_move_constructible_v<non_null_data_value>);
|
|
static_assert(std::is_nothrow_move_assignable_v<non_null_data_value>);
|
|
static_assert(std::is_nothrow_move_constructible_v<result_set_row>);
|
|
static_assert(std::is_nothrow_move_assignable_v<result_set_row>);
|
|
static_assert(std::is_nothrow_move_constructible_v<result_set>);
|
|
static_assert(std::is_nothrow_move_assignable_v<result_set>);
|
|
|
|
class deserialization_error : public std::runtime_error {
|
|
public:
|
|
using runtime_error::runtime_error;
|
|
};
|
|
|
|
// Result set builder is passed as a visitor to query_result::consume()
|
|
// function. You can call the build() method to obtain a result set that
|
|
// contains cells from the visited results.
|
|
class result_set_builder {
|
|
schema_ptr _schema;
|
|
const partition_slice& _slice;
|
|
result_set::rows_type _rows;
|
|
std::unordered_map<sstring, non_null_data_value> _pkey_cells;
|
|
uint64_t _row_count;
|
|
public:
|
|
// Keep slice live as long as the builder is used.
|
|
result_set_builder(schema_ptr schema, const partition_slice& slice);
|
|
result_set build();
|
|
void accept_new_partition(const partition_key& key, uint64_t row_count);
|
|
void accept_new_partition(uint64_t row_count);
|
|
void accept_new_row(const clustering_key& key, const result_row_view& static_row, const result_row_view& row);
|
|
void accept_new_row(const result_row_view &static_row, const result_row_view &row);
|
|
void accept_partition_end(const result_row_view& static_row);
|
|
private:
|
|
std::unordered_map<sstring, non_null_data_value> deserialize(const partition_key& key);
|
|
std::unordered_map<sstring, non_null_data_value> deserialize(const clustering_key& key);
|
|
std::unordered_map<sstring, non_null_data_value> deserialize(const result_row_view& row, bool is_static);
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& out, const result_set_row& row) {
|
|
for (auto&& cell : row.cells()) {
|
|
auto&& type = static_cast<const data_value&>(cell.second).type();
|
|
auto&& value = cell.second;
|
|
out << cell.first << "=\"" << type->to_string(type->decompose(value)) << "\" ";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& out, const result_set& rs) {
|
|
for (auto&& row : rs._rows) {
|
|
out << row << std::endl;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
static logging::logger query_result_log("query_result_log");
|
|
|
|
non_null_data_value::non_null_data_value(data_value&& v) : _v(std::move(v)) {
|
|
if (_v.is_null()) {
|
|
on_internal_error(query_result_log, "Trying to add a null data_value to a result_set_row");
|
|
}
|
|
}
|
|
|
|
result_set_builder::result_set_builder(schema_ptr schema, const partition_slice& slice)
|
|
: _schema{schema}, _slice(slice)
|
|
{ }
|
|
|
|
result_set result_set_builder::build() {
|
|
return { _schema, std::move(_rows) };
|
|
}
|
|
|
|
void result_set_builder::accept_new_partition(const partition_key& key, uint64_t row_count)
|
|
{
|
|
_pkey_cells = deserialize(key);
|
|
accept_new_partition(row_count);
|
|
}
|
|
|
|
void result_set_builder::accept_new_partition(uint64_t row_count)
|
|
{
|
|
_row_count = row_count;
|
|
}
|
|
|
|
void result_set_builder::accept_new_row(const clustering_key& key, const result_row_view& static_row, const result_row_view& row)
|
|
{
|
|
auto ckey_cells = deserialize(key);
|
|
auto static_cells = deserialize(static_row, true);
|
|
auto regular_cells = deserialize(row, false);
|
|
|
|
std::unordered_map<sstring, non_null_data_value> cells;
|
|
cells.insert(_pkey_cells.begin(), _pkey_cells.end());
|
|
cells.insert(ckey_cells.begin(), ckey_cells.end());
|
|
cells.insert(static_cells.begin(), static_cells.end());
|
|
cells.insert(regular_cells.begin(), regular_cells.end());
|
|
_rows.emplace_back(_schema, std::move(cells));
|
|
}
|
|
|
|
void result_set_builder::accept_new_row(const query::result_row_view &static_row, const query::result_row_view &row)
|
|
{
|
|
auto static_cells = deserialize(static_row, true);
|
|
auto regular_cells = deserialize(row, false);
|
|
|
|
std::unordered_map<sstring, non_null_data_value> cells;
|
|
cells.insert(_pkey_cells.begin(), _pkey_cells.end());
|
|
cells.insert(static_cells.begin(), static_cells.end());
|
|
cells.insert(regular_cells.begin(), regular_cells.end());
|
|
_rows.emplace_back(_schema, std::move(cells));
|
|
}
|
|
|
|
void result_set_builder::accept_partition_end(const result_row_view& static_row)
|
|
{
|
|
if (_row_count == 0) {
|
|
auto static_cells = deserialize(static_row, true);
|
|
std::unordered_map<sstring, non_null_data_value> cells;
|
|
cells.insert(_pkey_cells.begin(), _pkey_cells.end());
|
|
cells.insert(static_cells.begin(), static_cells.end());
|
|
_rows.emplace_back(_schema, std::move(cells));
|
|
}
|
|
_pkey_cells.clear();
|
|
}
|
|
|
|
std::unordered_map<sstring, non_null_data_value>
|
|
result_set_builder::deserialize(const partition_key& key)
|
|
{
|
|
std::unordered_map<sstring, non_null_data_value> cells;
|
|
auto i = key.begin(*_schema);
|
|
for (auto&& col : _schema->partition_key_columns()) {
|
|
cells.emplace(col.name_as_text(), col.type->deserialize_value(*i));
|
|
++i;
|
|
}
|
|
return cells;
|
|
}
|
|
|
|
std::unordered_map<sstring, non_null_data_value>
|
|
result_set_builder::deserialize(const clustering_key& key)
|
|
{
|
|
std::unordered_map<sstring, non_null_data_value> cells;
|
|
auto i = key.begin(*_schema);
|
|
for (auto&& col : _schema->clustering_key_columns()) {
|
|
if (i == key.end(*_schema)) {
|
|
break;
|
|
}
|
|
cells.emplace(col.name_as_text(), col.type->deserialize_value(*i));
|
|
++i;
|
|
}
|
|
return cells;
|
|
}
|
|
|
|
std::unordered_map<sstring, non_null_data_value>
|
|
result_set_builder::deserialize(const result_row_view& row, bool is_static)
|
|
{
|
|
std::unordered_map<sstring, non_null_data_value> cells;
|
|
auto i = row.iterator();
|
|
auto column_ids = is_static ? _slice.static_columns : _slice.regular_columns;
|
|
auto columns = column_ids | std::views::transform([this, is_static] (column_id id) -> const column_definition& {
|
|
if (is_static) {
|
|
return _schema->static_column_at(id);
|
|
} else {
|
|
return _schema->regular_column_at(id);
|
|
}
|
|
});
|
|
size_t index = 0;
|
|
for (auto &&col : columns) {
|
|
try {
|
|
if (col.is_atomic()) {
|
|
auto cell = i.next_atomic_cell();
|
|
if (cell) {
|
|
cells.emplace(col.name_as_text(), col.type->deserialize_value(cell->value()));
|
|
}
|
|
} else {
|
|
auto cell = i.next_collection_cell();
|
|
if (cell) {
|
|
if (col.type->is_collection()) {
|
|
auto ctype = static_pointer_cast<const collection_type_impl>(col.type);
|
|
if (_slice.options.contains<partition_slice::option::collections_as_maps>()) {
|
|
ctype = map_type_impl::get_instance(ctype->name_comparator(), ctype->value_comparator(), true);
|
|
}
|
|
|
|
cells.emplace(col.name_as_text(), ctype->deserialize_value(*cell));
|
|
} else {
|
|
cells.emplace(col.name_as_text(), col.type->deserialize_value(*cell));
|
|
}
|
|
}
|
|
}
|
|
index++;
|
|
} catch (...) {
|
|
throw deserialization_error(fmt::format(FMT_STRING("failed on column {}.{}#{} (version: {}, id: {}, index: {}, type: {}): {}"),
|
|
_schema->ks_name(), _schema->cf_name(), col.name_as_text(), _schema->version(), col.id, index, col.type->name(), std::current_exception()));
|
|
}
|
|
}
|
|
return cells;
|
|
}
|
|
|
|
result_set
|
|
result_set::from_raw_result(schema_ptr s, const partition_slice& slice, const result& r) {
|
|
result_set_builder builder{std::move(s), slice};
|
|
result_view::consume(r, slice, builder);
|
|
return builder.build();
|
|
}
|
|
|
|
result_set::result_set(const mutation& m) : result_set([&m] {
|
|
auto slice = partition_slice_builder(*m.schema()).build();
|
|
auto qr = query_mutation(mutation(m), slice);
|
|
return result_set::from_raw_result(m.schema(), slice, qr);
|
|
}())
|
|
{ }
|
|
|
|
}
|