Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e3f59d5
Initial hot/cold allocation proof of concept
ambuc Apr 11, 2018
bfd2084
Drafting out new BlockStatDataAllocator colocated for now
ambuc Apr 12, 2018
c306c01
Working draft of BlockRawStatDataAllocator passing all AllocConsisten…
ambuc Apr 16, 2018
a263690
HotRestartImpl now relies upon BlockRawStatDataAllocator
ambuc Apr 17, 2018
9298e3c
Formatting updates
ambuc Apr 17, 2018
f1e94fa
Assorted nits
ambuc Apr 17, 2018
e49a15a
Actually use BlockRawStatDataAllocator in BlockRawStatDataAllocator test
ambuc Apr 17, 2018
d7dc10e
Make BlockRawStatDataAllocator member variable stats_set_ private
ambuc Apr 17, 2018
de2879d
Formatting updates
ambuc Apr 17, 2018
e9c65fa
Rework HotRestartImpl's member variable allocator_ to be a unique poi…
ambuc Apr 17, 2018
5dde224
Remove not-ready tests
ambuc Apr 18, 2018
46d4b9e
Rename SharedMemoryHashSet to MemoryHashSet
ambuc Apr 18, 2018
5466e13
Move allocator unit tests from hot_restart_impl_test to stats_impl_test
ambuc Apr 18, 2018
04d28f2
Add uniqueNaming test
ambuc Apr 18, 2018
bc25b35
Add stat_lock_ mutex to BlockRawStatDataAllocator
ambuc Apr 18, 2018
adf9a6e
Add stat_lock for mocking purposes inside HotRestart abstract class
ambuc Apr 18, 2018
3dde624
Add some documentation around BlockRawStatDataAllocator, assorted for…
ambuc Apr 18, 2018
3c773a8
Undo statLock stuff, replace calloc with more c-like allocation seman…
ambuc Apr 19, 2018
01c70ca
HotRestartImpl and HotRestartNopImpl now both create allocators, supp…
ambuc Apr 19, 2018
2b9f176
Assorted formatting fixes
ambuc Apr 19, 2018
1916c2c
Rename SharedMemoryHashSet to BlockMemoryHashSet
ambuc Apr 20, 2018
765df8e
Actually pass in stat_lock by reference to BlockRawStatDataAllocator …
ambuc Apr 20, 2018
afeb8f2
Formatting updates
ambuc Apr 20, 2018
5aff3d1
Minor nits
ambuc Apr 20, 2018
cc22d9e
Swap out TestAllocator for real instance of BlockRawStatDataAllocator…
ambuc Apr 20, 2018
fafdf72
Formatting updates
ambuc Apr 20, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions source/common/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ envoy_cc_library(
)

envoy_cc_library(
name = "shared_memory_hash_set_lib",
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrice32 This is one of a few commits which rename SharedMemoryHashSet to MemoryHashSet, or shared_memory_hash_set to memory_hash_set, or sharedMemOptions to memOptions. I wanted to indicate that the MemoryHashSet doesn't need to be run on shared memory.

hdrs = ["shared_memory_hash_set.h"],
name = "block_memory_hash_set_lib",
hdrs = ["block_memory_hash_set.h"],
deps = [
":assert_lib",
":logger_lib",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@
namespace Envoy {

/**
* Initialization parameters for SharedMemoryHashSet. The options are duplicated
* Initialization parameters for BlockMemoryHashSet. The options are duplicated
* to the control-block after init, to aid with sanity checking when attaching
* an existing memory segment.
*/
struct SharedMemoryHashSetOptions {
struct BlockMemoryHashSetOptions {
std::string toString() const {
return fmt::format("capacity={}, num_slots={}", capacity, num_slots);
}
bool operator==(const SharedMemoryHashSetOptions& that) const {
bool operator==(const BlockMemoryHashSetOptions& that) const {
return capacity == that.capacity && num_slots == that.num_slots;
}
bool operator!=(const SharedMemoryHashSetOptions& that) const { return !(*this == that); }
bool operator!=(const BlockMemoryHashSetOptions& that) const { return !(*this == that); }

uint32_t capacity; // how many values can be stored.
uint32_t num_slots; // determines speed of hash vs size efficiency.
Expand All @@ -45,7 +45,7 @@ struct SharedMemoryHashSetOptions {
* term storage, but not across machine architectures, as it doesn't
* use network byte order for storing ints.
*/
template <class Value> class SharedMemoryHashSet : public Logger::Loggable<Logger::Id::config> {
template <class Value> class BlockMemoryHashSet : public Logger::Loggable<Logger::Id::config> {
public:
/**
* Sentinal used for next_cell links to indicate end-of-list.
Expand All @@ -58,31 +58,31 @@ template <class Value> class SharedMemoryHashSet : public Logger::Loggable<Logge
/**
* Constructs a map control structure given a set of options, which cannot be changed.
* @param options describes the parameters comtrolling set layout.
* @param init true if the shared-memory should be initialized on construction. If false,
* @param init true if the memory should be initialized on construction. If false,
* the data in the table will be sanity checked, and an exception thrown if
* it is incoherent or mismatches the passed-in options.
* @param memory the memory buffer for the set data.
*
* Note that no locking of any kind is done by this class; this must be done at the
* call-site to support concurrent access.
*/
SharedMemoryHashSet(const SharedMemoryHashSetOptions& options, bool init, uint8_t* memory)
BlockMemoryHashSet(const BlockMemoryHashSetOptions& options, bool init, uint8_t* memory)
: cells_(nullptr), control_(nullptr), slots_(nullptr) {
mapMemorySegments(options, memory);
if (init) {
initialize(options);
} else if (!attach(options)) {
throw EnvoyException("SharedMemoryHashSet: Incompatible memory block");
throw EnvoyException("BlockMemoryHashSet: Incompatible memory block");
}
}

/**
* Returns the numbers of byte required for the hash-table, based on
* the control structure. This must be used to allocate the
* backing-store (eg) in shared memory, which we do after
* backing-store (eg) in memory, which we do after
* constructing the object with the desired sizing.
*/
static uint32_t numBytes(const SharedMemoryHashSetOptions& options) {
static uint32_t numBytes(const BlockMemoryHashSetOptions& options) {
uint32_t size =
cellOffset(options.capacity) + sizeof(Control) + options.num_slots * sizeof(uint32_t);
return align(size);
Expand All @@ -93,7 +93,7 @@ template <class Value> class SharedMemoryHashSet : public Logger::Loggable<Logge
/**
* Returns the options structure that was used to construct the set.
*/
const SharedMemoryHashSetOptions& options() const { return control_->options; }
const BlockMemoryHashSetOptions& options() const { return control_->options; }

/** Examines the data structures to see if they are sane, assert-failing on any trouble. */
void sanityCheck() {
Expand Down Expand Up @@ -221,14 +221,14 @@ template <class Value> class SharedMemoryHashSet : public Logger::Loggable<Logge
}

private:
friend class SharedMemoryHashSetTest;
friend class BlockMemoryHashSetTest;

/**
* Initializes a hash-map on raw memory. No expectations are made about the state of the memory
* coming in.
* @param memory
*/
void initialize(const SharedMemoryHashSetOptions& options) {
void initialize(const BlockMemoryHashSetOptions& options) {
control_->hash_signature = Value::hash(signatureStringToHash());
control_->num_bytes = numBytes(options);
control_->options = options;
Expand All @@ -250,18 +250,18 @@ template <class Value> class SharedMemoryHashSet : public Logger::Loggable<Logge
}

/**
* Attempts to attach to an existing shared memory segment. Does a (relatively) quick
* Attempts to attach to an existing memory segment. Does a (relatively) quick
* sanity check to make sure the options copied to the provided memory match, and also
* that the slot, cell, and key-string structures look sane.
*/
bool attach(const SharedMemoryHashSetOptions& options) {
bool attach(const BlockMemoryHashSetOptions& options) {
if (numBytes(options) != control_->num_bytes) {
ENVOY_LOG(error, "SharedMemoryHashSet unexpected memory size {} != {}", numBytes(options),
ENVOY_LOG(error, "BlockMemoryHashSet unexpected memory size {} != {}", numBytes(options),
control_->num_bytes);
return false;
}
if (Value::hash(signatureStringToHash()) != control_->hash_signature) {
ENVOY_LOG(error, "SharedMemoryHashSet hash signature mismatch.");
ENVOY_LOG(error, "BlockMemoryHashSet hash signature mismatch.");
return false;
}
sanityCheck();
Expand Down Expand Up @@ -290,11 +290,11 @@ template <class Value> class SharedMemoryHashSet : public Logger::Loggable<Logge
* Represents control-values for the hash-table.
*/
struct Control {
SharedMemoryHashSetOptions options; // Options established at map construction time.
uint64_t hash_signature; // Hash of a constant signature string.
uint32_t num_bytes; // Bytes allocated on behalf of the map.
uint32_t size; // Number of values currently stored.
uint32_t free_cell_index; // Offset of first free cell.
BlockMemoryHashSetOptions options; // Options established at map construction time.
uint64_t hash_signature; // Hash of a constant signature string.
uint32_t num_bytes; // Bytes allocated on behalf of the map.
uint32_t size; // Number of values currently stored.
uint32_t free_cell_index; // Offset of first free cell.
};

/**
Expand Down Expand Up @@ -340,8 +340,8 @@ template <class Value> class SharedMemoryHashSet : public Logger::Loggable<Logge
return *reinterpret_cast<Cell*>(ptr);
}

/** Maps out the segments of shared memory for us to work with. */
void mapMemorySegments(const SharedMemoryHashSetOptions& options, uint8_t* memory) {
/** Maps out the segments of memory for us to work with. */
void mapMemorySegments(const BlockMemoryHashSetOptions& options, uint8_t* memory) {
// Note that we are not examining or mutating memory here, just looking at the pointer,
// so we don't need to hold any locks.
cells_ = reinterpret_cast<Cell*>(memory); // First because Value may need to be aligned.
Expand All @@ -351,7 +351,7 @@ template <class Value> class SharedMemoryHashSet : public Logger::Loggable<Logge
slots_ = reinterpret_cast<uint32_t*>(memory);
}

// Pointers into shared memory. Cells go first, because Value may need a more aggressive
// Pointers into memory. Cells go first, because Value may need a more aggressive
// aligmnment.
Cell* cells_;
Control* control_;
Expand Down
1 change: 1 addition & 0 deletions source/common/stats/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ envoy_cc_library(
"//include/envoy/server:options_interface",
"//include/envoy/stats:stats_interface",
"//source/common/common:assert_lib",
"//source/common/common:block_memory_hash_set_lib",
"//source/common/common:hash_lib",
"//source/common/common:perf_annotation_lib",
"//source/common/common:utility_lib",
Expand Down
46 changes: 40 additions & 6 deletions source/common/stats/stats_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,53 @@ bool TagExtractorImpl::extractTag(const std::string& stat_name, std::vector<Tag>
return false;
}

RawStatData* BlockRawStatDataAllocator::alloc(const std::string& name) {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved a lot of these tests from hot_restart_impl_test.cc since they are really unit tests on the allocator itself. A few stayed in hot_restart_impl_test.cc as they tested behavior across hot restart.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What tests are you referring to? This is an implementation file...?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, I don't know how this ended up here. I think I meant to say I moved these ::alloc and ::free functions from hot_restart_impl.cc.

std::unique_lock<Thread::BasicLockable> lock(stat_lock_);

absl::string_view key = name;
if (key.size() > Stats::RawStatData::maxNameLength()) {
key.remove_suffix(key.size() - Stats::RawStatData::maxNameLength());
}
auto value_created = stats_set_->insert(key);
Stats::RawStatData* data = value_created.first;
if (data == nullptr) {
return nullptr;
}
// For new entries (value-created.second==true), BlockMemoryHashSet calls Value::initialize()
// automatically, but on recycled entries (value-created.second==false) we need to bump the
// ref-count.
if (!value_created.second) {
++data->ref_count_;
}
return data;
}

void BlockRawStatDataAllocator::free(RawStatData& data) {
// We must hold the lock since the reference decrement can race with an initialize above.
std::unique_lock<Thread::BasicLockable> lock(stat_lock_);

ASSERT(data.ref_count_ > 0);
if (--data.ref_count_ > 0) {
return;
}
bool key_removed = stats_set_->remove(data.key());
ASSERT(key_removed);
memset(&data, 0, Stats::RawStatData::size());
}

RawStatData* HeapRawStatDataAllocator::alloc(const std::string& name) {
// This must be zero-initialized
RawStatData* data = static_cast<RawStatData*>(::calloc(RawStatData::size(), 1));
data->initialize(name);
return data;
}

void HeapRawStatDataAllocator::free(RawStatData& data) {
// This allocator does not ever have concurrent access to the raw data.
ASSERT(data.ref_count_ == 1);
::free(&data);
}

TagProducerImpl::TagProducerImpl(const envoy::config::metrics::v2::StatsConfig& config)
: TagProducerImpl() {
// To check name conflict.
Expand Down Expand Up @@ -255,12 +295,6 @@ TagProducerImpl::addDefaultExtractors(const envoy::config::metrics::v2::StatsCon
return names;
}

void HeapRawStatDataAllocator::free(RawStatData& data) {
// This allocator does not ever have concurrent access to the raw data.
ASSERT(data.ref_count_ == 1);
::free(&data);
}

void RawStatData::initialize(absl::string_view key) {
ASSERT(!initialized());
ASSERT(key.size() <= maxNameLength());
Expand Down
48 changes: 41 additions & 7 deletions source/common/stats/stats_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "envoy/stats/stats.h"

#include "common/common/assert.h"
#include "common/common/block_memory_hash_set.h"
#include "common/common/hash.h"
#include "common/common/utility.h"
#include "common/protobuf/protobuf.h"
Expand All @@ -26,6 +27,8 @@
namespace Envoy {
namespace Stats {

typedef BlockMemoryHashSet<Stats::RawStatData> RawStatDataSet;

class TagExtractorImpl : public TagExtractor {
public:
/**
Expand Down Expand Up @@ -217,19 +220,19 @@ struct RawStatData {

/**
* Returns the size of this struct, accounting for the length of name_
* and padding for alignment. This is required by SharedMemoryHashSet.
* and padding for alignment. This is required by BlockMemoryHashSet.
*/
static size_t size();

/**
* Initializes this object to have the specified key,
* a refcount of 1, and all other values zero. This is required by
* SharedMemoryHashSet.
* BlockMemoryHashSet.
*/
void initialize(absl::string_view key);

/**
* Returns a hash of the key. This is required by SharedMemoryHashSet.
* Returns a hash of the key. This is required by BlockMemoryHashSet.
*/
static uint64_t hash(absl::string_view key) { return HashUtil::xxHash64(key); }

Expand All @@ -239,7 +242,7 @@ struct RawStatData {
bool initialized() { return name_[0] != '\0'; }

/**
* Returns the name as a string_view. This is required by SharedMemoryHashSet.
* Returns the name as a string_view. This is required by BlockMemoryHashSet.
*/
absl::string_view key() const {
return absl::string_view(name_, strnlen(name_, maxNameLength()));
Expand Down Expand Up @@ -370,12 +373,43 @@ class HistogramImpl : public Histogram, public MetricImpl {
};

/**
* Implementation of RawStatDataAllocator that just allocates a new structure in memory and returns
* it.
* Implementation of RawStatDataAllocator which accepts a block of (potentially shared) memory.
*/
class BlockRawStatDataAllocator : public RawStatDataAllocator {
public:
/**
* Creates a RawStatDataAllocator with an underlying MemoryHashMap to track stat names. Optionally
* works with shared memory, as in HotRestartImpl. Thread-safe but requires a lock as argument.
* @param options initialization parameters for BlockMemoryHashSet
* @param init true if the shared-memory should be initialized on construction. See the
* BlockMemoryHashSet constructor.
* @param memory the memory buffer for the set data.
* @param stat_lock a lock for thread safety.
* @return BlockRawStatDataAllocator newly constructed BlockRawStatDataAllocator.
*/
BlockRawStatDataAllocator(const BlockMemoryHashSetOptions& options, bool init, uint8_t* memory,
Thread::BasicLockable& stat_lock)
: stat_lock_(stat_lock) {
stats_set_ = std::make_unique<RawStatDataSet>(options, init, memory);
}

RawStatData* alloc(const std::string& name);
void free(RawStatData& data);
std::string version() { return stats_set_->version(); }

private:
std::unique_ptr<RawStatDataSet> stats_set_;
// We must hold the stat lock when attaching to an existing memory segment
// because it might be actively written to while we sanityCheck it.
Thread::BasicLockable& stat_lock_;
};

/**
* Heap-based implementation of RawStatDataAllocator that just allocates a new structure in memory
* and returns it.
*/
class HeapRawStatDataAllocator : public RawStatDataAllocator {
public:
// RawStatDataAllocator
RawStatData* alloc(const std::string& name) override;
void free(RawStatData& data) override;
};
Expand Down
4 changes: 2 additions & 2 deletions source/exe/main_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ MainCommonBase::MainCommonBase(OptionsImpl& options) : options_(options) {
}
#endif
if (restarter_.get() == nullptr) {
restarter_.reset(new Server::HotRestartNopImpl());
restarter_.reset(new Server::HotRestartNopImpl(options_));
}

tls_.reset(new ThreadLocal::InstanceImpl);
Expand All @@ -69,7 +69,7 @@ MainCommonBase::MainCommonBase(OptionsImpl& options) : options_(options) {
break;
}
case Server::Mode::Validate:
restarter_.reset(new Server::HotRestartNopImpl());
restarter_.reset(new Server::HotRestartNopImpl(options_));
Logger::Registry::initialize(options_.logLevel(), options_.logFormat(), restarter_->logLock());
break;
}
Expand Down
3 changes: 2 additions & 1 deletion source/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ envoy_cc_library(
"//include/envoy/server:options_interface",
"//source/common/api:os_sys_calls_lib",
"//source/common/common:assert_lib",
"//source/common/common:shared_memory_hash_set_lib",
"//source/common/common:block_memory_hash_set_lib",
"//source/common/common:utility_lib",
"//source/common/network:utility_lib",
"//source/common/stats:stats_lib",
Expand All @@ -128,6 +128,7 @@ envoy_cc_library(
hdrs = ["hot_restart_nop_impl.h"],
deps = [
"//include/envoy/server:hot_restart_interface",
"//source/server:hot_restart_lib",
],
)

Expand Down
Loading