Strongly typed slices for server method/host.

Leveraging the new strongly typed core/internal slices, we are able to remove
some branches and unnecessary ops pertaining to method/host slices in grpc
server.
pull/19987/head
Arjun Roy 5 years ago
parent 4780da7b50
commit a0f2551d17
  1. 81
      src/core/lib/slice/slice_intern.cc
  2. 6
      src/core/lib/slice/slice_internal.h
  3. 25
      src/core/lib/slice/slice_utils.h
  4. 33
      src/core/lib/surface/server.cc

@ -50,10 +50,6 @@ typedef struct slice_shard {
size_t capacity; size_t capacity;
} slice_shard; } slice_shard;
/* hash seed: decided at initialization time */
uint32_t g_hash_seed;
static int g_forced_hash_seed = 0;
static slice_shard g_shards[SHARD_COUNT]; static slice_shard g_shards[SHARD_COUNT];
typedef struct { typedef struct {
@ -68,6 +64,10 @@ uint32_t grpc_static_metadata_hash_values[GRPC_STATIC_MDSTR_COUNT];
namespace grpc_core { namespace grpc_core {
/* hash seed: decided at initialization time */
uint32_t g_hash_seed;
static bool g_forced_hash_seed = false;
InternedSliceRefcount::~InternedSliceRefcount() { InternedSliceRefcount::~InternedSliceRefcount() {
slice_shard* shard = &g_shards[SHARD_IDX(this->hash)]; slice_shard* shard = &g_shards[SHARD_IDX(this->hash)];
MutexLock lock(&shard->mu); MutexLock lock(&shard->mu);
@ -115,7 +115,7 @@ grpc_core::InternedSlice::InternedSlice(InternedSliceRefcount* s) {
uint32_t grpc_slice_default_hash_impl(grpc_slice s) { uint32_t grpc_slice_default_hash_impl(grpc_slice s) {
return gpr_murmur_hash3(GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s), return gpr_murmur_hash3(GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s),
g_hash_seed); grpc_core::g_hash_seed);
} }
uint32_t grpc_static_slice_hash(grpc_slice s) { uint32_t grpc_static_slice_hash(grpc_slice s) {
@ -159,20 +159,20 @@ grpc_slice grpc_slice_intern(grpc_slice slice) {
} }
// Attempt to see if the provided slice or string matches a static slice. // Attempt to see if the provided slice or string matches a static slice.
// SliceArgs... is either a const grpc_slice& or a string and length. In either // SliceArgs is either a const grpc_slice& or const pair<const char*, size_t>&.
// case, hash is the pre-computed hash value. // In either case, hash is the pre-computed hash value.
// //
// Returns: a matching static slice, or null. // Returns: a matching static slice, or null.
template <class... SliceArgs> template <typename SliceArgs>
static const grpc_core::StaticMetadataSlice* MatchStaticSlice( static const grpc_core::StaticMetadataSlice* MatchStaticSlice(
uint32_t hash, SliceArgs&&... args) { uint32_t hash, const SliceArgs& args) {
for (uint32_t i = 0; i <= max_static_metadata_hash_probe; i++) { for (uint32_t i = 0; i <= max_static_metadata_hash_probe; i++) {
static_metadata_hash_ent ent = static_metadata_hash_ent ent =
static_metadata_hash[(hash + i) % GPR_ARRAY_SIZE(static_metadata_hash)]; static_metadata_hash[(hash + i) % GPR_ARRAY_SIZE(static_metadata_hash)];
const grpc_core::StaticMetadataSlice* static_slice_table = const grpc_core::StaticMetadataSlice* static_slice_table =
grpc_static_slice_table(); grpc_static_slice_table();
if (ent.hash == hash && ent.idx < GRPC_STATIC_MDSTR_COUNT && if (ent.hash == hash && ent.idx < GRPC_STATIC_MDSTR_COUNT &&
static_slice_table[ent.idx].Equals(std::forward<SliceArgs>(args)...)) { static_slice_table[ent.idx] == args) {
return &static_slice_table[ent.idx]; return &static_slice_table[ent.idx];
} }
} }
@ -182,8 +182,12 @@ static const grpc_core::StaticMetadataSlice* MatchStaticSlice(
// Helper methods to enable us to select appropriately overloaded slice methods // Helper methods to enable us to select appropriately overloaded slice methods
// whether we're dealing with a slice, or a buffer with length, when interning // whether we're dealing with a slice, or a buffer with length, when interning
// strings. Helpers for FindOrCreateInternedSlice(). // strings. Helpers for FindOrCreateInternedSlice().
static const void* GetBuffer(const void* buf, size_t len) { return buf; } static const char* GetBuffer(const std::pair<const char*, size_t>& buflen) {
static size_t GetLength(const void* buf, size_t len) { return len; } return buflen.first;
}
static size_t GetLength(const std::pair<const char*, size_t>& buflen) {
return buflen.second;
}
static const void* GetBuffer(const grpc_slice& slice) { static const void* GetBuffer(const grpc_slice& slice) {
return GRPC_SLICE_START_PTR(slice); return GRPC_SLICE_START_PTR(slice);
} }
@ -192,19 +196,19 @@ static size_t GetLength(const grpc_slice& slice) {
} }
// Creates an interned slice for a string that does not currently exist in the // Creates an interned slice for a string that does not currently exist in the
// intern table. SliceArgs... is either a const grpc_slice& or a string and // intern table. SliceArgs is either a const grpc_slice& or a const
// length. In either case, hash is the pre-computed hash value. We must already // pair<const char*, size_t>&. Hash is the pre-computed hash value. We must
// hold the shard lock. Helper for FindOrCreateInternedSlice(). // already hold the shard lock. Helper for FindOrCreateInternedSlice().
// //
// Returns: a newly interned slice. // Returns: a newly interned slice.
template <class... SliceArgs> template <typename SliceArgs>
static InternedSliceRefcount* InternNewStringLocked(slice_shard* shard, static InternedSliceRefcount* InternNewStringLocked(slice_shard* shard,
size_t shard_idx, size_t shard_idx,
uint32_t hash, uint32_t hash,
SliceArgs&&... args) { const SliceArgs& args) {
/* string data goes after the internal_string header */ /* string data goes after the internal_string header */
size_t len = GetLength(std::forward<SliceArgs>(args)...); size_t len = GetLength(args);
const void* buffer = GetBuffer(std::forward<SliceArgs>(args)...); const void* buffer = GetBuffer(args);
InternedSliceRefcount* s = InternedSliceRefcount* s =
static_cast<InternedSliceRefcount*>(gpr_malloc(sizeof(*s) + len)); static_cast<InternedSliceRefcount*>(gpr_malloc(sizeof(*s) + len));
new (s) grpc_core::InternedSliceRefcount(len, hash, shard->strs[shard_idx]); new (s) grpc_core::InternedSliceRefcount(len, hash, shard->strs[shard_idx]);
@ -227,16 +231,15 @@ static InternedSliceRefcount* InternNewStringLocked(slice_shard* shard,
// shard lock. Helper for FindOrCreateInternedSlice(). // shard lock. Helper for FindOrCreateInternedSlice().
// //
// Returns: a pre-existing matching static slice, or null. // Returns: a pre-existing matching static slice, or null.
template <class... SliceArgs> template <typename SliceArgs>
static InternedSliceRefcount* MatchInternedSliceLocked(uint32_t hash, static InternedSliceRefcount* MatchInternedSliceLocked(uint32_t hash,
size_t idx, size_t idx,
SliceArgs&&... args) { const SliceArgs& args) {
InternedSliceRefcount* s; InternedSliceRefcount* s;
slice_shard* shard = &g_shards[SHARD_IDX(hash)]; slice_shard* shard = &g_shards[SHARD_IDX(hash)];
/* search for an existing string */ /* search for an existing string */
for (s = shard->strs[idx]; s; s = s->bucket_next) { for (s = shard->strs[idx]; s; s = s->bucket_next) {
if (s->hash == hash && if (s->hash == hash && grpc_core::InternedSlice(s) == args) {
grpc_core::InternedSlice(s).Equals(std::forward<SliceArgs>(args)...)) {
if (s->refcnt.RefIfNonZero()) { if (s->refcnt.RefIfNonZero()) {
return s; return s;
} }
@ -248,22 +251,20 @@ static InternedSliceRefcount* MatchInternedSliceLocked(uint32_t hash,
// Attempt to see if the provided slice or string matches an existing interned // Attempt to see if the provided slice or string matches an existing interned
// slice, and failing that, create an interned slice with its contents. Returns // slice, and failing that, create an interned slice with its contents. Returns
// either the existing matching interned slice or the newly created one. // either the existing matching interned slice or the newly created one.
// SliceArgs... is either a const grpc_slice& or a string and length. In either // SliceArgs is either a const grpc_slice& or const pair<const char*, size_t>&.
// case, hash is the pre-computed hash value. We do not hold the shard lock // In either case, hash is the pre-computed hash value. We do not hold the
// here, but do take it. // shard lock here, but do take it.
// //
// Returns: an interned slice, either pre-existing/matched or newly created. // Returns: an interned slice, either pre-existing/matched or newly created.
template <class... SliceArgs> template <typename SliceArgs>
static InternedSliceRefcount* FindOrCreateInternedSlice(uint32_t hash, static InternedSliceRefcount* FindOrCreateInternedSlice(uint32_t hash,
SliceArgs&&... args) { const SliceArgs& args) {
slice_shard* shard = &g_shards[SHARD_IDX(hash)]; slice_shard* shard = &g_shards[SHARD_IDX(hash)];
gpr_mu_lock(&shard->mu); gpr_mu_lock(&shard->mu);
const size_t idx = TABLE_IDX(hash, shard->capacity); const size_t idx = TABLE_IDX(hash, shard->capacity);
InternedSliceRefcount* s = InternedSliceRefcount* s = MatchInternedSliceLocked(hash, idx, args);
MatchInternedSliceLocked(hash, idx, std::forward<SliceArgs>(args)...);
if (s == nullptr) { if (s == nullptr) {
s = InternNewStringLocked(shard, idx, hash, s = InternNewStringLocked(shard, idx, hash, args);
std::forward<SliceArgs>(args)...);
} }
gpr_mu_unlock(&shard->mu); gpr_mu_unlock(&shard->mu);
return s; return s;
@ -277,12 +278,13 @@ grpc_core::ManagedMemorySlice::ManagedMemorySlice(const char* string,
size_t len) { size_t len) {
GPR_TIMER_SCOPE("grpc_slice_intern", 0); GPR_TIMER_SCOPE("grpc_slice_intern", 0);
const uint32_t hash = gpr_murmur_hash3(string, len, g_hash_seed); const uint32_t hash = gpr_murmur_hash3(string, len, g_hash_seed);
const StaticMetadataSlice* static_slice = MatchStaticSlice(hash, string, len); const StaticMetadataSlice* static_slice =
MatchStaticSlice(hash, std::pair<const char*, size_t>(string, len));
if (static_slice) { if (static_slice) {
*this = *static_slice; *this = *static_slice;
} else { } else {
*this = *this = grpc_core::InternedSlice(FindOrCreateInternedSlice(
grpc_core::InternedSlice(FindOrCreateInternedSlice(hash, string, len)); hash, std::pair<const char*, size_t>(string, len)));
} }
} }
@ -303,13 +305,14 @@ grpc_core::ManagedMemorySlice::ManagedMemorySlice(const grpc_slice* slice_ptr) {
} }
void grpc_test_only_set_slice_hash_seed(uint32_t seed) { void grpc_test_only_set_slice_hash_seed(uint32_t seed) {
g_hash_seed = seed; grpc_core::g_hash_seed = seed;
g_forced_hash_seed = 1; grpc_core::g_forced_hash_seed = true;
} }
void grpc_slice_intern_init(void) { void grpc_slice_intern_init(void) {
if (!g_forced_hash_seed) { if (!grpc_core::g_forced_hash_seed) {
g_hash_seed = static_cast<uint32_t>(gpr_now(GPR_CLOCK_REALTIME).tv_nsec); grpc_core::g_hash_seed =
static_cast<uint32_t>(gpr_now(GPR_CLOCK_REALTIME).tv_nsec);
} }
for (size_t i = 0; i < SHARD_COUNT; i++) { for (size_t i = 0; i < SHARD_COUNT; i++) {
slice_shard* shard = &g_shards[i]; slice_shard* shard = &g_shards[i];

@ -36,7 +36,6 @@
// Interned slices have specific fast-path operations for hashing. To inline // Interned slices have specific fast-path operations for hashing. To inline
// these operations, we need to forward declare them here. // these operations, we need to forward declare them here.
extern uint32_t grpc_static_metadata_hash_values[GRPC_STATIC_MDSTR_COUNT]; extern uint32_t grpc_static_metadata_hash_values[GRPC_STATIC_MDSTR_COUNT];
extern uint32_t g_hash_seed;
// grpc_slice_refcount : A reference count for grpc_slice. // grpc_slice_refcount : A reference count for grpc_slice.
// //
@ -259,7 +258,8 @@ inline uint32_t grpc_slice_refcount::Hash(const grpc_slice& slice) {
break; break;
} }
return gpr_murmur_hash3(grpc_refcounted_slice_data(slice), return gpr_murmur_hash3(grpc_refcounted_slice_data(slice),
grpc_refcounted_slice_length(slice), g_hash_seed); grpc_refcounted_slice_length(slice),
grpc_core::g_hash_seed);
} }
inline const grpc_slice& grpc_slice_ref_internal(const grpc_slice& slice) { inline const grpc_slice& grpc_slice_ref_internal(const grpc_slice& slice) {
@ -327,7 +327,7 @@ inline uint32_t grpc_slice_hash_refcounted(const grpc_slice& s) {
inline uint32_t grpc_slice_default_hash_internal(const grpc_slice& s) { inline uint32_t grpc_slice_default_hash_internal(const grpc_slice& s) {
return gpr_murmur_hash3(GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s), return gpr_murmur_hash3(GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s),
g_hash_seed); grpc_core::g_hash_seed);
} }
inline uint32_t grpc_slice_hash_internal(const grpc_slice& s) { inline uint32_t grpc_slice_hash_internal(const grpc_slice& s) {

@ -25,6 +25,12 @@
#include <grpc/slice.h> #include <grpc/slice.h>
#include "src/core/lib/gpr/murmur_hash.h"
namespace grpc_core {
extern uint32_t g_hash_seed;
} // namespace grpc_core
// When we compare two slices, and we know the latter is not inlined, we can // When we compare two slices, and we know the latter is not inlined, we can
// short circuit our comparison operator. We specifically use differs() // short circuit our comparison operator. We specifically use differs()
// semantics instead of equals() semantics due to more favourable code // semantics instead of equals() semantics due to more favourable code
@ -101,15 +107,16 @@ struct ManagedMemorySlice : public grpc_slice {
explicit ManagedMemorySlice(const char* string); explicit ManagedMemorySlice(const char* string);
ManagedMemorySlice(const char* buf, size_t len); ManagedMemorySlice(const char* buf, size_t len);
explicit ManagedMemorySlice(const grpc_slice* slice); explicit ManagedMemorySlice(const grpc_slice* slice);
bool Equals(const grpc_slice& other) const { bool operator==(const grpc_slice& other) const {
if (refcount == other.refcount) { if (refcount == other.refcount) {
return true; return true;
} }
return !grpc_slice_differs_refcounted(other, *this); return !grpc_slice_differs_refcounted(other, *this);
} }
bool Equals(const char* buf, const size_t len) const { bool operator!=(const grpc_slice& other) const { return !(*this == other); }
return data.refcounted.length == len && buf != nullptr && bool operator==(std::pair<const char*, size_t> buflen) const {
memcmp(buf, data.refcounted.bytes, len) == 0; return data.refcounted.length == buflen.second && buflen.first != nullptr &&
memcmp(buflen.first, data.refcounted.bytes, buflen.second) == 0;
} }
}; };
struct UnmanagedMemorySlice : public grpc_slice { struct UnmanagedMemorySlice : public grpc_slice {
@ -150,6 +157,16 @@ struct ExternallyManagedSlice : public UnmanagedMemorySlice {
data.refcounted.length = length; data.refcounted.length = length;
data.refcounted.bytes = bytes; data.refcounted.bytes = bytes;
} }
bool operator==(const grpc_slice& other) const {
return data.refcounted.length == GRPC_SLICE_LENGTH(other) &&
memcmp(data.refcounted.bytes, GRPC_SLICE_START_PTR(other),
data.refcounted.length) == 0;
}
bool operator!=(const grpc_slice& other) const { return !(*this == other); }
uint32_t Hash() {
return gpr_murmur_hash3(data.refcounted.bytes, data.refcounted.length,
g_hash_seed);
}
}; };
struct StaticMetadataSlice : public ManagedMemorySlice { struct StaticMetadataSlice : public ManagedMemorySlice {

@ -99,8 +99,8 @@ struct channel_registered_method {
registered_method* server_registered_method; registered_method* server_registered_method;
uint32_t flags; uint32_t flags;
bool has_host; bool has_host;
grpc_slice method; grpc_core::ExternallyManagedSlice method;
grpc_slice host; grpc_core::ExternallyManagedSlice host;
}; };
struct channel_data { struct channel_data {
@ -630,8 +630,8 @@ static void start_new_rpc(grpc_call_element* elem) {
chand->registered_method_slots]; chand->registered_method_slots];
if (rm->server_registered_method == nullptr) break; if (rm->server_registered_method == nullptr) break;
if (!rm->has_host) continue; if (!rm->has_host) continue;
if (!grpc_slice_eq(rm->host, calld->host)) continue; if (rm->host != calld->host) continue;
if (!grpc_slice_eq(rm->method, calld->path)) continue; if (rm->method != calld->path) continue;
if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) && if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) &&
0 == (calld->recv_initial_metadata_flags & 0 == (calld->recv_initial_metadata_flags &
GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) { GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) {
@ -648,7 +648,7 @@ static void start_new_rpc(grpc_call_element* elem) {
chand->registered_method_slots]; chand->registered_method_slots];
if (rm->server_registered_method == nullptr) break; if (rm->server_registered_method == nullptr) break;
if (rm->has_host) continue; if (rm->has_host) continue;
if (!grpc_slice_eq(rm->method, calld->path)) continue; if (rm->method != calld->path) continue;
if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) && if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) &&
0 == (calld->recv_initial_metadata_flags & 0 == (calld->recv_initial_metadata_flags &
GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) { GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) {
@ -921,8 +921,14 @@ static void server_destroy_channel_elem(grpc_channel_element* elem) {
if (chand->registered_methods) { if (chand->registered_methods) {
for (i = 0; i < chand->registered_method_slots; i++) { for (i = 0; i < chand->registered_method_slots; i++) {
grpc_slice_unref_internal(chand->registered_methods[i].method); grpc_slice_unref_internal(chand->registered_methods[i].method);
GPR_DEBUG_ASSERT(chand->registered_methods[i].method.refcount ==
&grpc_core::kNoopRefcount ||
chand->registered_methods[i].method.refcount == nullptr);
if (chand->registered_methods[i].has_host) { if (chand->registered_methods[i].has_host) {
grpc_slice_unref_internal(chand->registered_methods[i].host); grpc_slice_unref_internal(chand->registered_methods[i].host);
GPR_DEBUG_ASSERT(chand->registered_methods[i].host.refcount ==
&grpc_core::kNoopRefcount ||
chand->registered_methods[i].host.refcount == nullptr);
} }
} }
gpr_free(chand->registered_methods); gpr_free(chand->registered_methods);
@ -1202,18 +1208,13 @@ void grpc_server_setup_transport(
chand->registered_methods = chand->registered_methods =
static_cast<channel_registered_method*>(gpr_zalloc(alloc)); static_cast<channel_registered_method*>(gpr_zalloc(alloc));
for (rm = s->registered_methods; rm; rm = rm->next) { for (rm = s->registered_methods; rm; rm = rm->next) {
grpc_slice host; grpc_core::ExternallyManagedSlice host;
bool has_host; grpc_core::ExternallyManagedSlice method(rm->method);
grpc_slice method; const bool has_host = rm->host != nullptr;
if (rm->host != nullptr) { if (has_host) {
host = grpc_slice_from_static_string(rm->host); host = grpc_core::ExternallyManagedSlice(rm->host);
has_host = true;
} else {
has_host = false;
} }
method = grpc_slice_from_static_string(rm->method); hash = GRPC_MDSTR_KV_HASH(has_host ? host.Hash() : 0, method.Hash());
hash = GRPC_MDSTR_KV_HASH(has_host ? grpc_slice_hash_internal(host) : 0,
grpc_slice_hash_internal(method));
for (probes = 0; chand->registered_methods[(hash + probes) % slots] for (probes = 0; chand->registered_methods[(hash + probes) % slots]
.server_registered_method != nullptr; .server_registered_method != nullptr;
probes++) probes++)

Loading…
Cancel
Save