2017-05-26 04:48:44 +08:00
|
|
|
/*
|
|
|
|
* FastAlloc.h
|
|
|
|
*
|
|
|
|
* This source file is part of the FoundationDB open source project
|
|
|
|
*
|
|
|
|
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
2018-02-22 02:25:11 +08:00
|
|
|
*
|
2017-05-26 04:48:44 +08:00
|
|
|
* 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
|
2018-02-22 02:25:11 +08:00
|
|
|
*
|
2017-05-26 04:48:44 +08:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2018-02-22 02:25:11 +08:00
|
|
|
*
|
2017-05-26 04:48:44 +08:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef FLOW_FASTALLOC_H
|
|
|
|
#define FLOW_FASTALLOC_H
|
|
|
|
#pragma once
|
|
|
|
|
2018-10-20 01:30:13 +08:00
|
|
|
#include "flow/Error.h"
|
|
|
|
#include "flow/Platform.h"
|
2020-11-26 06:06:59 +08:00
|
|
|
#include "flow/config.h"
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
// ALLOC_INSTRUMENTATION_STDOUT enables non-sampled logging of all allocations and deallocations to stdout to be
|
|
|
|
// processed by tools/alloc_instrumentation.py
|
2017-05-26 04:48:44 +08:00
|
|
|
//#define ALLOC_INSTRUMENTATION_STDOUT ENABLED(NOT_IN_CLEAN)
|
|
|
|
|
|
|
|
//#define ALLOC_INSTRUMENTATION ENABLED(NOT_IN_CLEAN)
|
2021-03-11 02:06:03 +08:00
|
|
|
// The form "(1==1)" in this context is used to satisfy both clang and vc++ with a single syntax. Clang rejects "1" and
|
|
|
|
// vc++ rejects "true".
|
|
|
|
#define FASTALLOC_THREAD_SAFE (FLOW_THREAD_SAFE || (1 == 1))
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
#if VALGRIND
|
|
|
|
#include <drd.h>
|
|
|
|
#include <memcheck.h>
|
2020-10-22 23:58:58 +08:00
|
|
|
bool valgrindPrecise();
|
2017-05-26 04:48:44 +08:00
|
|
|
#endif
|
|
|
|
|
2018-10-20 01:30:13 +08:00
|
|
|
#include "flow/Hash3.h"
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2019-06-08 05:39:07 +08:00
|
|
|
#include <assert.h>
|
2019-07-17 06:22:19 +08:00
|
|
|
#include <atomic>
|
2017-05-26 04:48:44 +08:00
|
|
|
#include <vector>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <unordered_map>
|
|
|
|
|
|
|
|
#if defined(ALLOC_INSTRUMENTATION) && defined(__linux__)
|
|
|
|
#include <execinfo.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ALLOC_INSTRUMENTATION
|
|
|
|
#include <map>
|
|
|
|
#include <algorithm>
|
2018-10-20 01:30:13 +08:00
|
|
|
#include "flow/ThreadPrimitives.h"
|
2017-05-26 04:48:44 +08:00
|
|
|
struct AllocInstrInfo {
|
|
|
|
int64_t allocCount;
|
|
|
|
int64_t deallocCount;
|
|
|
|
int64_t maxAllocated;
|
2021-03-11 02:06:03 +08:00
|
|
|
inline void alloc(int64_t count = 1) {
|
2017-05-26 04:48:44 +08:00
|
|
|
allocCount += count;
|
2021-03-11 02:06:03 +08:00
|
|
|
maxAllocated = std::max(allocCount - deallocCount, maxAllocated);
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
2021-03-11 02:06:03 +08:00
|
|
|
inline void dealloc(int64_t count = 1) { deallocCount += count; }
|
2017-05-26 04:48:44 +08:00
|
|
|
};
|
|
|
|
extern std::map<const char*, AllocInstrInfo> allocInstr;
|
|
|
|
#define INSTRUMENT_ALLOCATE(name) (allocInstr[(name)].alloc())
|
|
|
|
#define INSTRUMENT_RELEASE(name) (allocInstr[(name)].dealloc())
|
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
// extern std::map<uint32_t, uint64_t> stackAllocations;
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
// maps from an address to the hash of the backtrace and the size of the alloction
|
|
|
|
extern std::unordered_map<int64_t, std::pair<uint32_t, size_t>> memSample;
|
|
|
|
|
|
|
|
struct BackTraceAccount {
|
|
|
|
double count;
|
|
|
|
size_t sampleCount;
|
|
|
|
size_t totalSize;
|
2021-03-11 02:06:03 +08:00
|
|
|
std::vector<void*>* backTrace;
|
2017-05-26 04:48:44 +08:00
|
|
|
};
|
|
|
|
// maps from a hash of a backtrace to a backtrace and the total size of data currently allocated from this stack
|
|
|
|
extern std::unordered_map<uint32_t, BackTraceAccount> backTraceLookup;
|
|
|
|
|
|
|
|
extern ThreadSpinLock memLock;
|
|
|
|
extern thread_local bool memSample_entered;
|
|
|
|
extern const size_t SAMPLE_BYTES;
|
|
|
|
|
|
|
|
#else
|
|
|
|
#define INSTRUMENT_ALLOCATE(name)
|
|
|
|
#define INSTRUMENT_RELEASE(name)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(ALLOC_INSTRUMENTATION) || defined(ALLOC_INSTRUMENTATION_STDOUT)
|
2021-03-11 02:06:03 +08:00
|
|
|
void recordAllocation(void* ptr, size_t size);
|
|
|
|
void recordDeallocation(void* ptr);
|
2017-05-26 04:48:44 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
template <int Size>
|
|
|
|
class FastAllocator {
|
|
|
|
public:
|
2019-08-10 03:55:21 +08:00
|
|
|
[[nodiscard]] static void* allocate();
|
2017-05-26 04:48:44 +08:00
|
|
|
static void release(void* ptr);
|
2021-03-11 02:06:03 +08:00
|
|
|
static void check(void* ptr, bool alloc);
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2019-01-09 06:36:01 +08:00
|
|
|
static long long getTotalMemory();
|
|
|
|
static long long getApproximateMemoryUnused();
|
|
|
|
static long long getActiveThreads();
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
static void releaseThreadMagazines();
|
|
|
|
|
|
|
|
#ifdef ALLOC_INSTRUMENTATION
|
|
|
|
static volatile int32_t pageCount;
|
|
|
|
#endif
|
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
FastAllocator() = delete;
|
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
private:
|
|
|
|
#ifdef VALGRIND
|
|
|
|
static unsigned long vLock;
|
|
|
|
#endif
|
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
static const int magazine_size = (128 << 10) / Size;
|
2017-05-26 04:48:44 +08:00
|
|
|
static const int PSize = Size / sizeof(void*);
|
|
|
|
struct GlobalData;
|
|
|
|
struct ThreadData {
|
|
|
|
void* freelist;
|
2021-03-11 02:06:03 +08:00
|
|
|
int count; // there are count items on freelist
|
|
|
|
void* alternate; // alternate is either a full magazine, or an empty one
|
2017-05-26 04:48:44 +08:00
|
|
|
};
|
|
|
|
static thread_local ThreadData threadData;
|
2019-01-09 06:36:01 +08:00
|
|
|
static thread_local bool threadInitialized;
|
2020-07-16 07:24:38 +08:00
|
|
|
static GlobalData* globalData() noexcept {
|
2017-05-26 04:48:44 +08:00
|
|
|
#ifdef VALGRIND
|
|
|
|
ANNOTATE_RWLOCK_ACQUIRED(vLock, 1);
|
|
|
|
#endif
|
2021-03-11 02:06:03 +08:00
|
|
|
static GlobalData* data = new GlobalData(); // This is thread-safe as of c++11 (VS 2015, gcc 4.8, clang 3.3)
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
#ifdef VALGRIND
|
|
|
|
ANNOTATE_RWLOCK_RELEASED(vLock, 1);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
static void* freelist;
|
|
|
|
|
2019-01-09 06:36:01 +08:00
|
|
|
static void initThread();
|
2020-02-05 02:26:18 +08:00
|
|
|
static void getMagazine();
|
2017-05-26 04:48:44 +08:00
|
|
|
static void releaseMagazine(void*);
|
|
|
|
};
|
|
|
|
|
2019-07-17 06:22:19 +08:00
|
|
|
extern std::atomic<int64_t> g_hugeArenaMemory;
|
2019-03-31 04:36:13 +08:00
|
|
|
void hugeArenaSample(int size);
|
2017-05-26 04:48:44 +08:00
|
|
|
void releaseAllThreadMagazines();
|
2018-06-21 05:33:59 +08:00
|
|
|
int64_t getTotalUnusedAllocatedMemory();
|
2021-03-11 02:06:03 +08:00
|
|
|
void setFastAllocatorThreadInitFunction(
|
|
|
|
void (*)()); // The given function will be called at least once in each thread that allocates from a FastAllocator.
|
|
|
|
// Currently just one such function is tracked.
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2019-06-08 05:39:07 +08:00
|
|
|
inline constexpr int nextFastAllocatedSize(int x) {
|
|
|
|
assert(x > 0 && x <= 8192);
|
|
|
|
if (x <= 16)
|
|
|
|
return 16;
|
|
|
|
else if (x <= 32)
|
|
|
|
return 32;
|
|
|
|
else if (x <= 64)
|
|
|
|
return 64;
|
|
|
|
else if (x <= 96)
|
|
|
|
return 96;
|
|
|
|
else if (x <= 128)
|
|
|
|
return 128;
|
|
|
|
else if (x <= 256)
|
|
|
|
return 256;
|
|
|
|
else if (x <= 512)
|
|
|
|
return 512;
|
|
|
|
else if (x <= 1024)
|
|
|
|
return 1024;
|
|
|
|
else if (x <= 2048)
|
|
|
|
return 2048;
|
|
|
|
else if (x <= 4096)
|
|
|
|
return 4096;
|
|
|
|
else
|
|
|
|
return 8192;
|
|
|
|
}
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
template <class Object>
|
|
|
|
class FastAllocated {
|
|
|
|
public:
|
2019-08-10 03:55:21 +08:00
|
|
|
[[nodiscard]] static void* operator new(size_t s) {
|
2021-03-11 02:06:03 +08:00
|
|
|
if (s != sizeof(Object))
|
|
|
|
abort();
|
2017-05-26 04:48:44 +08:00
|
|
|
INSTRUMENT_ALLOCATE(typeid(Object).name());
|
2019-06-08 05:39:07 +08:00
|
|
|
void* p = FastAllocator < sizeof(Object) <= 64 ? 64 : nextFastAllocatedSize(sizeof(Object)) > ::allocate();
|
2017-05-26 04:48:44 +08:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void operator delete(void* s) {
|
|
|
|
INSTRUMENT_RELEASE(typeid(Object).name());
|
2019-06-08 05:39:07 +08:00
|
|
|
FastAllocator<sizeof(Object) <= 64 ? 64 : nextFastAllocatedSize(sizeof(Object))>::release(s);
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
// Redefine placement new so you can still use it
|
2021-03-11 02:06:03 +08:00
|
|
|
static void* operator new(size_t, void* p) { return p; }
|
|
|
|
static void operator delete(void*, void*) {}
|
2017-05-26 04:48:44 +08:00
|
|
|
};
|
|
|
|
|
2020-02-05 02:26:18 +08:00
|
|
|
[[nodiscard]] inline void* allocateFast(int size) {
|
2021-03-11 02:06:03 +08:00
|
|
|
if (size <= 16)
|
|
|
|
return FastAllocator<16>::allocate();
|
|
|
|
if (size <= 32)
|
|
|
|
return FastAllocator<32>::allocate();
|
|
|
|
if (size <= 64)
|
|
|
|
return FastAllocator<64>::allocate();
|
|
|
|
if (size <= 96)
|
|
|
|
return FastAllocator<96>::allocate();
|
|
|
|
if (size <= 128)
|
|
|
|
return FastAllocator<128>::allocate();
|
|
|
|
if (size <= 256)
|
|
|
|
return FastAllocator<256>::allocate();
|
|
|
|
if (size <= 512)
|
|
|
|
return FastAllocator<512>::allocate();
|
|
|
|
if (size <= 1024)
|
|
|
|
return FastAllocator<1024>::allocate();
|
|
|
|
if (size <= 2048)
|
|
|
|
return FastAllocator<2048>::allocate();
|
|
|
|
if (size <= 4096)
|
|
|
|
return FastAllocator<4096>::allocate();
|
|
|
|
if (size <= 8192)
|
|
|
|
return FastAllocator<8192>::allocate();
|
|
|
|
if (size <= 16384)
|
|
|
|
return FastAllocator<16384>::allocate();
|
2017-05-26 04:48:44 +08:00
|
|
|
return new uint8_t[size];
|
|
|
|
}
|
|
|
|
|
2019-06-21 00:29:01 +08:00
|
|
|
inline void freeFast(int size, void* ptr) {
|
2021-03-11 02:06:03 +08:00
|
|
|
if (size <= 16)
|
|
|
|
return FastAllocator<16>::release(ptr);
|
|
|
|
if (size <= 32)
|
|
|
|
return FastAllocator<32>::release(ptr);
|
|
|
|
if (size <= 64)
|
|
|
|
return FastAllocator<64>::release(ptr);
|
|
|
|
if (size <= 96)
|
|
|
|
return FastAllocator<96>::release(ptr);
|
|
|
|
if (size <= 128)
|
|
|
|
return FastAllocator<128>::release(ptr);
|
|
|
|
if (size <= 256)
|
|
|
|
return FastAllocator<256>::release(ptr);
|
|
|
|
if (size <= 512)
|
|
|
|
return FastAllocator<512>::release(ptr);
|
|
|
|
if (size <= 1024)
|
|
|
|
return FastAllocator<1024>::release(ptr);
|
|
|
|
if (size <= 2048)
|
|
|
|
return FastAllocator<2048>::release(ptr);
|
|
|
|
if (size <= 4096)
|
|
|
|
return FastAllocator<4096>::release(ptr);
|
|
|
|
if (size <= 8192)
|
|
|
|
return FastAllocator<8192>::release(ptr);
|
|
|
|
if (size <= 16384)
|
|
|
|
return FastAllocator<16384>::release(ptr);
|
|
|
|
delete[](uint8_t*) ptr;
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
2021-05-15 14:12:00 +08:00
|
|
|
[[nodiscard]] inline void* allocateFast4kAligned(int size) {
|
2021-05-18 09:02:09 +08:00
|
|
|
// Use FastAllocator for sizes it supports to avoid internal fragmentation in some implementations of aligned_alloc
|
2021-05-15 14:12:00 +08:00
|
|
|
if (size <= 4096)
|
|
|
|
return FastAllocator<4096>::allocate();
|
|
|
|
if (size <= 8192)
|
|
|
|
return FastAllocator<8192>::allocate();
|
|
|
|
if (size <= 16384)
|
|
|
|
return FastAllocator<16384>::allocate();
|
|
|
|
return aligned_alloc(4096, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void freeFast4kAligned(int size, void* ptr) {
|
2021-05-18 09:02:09 +08:00
|
|
|
// Sizes supported by FastAllocator must be release via FastAllocator
|
2021-05-15 14:12:00 +08:00
|
|
|
if (size <= 4096)
|
|
|
|
return FastAllocator<4096>::release(ptr);
|
|
|
|
if (size <= 8192)
|
|
|
|
return FastAllocator<8192>::release(ptr);
|
|
|
|
if (size <= 16384)
|
|
|
|
return FastAllocator<16384>::release(ptr);
|
|
|
|
aligned_free(ptr);
|
|
|
|
}
|
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
#endif
|