/src/duckdb/extension/jemalloc/jemalloc_extension.cpp
Line | Count | Source |
1 | | #include "jemalloc_extension.hpp" |
2 | | |
3 | | #include "duckdb/common/allocator.hpp" |
4 | | #include "jemalloc/jemalloc.h" |
5 | | #include "malloc_ncpus.h" |
6 | | |
7 | | #include <thread> |
8 | | |
9 | | namespace duckdb { |
10 | | |
11 | 8.91k | static void LoadInternal(ExtensionLoader &) { |
12 | | // NOP: This extension can only be loaded statically |
13 | 8.91k | } |
14 | 8.91k | void JemallocExtension::Load(ExtensionLoader &loader) { |
15 | 8.91k | LoadInternal(loader); |
16 | 8.91k | } |
17 | | |
18 | 8.91k | std::string JemallocExtension::Name() { |
19 | 8.91k | return "jemalloc"; |
20 | 8.91k | } |
21 | | |
22 | 0 | data_ptr_t JemallocExtension::Allocate(PrivateAllocatorData *private_data, idx_t size) { |
23 | 0 | return data_ptr_cast(duckdb_je_malloc(size)); |
24 | 0 | } |
25 | | |
26 | 0 | void JemallocExtension::Free(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t size) { |
27 | 0 | duckdb_je_free(pointer); |
28 | 0 | } |
29 | | |
30 | | data_ptr_t JemallocExtension::Reallocate(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t old_size, |
31 | 0 | idx_t size) { |
32 | 0 | return data_ptr_cast(duckdb_je_realloc(pointer, size)); |
33 | 0 | } |
34 | | |
35 | 0 | static void JemallocCTL(const char *name, void *old_ptr, size_t *old_len, void *new_ptr, size_t new_len) { |
36 | 0 | if (duckdb_je_mallctl(name, old_ptr, old_len, new_ptr, new_len) != 0) { |
37 | | #ifdef DEBUG |
38 | | // We only want to throw an exception here when debugging |
39 | | throw InternalException("je_mallctl failed for setting \"%s\"", name); |
40 | | #endif |
41 | 0 | } |
42 | 0 | } |
43 | | |
44 | | template <class T> |
45 | 0 | static void SetJemallocCTL(const char *name, T &val) { |
46 | 0 | JemallocCTL(name, nullptr, nullptr, &val, sizeof(T)); |
47 | 0 | } |
48 | | |
49 | 0 | static void SetJemallocCTL(const char *name) { |
50 | 0 | JemallocCTL(name, nullptr, nullptr, nullptr, 0); |
51 | 0 | } |
52 | | |
53 | | template <class T> |
54 | 0 | static T GetJemallocCTL(const char *name) { |
55 | 0 | T result; |
56 | 0 | size_t len = sizeof(T); |
57 | 0 | JemallocCTL(name, &result, &len, nullptr, 0); |
58 | 0 | return result; |
59 | 0 | } Unexecuted instantiation: jemalloc_extension.cpp:unsigned long duckdb::GetJemallocCTL<unsigned long>(char const*) Unexecuted instantiation: jemalloc_extension.cpp:unsigned int duckdb::GetJemallocCTL<unsigned int>(char const*) |
60 | | |
61 | 0 | static inline string PurgeArenaString(idx_t arena_idx) { |
62 | 0 | return StringUtil::Format("arena.%llu.purge", arena_idx); |
63 | 0 | } |
64 | | |
65 | 0 | int64_t JemallocExtension::DecayDelay() { |
66 | 0 | return DUCKDB_JEMALLOC_DECAY; |
67 | 0 | } |
68 | | |
69 | 0 | void JemallocExtension::ThreadFlush(idx_t threshold) { |
70 | | // We flush after exceeding the threshold |
71 | 0 | if (GetJemallocCTL<uint64_t>("thread.peak.read") > threshold) { |
72 | 0 | return; |
73 | 0 | } |
74 | | |
75 | | // Flush thread-local cache |
76 | 0 | SetJemallocCTL("thread.tcache.flush"); |
77 | | |
78 | | // Flush this thread's arena |
79 | 0 | const auto purge_arena = PurgeArenaString(idx_t(GetJemallocCTL<unsigned>("thread.arena"))); |
80 | 0 | SetJemallocCTL(purge_arena.c_str()); |
81 | | |
82 | | // Reset the peak after resetting |
83 | 0 | SetJemallocCTL("thread.peak.reset"); |
84 | 0 | } |
85 | | |
86 | 0 | void JemallocExtension::ThreadIdle() { |
87 | | // Indicate that this thread is idle |
88 | 0 | SetJemallocCTL("thread.idle"); |
89 | | |
90 | | // Reset the peak after resetting |
91 | 0 | SetJemallocCTL("thread.peak.reset"); |
92 | 0 | } |
93 | | |
94 | 0 | void JemallocExtension::FlushAll() { |
95 | | // Flush thread-local cache |
96 | 0 | SetJemallocCTL("thread.tcache.flush"); |
97 | | |
98 | | // Flush all arenas |
99 | 0 | const auto purge_arena = PurgeArenaString(MALLCTL_ARENAS_ALL); |
100 | 0 | SetJemallocCTL(purge_arena.c_str()); |
101 | | |
102 | | // Reset the peak after resetting |
103 | 0 | SetJemallocCTL("thread.peak.reset"); |
104 | 0 | } |
105 | | |
106 | 0 | void JemallocExtension::SetBackgroundThreads(bool enable) { |
107 | 0 | #ifndef __APPLE__ |
108 | 0 | SetJemallocCTL("background_thread", enable); |
109 | 0 | #endif |
110 | 0 | } |
111 | | |
112 | 8.91k | std::string JemallocExtension::Version() const { |
113 | 8.91k | #ifdef EXT_VERSION_JEMALLOC |
114 | 8.91k | return EXT_VERSION_JEMALLOC; |
115 | | #else |
116 | | return ""; |
117 | | #endif |
118 | 8.91k | } |
119 | | |
120 | | } // namespace duckdb |
121 | | |
122 | | extern "C" { |
123 | | |
124 | 0 | unsigned duckdb_malloc_ncpus() { |
125 | | #ifdef DUCKDB_NO_THREADS |
126 | | return 1 |
127 | | #else |
128 | 0 | unsigned concurrency = duckdb::NumericCast<unsigned>(std::thread::hardware_concurrency()); |
129 | 0 | return std::max(concurrency, 1u); |
130 | 0 | #endif |
131 | 0 | } |
132 | | |
133 | 0 | DUCKDB_CPP_EXTENSION_ENTRY(jemalloc, loader) { |
134 | 0 | duckdb::LoadInternal(loader); |
135 | 0 | } |
136 | | } |