Files
scylladb/sstables/index_reader.hh
Glauber Costa aab1ae9dc1 index_entry: don't generate a temporary bytes element
The one thing that is still showing pretty high at the read_indexes flamegraph,
is allocations.

We can, however, do better. Since most of the index is the keys anyway - and we need
all of them, the amount of memory we use by copying the buffers over is about the same
as the space we would use by just keeping the buffers around.

So we can change index_entry to just keep the shared_buffers, and since we always access
it through views anyway, that is perfectly fine. The index_entry destructor will then
release() the temporary_buffer, instead of doing this after the buffer copy.

This gives us a nice additional 4 %.

perf_sstable_g  --smp 1 --iterations 30 --parallelism 1 --mode index_read

Before:
839484.65 +- 585.52 partitions / sec (30 runs, 1 concurrent ops)

After:
873323.18 +- 442.52 partitions / sec (30 runs, 1 concurrent ops)

Signed-off-by: Glauber Costa <glommer@cloudius-systems.com>
2015-08-29 14:09:53 -05:00

107 lines
2.9 KiB
C++

/*
* Copyright 2015 Cloudius Systems
*/
#pragma once
#include "sstables.hh"
#include "consumer.hh"
namespace sstables {
class index_consumer {
uint64_t max_quantity;
public:
index_list indexes;
index_consumer(uint64_t q) : max_quantity(q) {
indexes.reserve(q);
}
bool should_continue() {
return indexes.size() < max_quantity;
}
void consume_entry(index_entry&& ie) {
indexes.push_back(std::move(ie));
}
};
class index_consume_entry_context: public data_consumer::continuous_data_consumer<index_consume_entry_context> {
using proceed = data_consumer::proceed;
private:
index_consumer& _consumer;
enum class state {
START,
KEY_SIZE,
KEY_BYTES,
POSITION,
PROMOTED_SIZE,
PROMOTED_BYTES,
CONSUME_ENTRY,
} _state = state::START;
temporary_buffer<char> _key;
temporary_buffer<char> _promoted;
public:
void verify_end_state() {
}
bool non_consuming() const {
return ((_state == state::CONSUME_ENTRY) || (_state == state::START) ||
((_state == state::PROMOTED_BYTES) && (_prestate == prestate::NONE)));
}
proceed process_state(temporary_buffer<char>& data) {
switch (_state) {
// START comes first, to make the handling of the 0-quantity case simpler
case state::START:
if (!_consumer.should_continue()) {
return proceed::no;
}
_state = state::KEY_SIZE;
break;
case state::KEY_SIZE:
if (read_16(data) != read_status::ready) {
_state = state::KEY_BYTES;
break;
}
case state::KEY_BYTES:
if (read_bytes(data, _u16, _key) != read_status::ready) {
_state = state::POSITION;
break;
}
case state::POSITION:
if (read_64(data) != read_status::ready) {
_state = state::PROMOTED_SIZE;
break;
}
case state::PROMOTED_SIZE:
if (read_32(data) != read_status::ready) {
_state = state::PROMOTED_BYTES;
break;
}
case state::PROMOTED_BYTES:
if (read_bytes(data, _u32, _promoted) != read_status::ready) {
_state = state::CONSUME_ENTRY;
break;
}
case state::CONSUME_ENTRY:
_consumer.consume_entry(index_entry(std::move(_key), _u64, std::move(_promoted)));
_state = state::START;
break;
default:
throw malformed_sstable_exception("unknown state");
}
return proceed::yes;
}
index_consume_entry_context(index_consumer& consumer,
input_stream<char>&& input, uint64_t maxlen)
: continuous_data_consumer(std::move(input), maxlen)
, _consumer(consumer)
{}
};
}