Skip to content

Commit 045f559

Browse files
dschoGit for Windows Build Agent
authored and
Git for Windows Build Agent
committed
Merge branch 'mimalloc-v2.0.9'
This topic vendors in mimalloc v2.0.9, a fast allocator that allows Git for Windows to perform efficiently. Switch Git for Windows to using mimalloc instead of nedmalloc
2 parents 5462d4a + 00c1de3 commit 045f559

30 files changed

+12970
-8
lines changed

Makefile

+38
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,7 @@ BUILTIN_OBJS += builtin/write-tree.o
13451345
# upstream unnecessarily (making merging in future changes easier).
13461346
THIRD_PARTY_SOURCES += compat/inet_ntop.c
13471347
THIRD_PARTY_SOURCES += compat/inet_pton.c
1348+
THIRD_PARTY_SOURCES += compat/mimalloc/%
13481349
THIRD_PARTY_SOURCES += compat/nedmalloc/%
13491350
THIRD_PARTY_SOURCES += compat/obstack.%
13501351
THIRD_PARTY_SOURCES += compat/poll/%
@@ -2149,6 +2150,43 @@ ifdef USE_NED_ALLOCATOR
21492150
OVERRIDE_STRDUP = YesPlease
21502151
endif
21512152

2153+
ifdef USE_MIMALLOC
2154+
MIMALLOC_OBJS = \
2155+
compat/mimalloc/alloc-aligned.o \
2156+
compat/mimalloc/alloc.o \
2157+
compat/mimalloc/arena.o \
2158+
compat/mimalloc/bitmap.o \
2159+
compat/mimalloc/heap.o \
2160+
compat/mimalloc/init.o \
2161+
compat/mimalloc/options.o \
2162+
compat/mimalloc/os.o \
2163+
compat/mimalloc/page.o \
2164+
compat/mimalloc/random.o \
2165+
compat/mimalloc/prim/windows/prim.o \
2166+
compat/mimalloc/segment.o \
2167+
compat/mimalloc/segment-cache.o \
2168+
compat/mimalloc/segment-map.o \
2169+
compat/mimalloc/stats.o
2170+
2171+
COMPAT_CFLAGS += -Icompat/mimalloc -DMI_DEBUG=0 -DUSE_MIMALLOC --std=gnu11
2172+
COMPAT_OBJS += $(MIMALLOC_OBJS)
2173+
2174+
$(MIMALLOC_OBJS): COMPAT_CFLAGS += -DBANNED_H
2175+
2176+
$(MIMALLOC_OBJS): COMPAT_CFLAGS += \
2177+
-Wno-attributes \
2178+
-Wno-unknown-pragmas \
2179+
-Wno-array-bounds
2180+
2181+
ifdef DEVELOPER
2182+
$(MIMALLOC_OBJS): COMPAT_CFLAGS += \
2183+
-Wno-pedantic \
2184+
-Wno-declaration-after-statement \
2185+
-Wno-old-style-definition \
2186+
-Wno-missing-prototypes
2187+
endif
2188+
endif
2189+
21522190
ifdef OVERRIDE_STRDUP
21532191
COMPAT_CFLAGS += -DOVERRIDE_STRDUP
21542192
COMPAT_OBJS += compat/strdup.o

compat/mimalloc/LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2018-2021 Microsoft Corporation, Daan Leijen
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

compat/mimalloc/alloc-aligned.c

+298
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
/* ----------------------------------------------------------------------------
2+
Copyright (c) 2018-2021, Microsoft Research, Daan Leijen
3+
This is free software; you can redistribute it and/or modify it under the
4+
terms of the MIT license. A copy of the license can be found in the file
5+
"LICENSE" at the root of this distribution.
6+
-----------------------------------------------------------------------------*/
7+
8+
#include "mimalloc.h"
9+
#include "mimalloc/internal.h"
10+
#include "mimalloc/prim.h" // mi_prim_get_default_heap
11+
12+
#include <string.h> // memset
13+
14+
// ------------------------------------------------------
15+
// Aligned Allocation
16+
// ------------------------------------------------------
17+
18+
// Fallback primitive aligned allocation -- split out for better codegen
19+
static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_fallback(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero) mi_attr_noexcept
20+
{
21+
mi_assert_internal(size <= PTRDIFF_MAX);
22+
mi_assert_internal(alignment != 0 && _mi_is_power_of_two(alignment));
23+
24+
const uintptr_t align_mask = alignment - 1; // for any x, `(x & align_mask) == (x % alignment)`
25+
const size_t padsize = size + MI_PADDING_SIZE;
26+
27+
// use regular allocation if it is guaranteed to fit the alignment constraints
28+
if (offset==0 && alignment<=padsize && padsize<=MI_MAX_ALIGN_GUARANTEE && (padsize&align_mask)==0) {
29+
void* p = _mi_heap_malloc_zero(heap, size, zero);
30+
mi_assert_internal(p == NULL || ((uintptr_t)p % alignment) == 0);
31+
return p;
32+
}
33+
34+
void* p;
35+
size_t oversize;
36+
if mi_unlikely(alignment > MI_ALIGNMENT_MAX) {
37+
// use OS allocation for very large alignment and allocate inside a huge page (dedicated segment with 1 page)
38+
// This can support alignments >= MI_SEGMENT_SIZE by ensuring the object can be aligned at a point in the
39+
// first (and single) page such that the segment info is `MI_SEGMENT_SIZE` bytes before it (so it can be found by aligning the pointer down)
40+
if mi_unlikely(offset != 0) {
41+
// todo: cannot support offset alignment for very large alignments yet
42+
#if MI_DEBUG > 0
43+
_mi_error_message(EOVERFLOW, "aligned allocation with a very large alignment cannot be used with an alignment offset (size %zu, alignment %zu, offset %zu)\n", size, alignment, offset);
44+
#endif
45+
return NULL;
46+
}
47+
oversize = (size <= MI_SMALL_SIZE_MAX ? MI_SMALL_SIZE_MAX + 1 /* ensure we use generic malloc path */ : size);
48+
p = _mi_heap_malloc_zero_ex(heap, oversize, false, alignment); // the page block size should be large enough to align in the single huge page block
49+
// zero afterwards as only the area from the aligned_p may be committed!
50+
if (p == NULL) return NULL;
51+
}
52+
else {
53+
// otherwise over-allocate
54+
oversize = size + alignment - 1;
55+
p = _mi_heap_malloc_zero(heap, oversize, zero);
56+
if (p == NULL) return NULL;
57+
}
58+
59+
// .. and align within the allocation
60+
const uintptr_t poffset = ((uintptr_t)p + offset) & align_mask;
61+
const uintptr_t adjust = (poffset == 0 ? 0 : alignment - poffset);
62+
mi_assert_internal(adjust < alignment);
63+
void* aligned_p = (void*)((uintptr_t)p + adjust);
64+
if (aligned_p != p) {
65+
mi_page_t* page = _mi_ptr_page(p);
66+
mi_page_set_has_aligned(page, true);
67+
_mi_padding_shrink(page, (mi_block_t*)p, adjust + size);
68+
}
69+
// todo: expand padding if overallocated ?
70+
71+
mi_assert_internal(mi_page_usable_block_size(_mi_ptr_page(p)) >= adjust + size);
72+
mi_assert_internal(p == _mi_page_ptr_unalign(_mi_ptr_segment(aligned_p), _mi_ptr_page(aligned_p), aligned_p));
73+
mi_assert_internal(((uintptr_t)aligned_p + offset) % alignment == 0);
74+
mi_assert_internal(mi_usable_size(aligned_p)>=size);
75+
mi_assert_internal(mi_usable_size(p) == mi_usable_size(aligned_p)+adjust);
76+
77+
// now zero the block if needed
78+
if (alignment > MI_ALIGNMENT_MAX) {
79+
// for the tracker, on huge aligned allocations only from the start of the large block is defined
80+
mi_track_mem_undefined(aligned_p, size);
81+
if (zero) {
82+
_mi_memzero_aligned(aligned_p, mi_usable_size(aligned_p));
83+
}
84+
}
85+
86+
if (p != aligned_p) {
87+
mi_track_align(p,aligned_p,adjust,mi_usable_size(aligned_p));
88+
}
89+
return aligned_p;
90+
}
91+
92+
// Primitive aligned allocation
93+
static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero) mi_attr_noexcept
94+
{
95+
// note: we don't require `size > offset`, we just guarantee that the address at offset is aligned regardless of the allocated size.
96+
if mi_unlikely(alignment == 0 || !_mi_is_power_of_two(alignment)) { // require power-of-two (see <https://en.cppreference.com/w/c/memory/aligned_alloc>)
97+
#if MI_DEBUG > 0
98+
_mi_error_message(EOVERFLOW, "aligned allocation requires the alignment to be a power-of-two (size %zu, alignment %zu)\n", size, alignment);
99+
#endif
100+
return NULL;
101+
}
102+
103+
if mi_unlikely(size > PTRDIFF_MAX) { // we don't allocate more than PTRDIFF_MAX (see <https://sourceware.org/ml/libc-announce/2019/msg00001.html>)
104+
#if MI_DEBUG > 0
105+
_mi_error_message(EOVERFLOW, "aligned allocation request is too large (size %zu, alignment %zu)\n", size, alignment);
106+
#endif
107+
return NULL;
108+
}
109+
const uintptr_t align_mask = alignment-1; // for any x, `(x & align_mask) == (x % alignment)`
110+
const size_t padsize = size + MI_PADDING_SIZE; // note: cannot overflow due to earlier size > PTRDIFF_MAX check
111+
112+
// try first if there happens to be a small block available with just the right alignment
113+
if mi_likely(padsize <= MI_SMALL_SIZE_MAX && alignment <= padsize) {
114+
mi_page_t* page = _mi_heap_get_free_small_page(heap, padsize);
115+
const bool is_aligned = (((uintptr_t)page->free+offset) & align_mask)==0;
116+
if mi_likely(page->free != NULL && is_aligned)
117+
{
118+
#if MI_STAT>1
119+
mi_heap_stat_increase(heap, malloc, size);
120+
#endif
121+
void* p = _mi_page_malloc(heap, page, padsize, zero); // TODO: inline _mi_page_malloc
122+
mi_assert_internal(p != NULL);
123+
mi_assert_internal(((uintptr_t)p + offset) % alignment == 0);
124+
mi_track_malloc(p,size,zero);
125+
return p;
126+
}
127+
}
128+
// fallback
129+
return mi_heap_malloc_zero_aligned_at_fallback(heap, size, alignment, offset, zero);
130+
}
131+
132+
133+
// ------------------------------------------------------
134+
// Optimized mi_heap_malloc_aligned / mi_malloc_aligned
135+
// ------------------------------------------------------
136+
137+
mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
138+
return mi_heap_malloc_zero_aligned_at(heap, size, alignment, offset, false);
139+
}
140+
141+
mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept {
142+
if mi_unlikely(alignment == 0 || !_mi_is_power_of_two(alignment)) return NULL;
143+
#if !MI_PADDING
144+
// without padding, any small sized allocation is naturally aligned (see also `_mi_segment_page_start`)
145+
if mi_likely(_mi_is_power_of_two(size) && size >= alignment && size <= MI_SMALL_SIZE_MAX)
146+
#else
147+
// with padding, we can only guarantee this for fixed alignments
148+
if mi_likely((alignment == sizeof(void*) || (alignment == MI_MAX_ALIGN_SIZE && size > (MI_MAX_ALIGN_SIZE/2)))
149+
&& size <= MI_SMALL_SIZE_MAX)
150+
#endif
151+
{
152+
// fast path for common alignment and size
153+
return mi_heap_malloc_small(heap, size);
154+
}
155+
else {
156+
return mi_heap_malloc_aligned_at(heap, size, alignment, 0);
157+
}
158+
}
159+
160+
// ensure a definition is emitted
161+
#if defined(__cplusplus)
162+
static void* _mi_heap_malloc_aligned = (void*)&mi_heap_malloc_aligned;
163+
#endif
164+
165+
// ------------------------------------------------------
166+
// Aligned Allocation
167+
// ------------------------------------------------------
168+
169+
mi_decl_nodiscard mi_decl_restrict void* mi_heap_zalloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
170+
return mi_heap_malloc_zero_aligned_at(heap, size, alignment, offset, true);
171+
}
172+
173+
mi_decl_nodiscard mi_decl_restrict void* mi_heap_zalloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept {
174+
return mi_heap_zalloc_aligned_at(heap, size, alignment, 0);
175+
}
176+
177+
mi_decl_nodiscard mi_decl_restrict void* mi_heap_calloc_aligned_at(mi_heap_t* heap, size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
178+
size_t total;
179+
if (mi_count_size_overflow(count, size, &total)) return NULL;
180+
return mi_heap_zalloc_aligned_at(heap, total, alignment, offset);
181+
}
182+
183+
mi_decl_nodiscard mi_decl_restrict void* mi_heap_calloc_aligned(mi_heap_t* heap, size_t count, size_t size, size_t alignment) mi_attr_noexcept {
184+
return mi_heap_calloc_aligned_at(heap,count,size,alignment,0);
185+
}
186+
187+
mi_decl_nodiscard mi_decl_restrict void* mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
188+
return mi_heap_malloc_aligned_at(mi_prim_get_default_heap(), size, alignment, offset);
189+
}
190+
191+
mi_decl_nodiscard mi_decl_restrict void* mi_malloc_aligned(size_t size, size_t alignment) mi_attr_noexcept {
192+
return mi_heap_malloc_aligned(mi_prim_get_default_heap(), size, alignment);
193+
}
194+
195+
mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
196+
return mi_heap_zalloc_aligned_at(mi_prim_get_default_heap(), size, alignment, offset);
197+
}
198+
199+
mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_aligned(size_t size, size_t alignment) mi_attr_noexcept {
200+
return mi_heap_zalloc_aligned(mi_prim_get_default_heap(), size, alignment);
201+
}
202+
203+
mi_decl_nodiscard mi_decl_restrict void* mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
204+
return mi_heap_calloc_aligned_at(mi_prim_get_default_heap(), count, size, alignment, offset);
205+
}
206+
207+
mi_decl_nodiscard mi_decl_restrict void* mi_calloc_aligned(size_t count, size_t size, size_t alignment) mi_attr_noexcept {
208+
return mi_heap_calloc_aligned(mi_prim_get_default_heap(), count, size, alignment);
209+
}
210+
211+
212+
// ------------------------------------------------------
213+
// Aligned re-allocation
214+
// ------------------------------------------------------
215+
216+
static void* mi_heap_realloc_zero_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset, bool zero) mi_attr_noexcept {
217+
mi_assert(alignment > 0);
218+
if (alignment <= sizeof(uintptr_t)) return _mi_heap_realloc_zero(heap,p,newsize,zero);
219+
if (p == NULL) return mi_heap_malloc_zero_aligned_at(heap,newsize,alignment,offset,zero);
220+
size_t size = mi_usable_size(p);
221+
if (newsize <= size && newsize >= (size - (size / 2))
222+
&& (((uintptr_t)p + offset) % alignment) == 0) {
223+
return p; // reallocation still fits, is aligned and not more than 50% waste
224+
}
225+
else {
226+
// note: we don't zero allocate upfront so we only zero initialize the expanded part
227+
void* newp = mi_heap_malloc_aligned_at(heap,newsize,alignment,offset);
228+
if (newp != NULL) {
229+
if (zero && newsize > size) {
230+
// also set last word in the previous allocation to zero to ensure any padding is zero-initialized
231+
size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0);
232+
_mi_memzero((uint8_t*)newp + start, newsize - start);
233+
}
234+
_mi_memcpy_aligned(newp, p, (newsize > size ? size : newsize));
235+
mi_free(p); // only free if successful
236+
}
237+
return newp;
238+
}
239+
}
240+
241+
static void* mi_heap_realloc_zero_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, bool zero) mi_attr_noexcept {
242+
mi_assert(alignment > 0);
243+
if (alignment <= sizeof(uintptr_t)) return _mi_heap_realloc_zero(heap,p,newsize,zero);
244+
size_t offset = ((uintptr_t)p % alignment); // use offset of previous allocation (p can be NULL)
245+
return mi_heap_realloc_zero_aligned_at(heap,p,newsize,alignment,offset,zero);
246+
}
247+
248+
mi_decl_nodiscard void* mi_heap_realloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept {
249+
return mi_heap_realloc_zero_aligned_at(heap,p,newsize,alignment,offset,false);
250+
}
251+
252+
mi_decl_nodiscard void* mi_heap_realloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept {
253+
return mi_heap_realloc_zero_aligned(heap,p,newsize,alignment,false);
254+
}
255+
256+
mi_decl_nodiscard void* mi_heap_rezalloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept {
257+
return mi_heap_realloc_zero_aligned_at(heap, p, newsize, alignment, offset, true);
258+
}
259+
260+
mi_decl_nodiscard void* mi_heap_rezalloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept {
261+
return mi_heap_realloc_zero_aligned(heap, p, newsize, alignment, true);
262+
}
263+
264+
mi_decl_nodiscard void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
265+
size_t total;
266+
if (mi_count_size_overflow(newcount, size, &total)) return NULL;
267+
return mi_heap_rezalloc_aligned_at(heap, p, total, alignment, offset);
268+
}
269+
270+
mi_decl_nodiscard void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept {
271+
size_t total;
272+
if (mi_count_size_overflow(newcount, size, &total)) return NULL;
273+
return mi_heap_rezalloc_aligned(heap, p, total, alignment);
274+
}
275+
276+
mi_decl_nodiscard void* mi_realloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept {
277+
return mi_heap_realloc_aligned_at(mi_prim_get_default_heap(), p, newsize, alignment, offset);
278+
}
279+
280+
mi_decl_nodiscard void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept {
281+
return mi_heap_realloc_aligned(mi_prim_get_default_heap(), p, newsize, alignment);
282+
}
283+
284+
mi_decl_nodiscard void* mi_rezalloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept {
285+
return mi_heap_rezalloc_aligned_at(mi_prim_get_default_heap(), p, newsize, alignment, offset);
286+
}
287+
288+
mi_decl_nodiscard void* mi_rezalloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept {
289+
return mi_heap_rezalloc_aligned(mi_prim_get_default_heap(), p, newsize, alignment);
290+
}
291+
292+
mi_decl_nodiscard void* mi_recalloc_aligned_at(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
293+
return mi_heap_recalloc_aligned_at(mi_prim_get_default_heap(), p, newcount, size, alignment, offset);
294+
}
295+
296+
mi_decl_nodiscard void* mi_recalloc_aligned(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept {
297+
return mi_heap_recalloc_aligned(mi_prim_get_default_heap(), p, newcount, size, alignment);
298+
}

0 commit comments

Comments
 (0)