From a1cb64b1b422d2a92d414963c2554bf594777ba3 Mon Sep 17 00:00:00 2001 From: WeijieSun Date: Mon, 23 Apr 2018 16:22:35 +0800 Subject: [PATCH] utility: refactor tls memory allocator (#25) remove c interface of tls memory allocator, and move related code to utility module --- include/dsn/c/api_utilities.h | 16 --- include/dsn/cpp/callocator.h | 99 ------------------- include/dsn/cpp/task_helper.h | 2 +- include/dsn/tool-api/rpc_message.h | 3 +- include/dsn/tool-api/task.h | 6 +- .../dsn/utility/callocator.h | 49 ++++----- include/dsn/utility/transient_memory.h | 85 ++++++++++++++++ src/core/core/disk_engine.cpp | 2 +- src/core/core/rpc_message.cpp | 6 +- src/core/core/service_api_c.cpp | 2 +- src/core/core/transient_memory.cpp | 37 ++++--- src/core/tests/rpc_message.cpp | 8 +- src/core/tests/transient_memory.cpp | 15 +-- src/core/tools/common/http_message_parser.cpp | 4 +- .../tools/common/thrift_message_parser.cpp | 10 +- 15 files changed, 156 insertions(+), 188 deletions(-) delete mode 100644 include/dsn/cpp/callocator.h rename src/core/core/transient_memory.h => include/dsn/utility/callocator.h (56%) create mode 100644 include/dsn/utility/transient_memory.h diff --git a/include/dsn/c/api_utilities.h b/include/dsn/c/api_utilities.h index 2d3cfad1a2..e5727eb654 100644 --- a/include/dsn/c/api_utilities.h +++ b/include/dsn/c/api_utilities.h @@ -185,20 +185,4 @@ extern DSN_API void dsn_coredump(); return; \ } \ } while (0) - -/*! -@defgroup memory Memory Management -@ingroup service-api-utilities - -Memory Management - -@{ -*/ - -/*! high-performance malloc for transient objects, i.e., their life-time is short */ -extern DSN_API void *dsn_transient_malloc(uint32_t size); - -/*! high-performance free for transient objects, paired with \ref dsn_transient_malloc */ -extern DSN_API void dsn_transient_free(void *ptr); - /*@}*/ diff --git a/include/dsn/cpp/callocator.h b/include/dsn/cpp/callocator.h deleted file mode 100644 index b02e3d63cd..0000000000 --- a/include/dsn/cpp/callocator.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Microsoft Corporation - * - * -=- Robust Distributed System Nucleus (rDSN) -=- - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * Description: - * What is this file about? - * - * Revision history: - * xxxx-xx-xx, author, first version - * xxxx-xx-xx, author, fix bug about xxx - */ - -#pragma once - -#include - -namespace dsn { - -typedef void *(*t_allocate)(uint32_t); -typedef void (*t_deallocate)(void *); - -template -class callocator_object -{ -public: - void *operator new(size_t size) { return a(uint32_t(size)); } - - void operator delete(void *p) { d(p); } - - void *operator new[](size_t size) { return a((uint32_t)size); } - - void operator delete[](void *p) { d(p); } -}; - -typedef callocator_object transient_object; - -template -class callocator -{ -public: - typedef T value_type; - typedef size_t size_type; - typedef const T *const_pointer; - - template - struct rebind - { - typedef callocator<_Tp1, a, d> other; - }; - - static T *allocate(size_type n) { return static_cast(a(uint32_t(n * sizeof(T)))); } - - static void deallocate(T *p, size_type n) { d(p); } - - callocator() throw() {} - template - callocator(const callocator &ac) throw() - { - } - ~callocator() throw() {} -}; - -template -using transient_allocator = callocator; - -template -bool operator==(const transient_allocator &, const transient_allocator &) -{ - return true; -} -template -bool operator!=(const transient_allocator &, const transient_allocator &) -{ - return false; -} -} diff --git a/include/dsn/cpp/task_helper.h b/include/dsn/cpp/task_helper.h index 90f60df779..58b6d0fed8 100644 --- a/include/dsn/cpp/task_helper.h +++ b/include/dsn/cpp/task_helper.h @@ -40,7 +40,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/dsn/tool-api/rpc_message.h b/include/dsn/tool-api/rpc_message.h index 7996339c5a..bedceb89c0 100644 --- a/include/dsn/tool-api/rpc_message.h +++ b/include/dsn/tool-api/rpc_message.h @@ -41,7 +41,8 @@ #include #include #include -#include +#include + #include #include #include diff --git a/include/dsn/tool-api/task.h b/include/dsn/tool-api/task.h index c4d849fb45..2373ec055e 100644 --- a/include/dsn/tool-api/task.h +++ b/include/dsn/tool-api/task.h @@ -38,13 +38,13 @@ #include #include +#include +#include +#include #include #include #include -#include #include -#include -#include namespace dsn { diff --git a/src/core/core/transient_memory.h b/include/dsn/utility/callocator.h similarity index 56% rename from src/core/core/transient_memory.h rename to include/dsn/utility/callocator.h index 69393e2f51..5ae3b0e73c 100644 --- a/src/core/core/transient_memory.h +++ b/include/dsn/utility/callocator.h @@ -24,42 +24,33 @@ * THE SOFTWARE. */ -/* - * Description: - * What is this file about? - * - * Revision history: - * xxxx-xx-xx, author, first version - * xxxx-xx-xx, author, fix bug about xxx - */ - #pragma once -#include -#include -#include -#include +#include namespace dsn { -typedef struct tls_transient_memory_t + +typedef void *(*t_allocate)(size_t); +typedef void (*t_deallocate)(void *); + +/// callocate_object overrides the operator "new" and "delete", which may be useful for objects +/// who want to use custom new/delete to boost performance +template +class callocator_object { - unsigned int magic; - size_t remain_bytes; - char block_ptr_buffer[sizeof(std::shared_ptr)]; - std::shared_ptr *block; - char *next; - bool committed; -} tls_transient_memory_t; +public: + void *operator new(size_t size) { return a(size); } -extern __thread tls_transient_memory_t tls_trans_memory; -extern void tls_trans_mem_init(size_t default_per_block_bytes); -extern void tls_trans_mem_alloc(size_t min_size); + void operator delete(void *p) { d(p); } -extern void tls_trans_mem_next(void **ptr, size_t *sz, size_t min_size); -extern void tls_trans_mem_commit(size_t use_size); + void *operator new[](size_t size) { return a(size); } -extern blob tls_trans_mem_alloc_blob(size_t sz); + void operator delete[](void *p) { d(p); } +}; -extern void *tls_trans_malloc(size_t sz); -extern void tls_trans_free(void *ptr); +/// transient_object uses tls_trans_malloc/tls_trans_free as custom memory allocate. +/// in rdsn, serveral frequenctly allocated objects(task, rpc_message, etc.) +/// are derived from transient_objects, +/// so that their memory can be mamanged by trans_memory_allocator +typedef callocator_object transient_object; } diff --git a/include/dsn/utility/transient_memory.h b/include/dsn/utility/transient_memory.h new file mode 100644 index 0000000000..686c6964e7 --- /dev/null +++ b/include/dsn/utility/transient_memory.h @@ -0,0 +1,85 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Microsoft Corporation + * + * -=- Robust Distributed System Nucleus (rDSN) -=- + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace dsn { + +/// transient memory is mainly used for allocating & freeing memory in block, +/// so as to avoid the memory fragmentation problems when server is running for a long time +/// +/// the key design is allocating a block with "malloc" each time, and keep it in +/// the structure "tls_trans_memory". memory allocation of other modules should try to +/// get memory from the pre-allocated block in "tls_trans_memory": +/// +/// |--------------- pre-allocated block --------------------| <- tls_trans_memory.block +/// |--memory piece 1--|--memory piece 2--|--memory piece 3--| +/// +/// the tls_trans_memory->block is a shared_ptr pointing to the pre-allocated block, +/// for each memory piece allocated from the block, there is also a shared-ptr pointing the whole +/// block. please refer to @tls_trans_mem_next for details +/// +/// so the pre-allocated block will be free until all the shared-ptrs are destructed. +/// tls_trans_memory.block will release an old memory_block if the remaining size is +/// too small, and other shared-ptr will release the block when "tls_trans_free" calls + +typedef struct tls_transient_memory_t +{ + unsigned int magic; + size_t remain_bytes; + char block_ptr_buffer[sizeof(std::shared_ptr)]; + std::shared_ptr *block; + char *next; + bool committed; +} tls_transient_memory_t; + +extern __thread tls_transient_memory_t tls_trans_memory; + +// initialize the default block size, should call this at the beginning of the process +void tls_trans_mem_init(size_t default_per_block_bytes); + +// acquire a memory piece from current block +// "min_size" is the minimal size of the memory piece user needs, +// "*sz" stores the actual size the allocator gives, which is no less than min_size +// *ptr stores the starting position of the memory piece +// both ptr & sz shouldn't be null +// +// "tls_trans_mem_next" should be used together with "tls_trans_mem_commit" +void tls_trans_mem_next(void **ptr, size_t *sz, size_t min_size); +void tls_trans_mem_commit(size_t use_size); + +// allocate a blob, the size is "sz" +blob tls_trans_mem_alloc_blob(size_t sz); + +// allocate memory +void *tls_trans_malloc(size_t sz); + +// free memory, ptr shouldn't be null +void tls_trans_free(void *ptr); +} diff --git a/src/core/core/disk_engine.cpp b/src/core/core/disk_engine.cpp index 73a5c01df7..404a9cfd7b 100644 --- a/src/core/core/disk_engine.cpp +++ b/src/core/core/disk_engine.cpp @@ -37,7 +37,7 @@ #include #include #include -#include "transient_memory.h" +#include using namespace dsn::utils; diff --git a/src/core/core/rpc_message.cpp b/src/core/core/rpc_message.cpp index 565c827d07..91768efeed 100644 --- a/src/core/core/rpc_message.cpp +++ b/src/core/core/rpc_message.cpp @@ -35,13 +35,13 @@ #include #include +#include #include #include #include #include #include "task_engine.h" -#include "transient_memory.h" using namespace dsn::utils; @@ -287,8 +287,8 @@ message_ex *message_ex::create_receive_message_with_standalone_header(const blob { message_ex *msg = new message_ex(); std::shared_ptr header_holder( - static_cast(dsn_transient_malloc(sizeof(message_header))), - [](char *c) { dsn_transient_free(c); }); + static_cast(dsn::tls_trans_malloc(sizeof(message_header))), + [](char *c) { dsn::tls_trans_free(c); }); msg->header = reinterpret_cast(header_holder.get()); memset(msg->header, 0, sizeof(message_header)); msg->buffers.emplace_back(blob(std::move(header_holder), sizeof(message_header))); diff --git a/src/core/core/service_api_c.cpp b/src/core/core/service_api_c.cpp index 29b623f1fa..adf1516c0f 100644 --- a/src/core/core/service_api_c.cpp +++ b/src/core/core/service_api_c.cpp @@ -49,13 +49,13 @@ #include #include +#include #include #include "service_engine.h" #include "rpc_engine.h" #include "disk_engine.h" #include "task_engine.h" #include "coredump.h" -#include "transient_memory.h" #include #ifndef _WIN32 diff --git a/src/core/core/transient_memory.cpp b/src/core/core/transient_memory.cpp index 3beb16f245..fb969d993c 100644 --- a/src/core/core/transient_memory.cpp +++ b/src/core/core/transient_memory.cpp @@ -33,18 +33,18 @@ * xxxx-xx-xx, author, fix bug about xxx */ -#include "transient_memory.h" +#include +#include +#include +#include namespace dsn { + __thread tls_transient_memory_t tls_trans_memory; static size_t tls_trans_mem_default_block_bytes = 1024 * 1024; // 1 MB -void tls_trans_mem_init(size_t default_per_block_bytes) -{ - tls_trans_mem_default_block_bytes = default_per_block_bytes; -} - +// allocate a block from the system, the block size should be at lease "min_size" void tls_trans_mem_alloc(size_t min_size) { // release last buffer if necessary @@ -63,13 +63,21 @@ void tls_trans_mem_alloc(size_t min_size) tls_trans_memory.next = tls_trans_memory.block->get(); } +/// +/// api of this moudle start from here +/// +void tls_trans_mem_init(size_t default_per_block_bytes) +{ + tls_trans_mem_default_block_bytes = default_per_block_bytes; +} + void tls_trans_mem_next(void **ptr, size_t *sz, size_t min_size) { if (tls_trans_memory.magic != 0xdeadbeef) tls_trans_mem_alloc(min_size); else { - dassert(tls_trans_memory.committed == true, - "tls_trans_mem_next and tls_trans_mem_commit must be called in pair"); + // tls_trans_mem_next and tls_trans_mem_commit must be called in pair + assert(tls_trans_memory.committed == true); if (min_size > tls_trans_memory.remain_bytes) tls_trans_mem_alloc(min_size); @@ -82,9 +90,9 @@ void tls_trans_mem_next(void **ptr, size_t *sz, size_t min_size) void tls_trans_mem_commit(size_t use_size) { - dbg_dassert(tls_trans_memory.magic == 0xdeadbeef && !tls_trans_memory.committed && - use_size <= tls_trans_memory.remain_bytes, - "invalid use or parameter of tls_trans_mem_commit"); + // invalid use or parameter of tls_trans_mem_commit + assert(tls_trans_memory.magic == 0xdeadbeef && !tls_trans_memory.committed && + use_size <= tls_trans_memory.remain_bytes); tls_trans_memory.next += use_size; tls_trans_memory.remain_bytes -= use_size; @@ -126,13 +134,10 @@ void *tls_trans_malloc(size_t sz) void tls_trans_free(void *ptr) { ptr = (void *)((char *)ptr - sizeof(uint32_t)); - dassert(*(uint32_t *)(ptr) == 0xdeadbeef, "invalid transient memory block"); + // invalid transient memory block + assert(*(uint32_t *)(ptr) == 0xdeadbeef); ptr = (void *)((char *)ptr - sizeof(std::shared_ptr)); ((std::shared_ptr *)(ptr))->~shared_ptr(); } } - -DSN_API void *dsn_transient_malloc(uint32_t size) { return ::dsn::tls_trans_malloc((size_t)size); } - -DSN_API void dsn_transient_free(void *ptr) { return ::dsn::tls_trans_free(ptr); } \ No newline at end of file diff --git a/src/core/tests/rpc_message.cpp b/src/core/tests/rpc_message.cpp index 58de173892..aa42e605af 100644 --- a/src/core/tests/rpc_message.cpp +++ b/src/core/tests/rpc_message.cpp @@ -33,15 +33,19 @@ * xxxx-xx-xx, author, fix bug about xxx */ -#include #include -#include <../core/transient_memory.h> +#include +#include #include using namespace ::dsn; DEFINE_TASK_CODE_RPC(RPC_CODE_FOR_TEST, TASK_PRIORITY_COMMON, ::dsn::THREAD_POOL_DEFAULT) +namespace dsn { +extern void tls_trans_mem_alloc(size_t min_size); +} + TEST(core, message_ex) { dsn_msg_context_t ctx0, ctx1; diff --git a/src/core/tests/transient_memory.cpp b/src/core/tests/transient_memory.cpp index aad522676d..008be1436a 100644 --- a/src/core/tests/transient_memory.cpp +++ b/src/core/tests/transient_memory.cpp @@ -24,20 +24,15 @@ * THE SOFTWARE. */ -/* - * Description: - * Unit-test for transient memory. - * - * Revision history: - * Nov., 2015, @qinzuoyan (Zuoyan Qin), first version - * xxxx-xx-xx, author, fix bug about xxx - */ - -#include "../core/transient_memory.h" +#include #include using namespace ::dsn; +namespace dsn { +extern void tls_trans_mem_alloc(size_t min_size); +} + TEST(core, transient_memory) { tls_trans_mem_init(1024); diff --git a/src/core/tools/common/http_message_parser.cpp b/src/core/tools/common/http_message_parser.cpp index 78da866c20..179201f643 100644 --- a/src/core/tools/common/http_message_parser.cpp +++ b/src/core/tools/common/http_message_parser.cpp @@ -386,8 +386,8 @@ void http_message_parser::prepare_on_send(message_ex *msg) header_str = ss.str(); } unsigned int header_len = header_str.size(); - std::shared_ptr header_holder(static_cast(dsn_transient_malloc(header_len)), - [](char *c) { dsn_transient_free(c); }); + std::shared_ptr header_holder(static_cast(dsn::tls_trans_malloc(header_len)), + [](char *c) { dsn::tls_trans_free(c); }); memcpy(header_holder.get(), header_str.data(), header_len); unsigned int dsn_size = sizeof(message_header) + header->body_length; diff --git a/src/core/tools/common/thrift_message_parser.cpp b/src/core/tools/common/thrift_message_parser.cpp index dce0875209..f5c6716e85 100644 --- a/src/core/tools/common/thrift_message_parser.cpp +++ b/src/core/tools/common/thrift_message_parser.cpp @@ -79,13 +79,15 @@ message_ex *thrift_message_parser::get_message_on_receive(message_reader *reader : sizeof(thrift_message_header) - reader->_buffer_occupied); msg->hdr_format = NET_HDR_THRIFT; return msg; - } else // buf_len < msg_sz - { + } + // buf_len < msg_sz + else { read_next = msg_sz - buf_len; return nullptr; } - } else // buf_len < sizeof(thrift_message_header) - { + } + // buf_len < sizeof(thrift_message_header) + else { read_next = sizeof(thrift_message_header) - buf_len; return nullptr; }