Coalesce arena create/first alloc for grpc_call.
diff --git a/src/core/lib/gprpp/arena.cc b/src/core/lib/gprpp/arena.cc
index ab3a486..5c344db 100644
--- a/src/core/lib/gprpp/arena.cc
+++ b/src/core/lib/gprpp/arena.cc
@@ -31,11 +31,23 @@
#include "src/core/lib/gpr/alloc.h"
#include "src/core/lib/gprpp/memory.h"
-template <size_t alignment>
-static void* gpr_arena_malloc(size_t size) {
- return gpr_malloc_aligned(size, alignment);
+namespace {
+
+void* ArenaStorage(size_t initial_size) {
+ static constexpr size_t base_size =
+ GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_core::Arena));
+ initial_size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(initial_size);
+ size_t alloc_size = base_size + initial_size;
+ static constexpr size_t alignment =
+ (GPR_CACHELINE_SIZE > GPR_MAX_ALIGNMENT &&
+ GPR_CACHELINE_SIZE % GPR_MAX_ALIGNMENT == 0)
+ ? GPR_CACHELINE_SIZE
+ : GPR_MAX_ALIGNMENT;
+ return gpr_malloc_aligned(alloc_size, alignment);
}
+} // namespace
+
namespace grpc_core {
Arena::~Arena() {
@@ -49,16 +61,17 @@
}
Arena* Arena::Create(size_t initial_size) {
+ return new (ArenaStorage(initial_size)) Arena(initial_size);
+}
+
+Pair<Arena*, void*> Arena::CreateWithAlloc(size_t initial_size,
+ size_t alloc_size) {
static constexpr size_t base_size =
GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(Arena));
- initial_size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(initial_size);
- size_t alloc_size = base_size + initial_size;
- static constexpr size_t alignment =
- (GPR_CACHELINE_SIZE > GPR_MAX_ALIGNMENT &&
- GPR_CACHELINE_SIZE % GPR_MAX_ALIGNMENT == 0)
- ? GPR_CACHELINE_SIZE
- : GPR_MAX_ALIGNMENT;
- return new (gpr_arena_malloc<alignment>(alloc_size)) Arena(initial_size);
+ auto* new_arena =
+ new (ArenaStorage(initial_size)) Arena(initial_size, alloc_size);
+ void* first_alloc = reinterpret_cast<char*>(new_arena) + base_size;
+ return MakePair(new_arena, first_alloc);
}
size_t Arena::Destroy() {
@@ -77,7 +90,7 @@
static constexpr size_t zone_base_size =
GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(Zone));
size_t alloc_size = zone_base_size + size;
- Zone* z = new (gpr_arena_malloc<GPR_MAX_ALIGNMENT>(alloc_size)) Zone();
+ Zone* z = new (gpr_malloc_aligned(alloc_size, GPR_MAX_ALIGNMENT)) Zone();
{
gpr_spinlock_lock(&arena_growth_spinlock_);
z->prev = last_zone_;
diff --git a/src/core/lib/gprpp/arena.h b/src/core/lib/gprpp/arena.h
index 749f4e4..b1b0c4a 100644
--- a/src/core/lib/gprpp/arena.h
+++ b/src/core/lib/gprpp/arena.h
@@ -36,6 +36,7 @@
#include "src/core/lib/gpr/alloc.h"
#include "src/core/lib/gpr/spinlock.h"
#include "src/core/lib/gprpp/atomic.h"
+#include "src/core/lib/gprpp/pair.h"
#include <stddef.h>
@@ -45,6 +46,13 @@
public:
// Create an arena, with \a initial_size bytes in the first allocated buffer.
static Arena* Create(size_t initial_size);
+
+ // Create an arena, with \a initial_size bytes in the first allocated buffer,
+ // and return both a void pointer to the returned arena and a void* with the
+ // first allocation.
+ static Pair<Arena*, void*> CreateWithAlloc(size_t initial_size,
+ size_t alloc_size);
+
// Destroy an arena, returning the total number of bytes allocated.
size_t Destroy();
// Allocate \a size bytes from the arena.
@@ -76,7 +84,21 @@
Zone* prev;
};
- explicit Arena(size_t initial_size) : initial_zone_size_(initial_size) {}
+ // Initialize an arena.
+ // Parameters:
+ // initial_size: The initial size of the whole arena in bytes. These bytes
+ // are contained within 'zone 0'. If the arena user ends up requiring more
+ // memory than the arena contains in zone 0, subsequent zones are allocated
+ // on demand and maintained in a tail-linked list.
+ //
+ // initial_alloc: Optionally, construct the arena as though a call to
+ // Alloc() had already been made for initial_alloc bytes. This provides a
+ // quick optimization (avoiding an atomic fetch-add) for the common case
+ // where we wish to create an arena and then perform an immediate
+ // allocation.
+ explicit Arena(size_t initial_size, size_t initial_alloc = 0)
+ : total_used_(initial_alloc), initial_zone_size_(initial_size) {}
+
~Arena();
void* AllocZone(size_t size);
diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc
index 54fdacd..bd14002 100644
--- a/src/core/lib/surface/call.cc
+++ b/src/core/lib/surface/call.cc
@@ -321,16 +321,23 @@
GRPC_CHANNEL_INTERNAL_REF(args->channel, "call");
+ grpc_core::Arena* arena;
+ grpc_call* call;
grpc_error* error = GRPC_ERROR_NONE;
grpc_channel_stack* channel_stack =
grpc_channel_get_channel_stack(args->channel);
- grpc_call* call;
size_t initial_size = grpc_channel_get_call_size_estimate(args->channel);
GRPC_STATS_INC_CALL_INITIAL_SIZE(initial_size);
- grpc_core::Arena* arena = grpc_core::Arena::Create(initial_size);
- call = new (arena->Alloc(GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)) +
- channel_stack->call_stack_size))
- grpc_call(arena, *args);
+ size_t call_and_stack_size =
+ GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)) +
+ channel_stack->call_stack_size;
+ size_t call_alloc_size =
+ call_and_stack_size + (args->parent ? sizeof(child_call) : 0);
+
+ std::pair<grpc_core::Arena*, void*> arena_with_call =
+ grpc_core::Arena::CreateWithAlloc(initial_size, call_alloc_size);
+ arena = arena_with_call.first;
+ call = new (arena_with_call.second) grpc_call(arena, *args);
*out_call = call;
grpc_slice path = grpc_empty_slice();
if (call->is_client) {
@@ -362,7 +369,8 @@
bool immediately_cancel = false;
if (args->parent != nullptr) {
- call->child = arena->New<child_call>(args->parent);
+ call->child = new (reinterpret_cast<char*>(arena_with_call.second) +
+ call_and_stack_size) child_call(args->parent);
GRPC_CALL_INTERNAL_REF(args->parent, "child");
GPR_ASSERT(call->is_client);