| /* |
| * Copyright 2015 Facebook, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // Functions to provide smarter use of jemalloc, if jemalloc is being used. |
| // http://www.canonware.com/download/jemalloc/jemalloc-latest/doc/jemalloc.html |
| |
| #ifndef FOLLY_MALLOC_H_ |
| #define FOLLY_MALLOC_H_ |
| |
| /** |
| * Define various MALLOCX_* macros normally provided by jemalloc. We define |
| * them so that we don't have to include jemalloc.h, in case the program is |
| * built without jemalloc support. |
| */ |
| #ifndef MALLOCX_LG_ALIGN |
| #define MALLOCX_LG_ALIGN(la) (la) |
| #endif |
| #ifndef MALLOCX_ZERO |
| #define MALLOCX_ZERO (static_cast<int>(0x40)) |
| #endif |
| |
| // If using fbstring from libstdc++, then just define stub code |
| // here to typedef the fbstring type into the folly namespace. |
| // This provides backwards compatibility for code that explicitly |
| // includes and uses fbstring. |
| #if defined(_GLIBCXX_USE_FB) && !defined(_LIBSTDCXX_FBSTRING) |
| |
| #include <folly/detail/Malloc.h> |
| |
| #include <string> |
| |
| namespace folly { |
| using std::goodMallocSize; |
| using std::jemallocMinInPlaceExpandable; |
| using std::usingJEMalloc; |
| using std::smartRealloc; |
| using std::checkedMalloc; |
| using std::checkedCalloc; |
| using std::checkedRealloc; |
| } |
| |
| #else // !defined(_GLIBCXX_USE_FB) || defined(_LIBSTDCXX_FBSTRING) |
| |
| #ifdef _LIBSTDCXX_FBSTRING |
| #pragma GCC system_header |
| |
| /** |
| * Declare *allocx() and mallctl*() as weak symbols. These will be provided by |
| * jemalloc if we are using jemalloc, or will be NULL if we are using another |
| * malloc implementation. |
| */ |
| extern "C" void* mallocx(size_t, int) |
| __attribute__((__weak__)); |
| extern "C" void* rallocx(void*, size_t, int) |
| __attribute__((__weak__)); |
| extern "C" size_t xallocx(void*, size_t, size_t, int) |
| __attribute__((__weak__)); |
| extern "C" size_t sallocx(const void*, int) |
| __attribute__((__weak__)); |
| extern "C" void dallocx(void*, int) |
| __attribute__((__weak__)); |
| extern "C" size_t nallocx(size_t, int) |
| __attribute__((__weak__)); |
| extern "C" int mallctl(const char*, void*, size_t*, void*, size_t) |
| __attribute__((__weak__)); |
| extern "C" int mallctlnametomib(const char*, size_t*, size_t*) |
| __attribute__((__weak__)); |
| extern "C" int mallctlbymib(const size_t*, size_t, void*, size_t*, void*, |
| size_t) |
| __attribute__((__weak__)); |
| |
| #include <bits/functexcept.h> |
| #define FOLLY_HAVE_MALLOC_H 1 |
| #else |
| #include <folly/detail/Malloc.h> /* nolint */ |
| #include <folly/Portability.h> |
| #endif |
| |
| // for malloc_usable_size |
| // NOTE: FreeBSD 9 doesn't have malloc.h. It's defitions |
| // are found in stdlib.h. |
| #if FOLLY_HAVE_MALLOC_H |
| #include <malloc.h> |
| #else |
| #include <stdlib.h> |
| #endif |
| |
| #include <cassert> |
| #include <cstddef> |
| #include <cstdlib> |
| #include <cstring> |
| |
| #include <new> |
| |
| #ifdef _LIBSTDCXX_FBSTRING |
| namespace std _GLIBCXX_VISIBILITY(default) { |
| _GLIBCXX_BEGIN_NAMESPACE_VERSION |
| #else |
| namespace folly { |
| #endif |
| |
| bool usingJEMallocSlow(); |
| |
| /** |
| * Determine if we are using jemalloc or not. |
| */ |
| inline bool usingJEMalloc() { |
| // Checking for rallocx != NULL is not sufficient; we may be in a dlopen()ed |
| // module that depends on libjemalloc, so rallocx is resolved, but the main |
| // program might be using a different memory allocator. Look at the |
| // implementation of usingJEMallocSlow() for the (hacky) details. |
| static const bool result = usingJEMallocSlow(); |
| return result; |
| } |
| |
| /** |
| * For jemalloc's size classes, see |
| * http://www.canonware.com/download/jemalloc/jemalloc-latest/doc/jemalloc.html |
| */ |
| inline size_t goodMallocSize(size_t minSize) noexcept { |
| if (!usingJEMalloc()) { |
| // Not using jemalloc - no smarts |
| return minSize; |
| } |
| size_t goodSize; |
| if (minSize <= 64) { |
| // Choose smallest allocation to be 64 bytes - no tripping over |
| // cache line boundaries, and small string optimization takes care |
| // of short strings anyway. |
| goodSize = 64; |
| } else if (minSize <= 512) { |
| // Round up to the next multiple of 64; we don't want to trip over |
| // cache line boundaries. |
| goodSize = (minSize + 63) & ~size_t(63); |
| } else { |
| // Boundaries between size classes depend on numerious factors, some of |
| // which can even be modified at run-time. Determine the good allocation |
| // size by calling nallocx() directly. |
| goodSize = nallocx(minSize, 0); |
| } |
| assert(nallocx(goodSize, 0) == goodSize); |
| return goodSize; |
| } |
| |
| // We always request "good" sizes for allocation, so jemalloc can |
| // never grow in place small blocks; they're already occupied to the |
| // brim. Blocks larger than or equal to 4096 bytes can in fact be |
| // expanded in place, and this constant reflects that. |
| static const size_t jemallocMinInPlaceExpandable = 4096; |
| |
| /** |
| * Trivial wrappers around malloc, calloc, realloc that check for allocation |
| * failure and throw std::bad_alloc in that case. |
| */ |
| inline void* checkedMalloc(size_t size) { |
| void* p = malloc(size); |
| if (!p) std::__throw_bad_alloc(); |
| return p; |
| } |
| |
| inline void* checkedCalloc(size_t n, size_t size) { |
| void* p = calloc(n, size); |
| if (!p) std::__throw_bad_alloc(); |
| return p; |
| } |
| |
| inline void* checkedRealloc(void* ptr, size_t size) { |
| void* p = realloc(ptr, size); |
| if (!p) std::__throw_bad_alloc(); |
| return p; |
| } |
| |
| /** |
| * This function tries to reallocate a buffer of which only the first |
| * currentSize bytes are used. The problem with using realloc is that |
| * if currentSize is relatively small _and_ if realloc decides it |
| * needs to move the memory chunk to a new buffer, then realloc ends |
| * up copying data that is not used. It's impossible to hook into |
| * GNU's malloc to figure whether expansion will occur in-place or as |
| * a malloc-copy-free troika. (If an expand_in_place primitive would |
| * be available, smartRealloc would use it.) As things stand, this |
| * routine just tries to call realloc() (thus benefitting of potential |
| * copy-free coalescing) unless there's too much slack memory. |
| */ |
| inline void* smartRealloc(void* p, |
| const size_t currentSize, |
| const size_t currentCapacity, |
| const size_t newCapacity) { |
| assert(p); |
| assert(currentSize <= currentCapacity && |
| currentCapacity < newCapacity); |
| |
| if (usingJEMalloc()) { |
| // using jemalloc's API. Don't forget that jemalloc can never grow |
| // in place blocks smaller than 4096 bytes. |
| // |
| // NB: newCapacity may not be precisely equal to a jemalloc size class, |
| // i.e. newCapacity is not guaranteed to be the result of a |
| // goodMallocSize() call, therefore xallocx() may return more than |
| // newCapacity bytes of space. Use >= rather than == to check whether |
| // xallocx() successfully expanded in place. |
| if (currentCapacity >= jemallocMinInPlaceExpandable && |
| xallocx(p, newCapacity, 0, 0) >= newCapacity) { |
| // Managed to expand in place |
| return p; |
| } |
| // Cannot expand; must move |
| auto const result = checkedMalloc(newCapacity); |
| std::memcpy(result, p, currentSize); |
| free(p); |
| return result; |
| } |
| |
| // No jemalloc no honey |
| auto const slack = currentCapacity - currentSize; |
| if (slack * 2 > currentSize) { |
| // Too much slack, malloc-copy-free cycle: |
| auto const result = checkedMalloc(newCapacity); |
| std::memcpy(result, p, currentSize); |
| free(p); |
| return result; |
| } |
| // If there's not too much slack, we realloc in hope of coalescing |
| return checkedRealloc(p, newCapacity); |
| } |
| |
| #ifdef _LIBSTDCXX_FBSTRING |
| _GLIBCXX_END_NAMESPACE_VERSION |
| #endif |
| |
| } // folly |
| |
| #endif // !defined(_GLIBCXX_USE_FB) || defined(_LIBSTDCXX_FBSTRING) |
| |
| #endif // FOLLY_MALLOC_H_ |