You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
This version of mimalloc is very efficient at distributing threaded allocations in a way that maintains
locality which in turn improve the amount of memory that we're able to send back to the system
after heavily multithreaded workloads. This also improves performance as less page fault and cache misses
are expected coming from more densily packed allocations.
mimalloc v1 seemed to waste more memory because of its commit size being larger than TBB.
However, its allocation patterns was already way tigther than TBB but for it to become apparent, you had
to activate the "page_reset" and "reset_decommits" options, which came at a performance loss.
mimalloc v2 offers both better locality and by default will more agressively decommit memory with
only minor performance loss in some cases and performance gain in many.
Given the advantages of mimalloc v2 compared to Intel TBB, we should probably consider it
as our next default allocator for the editor.
- All tests performed on AMD TR 3970X with 256GB RAM
- Loading FramingCameraTest map on special project with -ddc=cold and waiting until every asset is built
- 699s @ 32GB for tbb malloc
- 655s @ 37GB for mimalloc v1
- 757s @ 12GB for mimalloc v1 + page_reset and reset_decommits
- 604s @ 15GB for mimalloc v2
- Loading P_World on Reverb -ddc=cold and waiting until every asset is built
- 2372s @ 71GB for tbb malloc
- 2587s @ 75GB for mimalloc v1
- 3212s @ 34GB for mimalloc v1 + page_reset and reset_decommits
- 2503s @ 37GB for mimalloc v2
- Loading P_Construct_WP on special project with -ddc=cold and waiting until every asset is built
- 6404s @ 56GB for tbb malloc
- 6640s @ 37GB for mimalloc v2
- Loading Apollo_Terrain on FortniteGame with -ddc=cold and waiting until every asset is built
- 751s @ 33GB for tbb malloc
- 744s @ 25GB for mimalloc v2
- Cooking FramingCameraTest map on special project with a warmed-up DDC
- 379s @ 34GB for tbb malloc
- 367s @ 29GB for mimalloc v2
#rb Brandon.Dawson, Yuriy.ODonnell, Stefan.Boberg
[CL 15859558 by danny couture in ue5-main branch]
250 lines
7.9 KiB
C
250 lines
7.9 KiB
C
/* ----------------------------------------------------------------------------
|
|
Copyright (c) 2018, Microsoft Research, Daan Leijen
|
|
This is free software; you can redistribute it and/or modify it under the
|
|
terms of the MIT license. A copy of the license can be found in the file
|
|
"LICENSE" at the root of this distribution.
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
Testing allocators is difficult as bugs may only surface after particular
|
|
allocation patterns. The main approach to testing _mimalloc_ is therefore
|
|
to have extensive internal invariant checking (see `page_is_valid` in `page.c`
|
|
for example), which is enabled in debug mode with `-DMI_DEBUG_FULL=ON`.
|
|
The main testing is then to run `mimalloc-bench` [1] using full invariant checking
|
|
to catch any potential problems over a wide range of intensive allocation bench
|
|
marks.
|
|
|
|
However, this does not test well for the entire API surface. In this test file
|
|
we therefore test the API over various inputs. Please add more tests :-)
|
|
|
|
[1] https://github.com/daanx/mimalloc-bench
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <errno.h>
|
|
|
|
#ifdef __cplusplus
|
|
#include <vector>
|
|
#endif
|
|
|
|
#include "mimalloc.h"
|
|
// #include "mimalloc-internal.h"
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Test macros: CHECK(name,predicate) and CHECK_BODY(name,body)
|
|
// ---------------------------------------------------------------------------
|
|
static int ok = 0;
|
|
static int failed = 0;
|
|
|
|
#define CHECK_BODY(name,body) \
|
|
do { \
|
|
fprintf(stderr,"test: %s... ", name ); \
|
|
bool result = true; \
|
|
do { body } while(false); \
|
|
if (!(result)) { \
|
|
failed++; \
|
|
fprintf(stderr, \
|
|
"\n FAILED: %s:%d:\n %s\n", \
|
|
__FILE__, \
|
|
__LINE__, \
|
|
#body); \
|
|
/* exit(1); */ \
|
|
} \
|
|
else { \
|
|
ok++; \
|
|
fprintf(stderr,"ok.\n"); \
|
|
} \
|
|
} while (false)
|
|
|
|
#define CHECK(name,expr) CHECK_BODY(name,{ result = (expr); })
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Test functions
|
|
// ---------------------------------------------------------------------------
|
|
bool test_heap1();
|
|
bool test_heap2();
|
|
bool test_stl_allocator1();
|
|
bool test_stl_allocator2();
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Main testing
|
|
// ---------------------------------------------------------------------------
|
|
int main() {
|
|
mi_option_disable(mi_option_verbose);
|
|
|
|
// ---------------------------------------------------
|
|
// Malloc
|
|
// ---------------------------------------------------
|
|
|
|
CHECK_BODY("malloc-zero",{
|
|
void* p = mi_malloc(0); mi_free(p);
|
|
});
|
|
CHECK_BODY("malloc-nomem1",{
|
|
result = (mi_malloc(SIZE_MAX/2) == NULL);
|
|
});
|
|
CHECK_BODY("malloc-null",{
|
|
mi_free(NULL);
|
|
});
|
|
CHECK_BODY("calloc-overflow",{
|
|
// use (size_t)&mi_calloc to get some number without triggering compiler warnings
|
|
result = (mi_calloc((size_t)&mi_calloc,SIZE_MAX/1000) == NULL);
|
|
});
|
|
CHECK_BODY("calloc0",{
|
|
result = (mi_usable_size(mi_calloc(0,1000)) <= 16);
|
|
});
|
|
|
|
// ---------------------------------------------------
|
|
// Extended
|
|
// ---------------------------------------------------
|
|
CHECK_BODY("posix_memalign1", {
|
|
void* p = &p;
|
|
int err = mi_posix_memalign(&p, sizeof(void*), 32);
|
|
result = ((err==0 && (uintptr_t)p % sizeof(void*) == 0) || p==&p);
|
|
mi_free(p);
|
|
});
|
|
CHECK_BODY("posix_memalign_no_align", {
|
|
void* p = &p;
|
|
int err = mi_posix_memalign(&p, 3, 32);
|
|
result = (err==EINVAL && p==&p);
|
|
});
|
|
CHECK_BODY("posix_memalign_zero", {
|
|
void* p = &p;
|
|
int err = mi_posix_memalign(&p, sizeof(void*), 0);
|
|
mi_free(p);
|
|
result = (err==0);
|
|
});
|
|
CHECK_BODY("posix_memalign_nopow2", {
|
|
void* p = &p;
|
|
int err = mi_posix_memalign(&p, 3*sizeof(void*), 32);
|
|
result = (err==EINVAL && p==&p);
|
|
});
|
|
CHECK_BODY("posix_memalign_nomem", {
|
|
void* p = &p;
|
|
int err = mi_posix_memalign(&p, sizeof(void*), SIZE_MAX);
|
|
result = (err==ENOMEM && p==&p);
|
|
});
|
|
|
|
// ---------------------------------------------------
|
|
// Aligned API
|
|
// ---------------------------------------------------
|
|
CHECK_BODY("malloc-aligned1", {
|
|
void* p = mi_malloc_aligned(32,32); result = (p != NULL && (uintptr_t)(p) % 32 == 0); mi_free(p);
|
|
});
|
|
CHECK_BODY("malloc-aligned2", {
|
|
void* p = mi_malloc_aligned(48,32); result = (p != NULL && (uintptr_t)(p) % 32 == 0); mi_free(p);
|
|
});
|
|
CHECK_BODY("malloc-aligned3", {
|
|
void* p1 = mi_malloc_aligned(48,32); bool result1 = (p1 != NULL && (uintptr_t)(p1) % 32 == 0);
|
|
void* p2 = mi_malloc_aligned(48,32); bool result2 = (p2 != NULL && (uintptr_t)(p2) % 32 == 0);
|
|
mi_free(p2);
|
|
mi_free(p1);
|
|
result = (result1&&result2);
|
|
});
|
|
CHECK_BODY("malloc-aligned4", {
|
|
void* p;
|
|
bool ok = true;
|
|
for (int i = 0; i < 8 && ok; i++) {
|
|
p = mi_malloc_aligned(8, 16);
|
|
ok = (p != NULL && (uintptr_t)(p) % 16 == 0); mi_free(p);
|
|
}
|
|
result = ok;
|
|
});
|
|
CHECK_BODY("malloc-aligned5", {
|
|
void* p = mi_malloc_aligned(4097,4096); size_t usable = mi_usable_size(p); result = usable >= 4097 && usable < 10000; mi_free(p);
|
|
});
|
|
CHECK_BODY("malloc-aligned-at1", {
|
|
void* p = mi_malloc_aligned_at(48,32,0); result = (p != NULL && ((uintptr_t)(p) + 0) % 32 == 0); mi_free(p);
|
|
});
|
|
CHECK_BODY("malloc-aligned-at2", {
|
|
void* p = mi_malloc_aligned_at(50,32,8); result = (p != NULL && ((uintptr_t)(p) + 8) % 32 == 0); mi_free(p);
|
|
});
|
|
CHECK_BODY("memalign1", {
|
|
void* p;
|
|
bool ok = true;
|
|
for (int i = 0; i < 8 && ok; i++) {
|
|
p = mi_memalign(16,8);
|
|
ok = (p != NULL && (uintptr_t)(p) % 16 == 0); mi_free(p);
|
|
}
|
|
result = ok;
|
|
});
|
|
|
|
// ---------------------------------------------------
|
|
// Heaps
|
|
// ---------------------------------------------------
|
|
CHECK("heap_destroy", test_heap1());
|
|
CHECK("heap_delete", test_heap2());
|
|
|
|
//mi_stats_print(NULL);
|
|
|
|
// ---------------------------------------------------
|
|
// various
|
|
// ---------------------------------------------------
|
|
CHECK_BODY("realpath", {
|
|
char* s = mi_realpath( ".", NULL );
|
|
// printf("realpath: %s\n",s);
|
|
mi_free(s);
|
|
});
|
|
|
|
CHECK("stl_allocator1", test_stl_allocator1());
|
|
CHECK("stl_allocator2", test_stl_allocator2());
|
|
|
|
// ---------------------------------------------------
|
|
// Done
|
|
// ---------------------------------------------------[]
|
|
fprintf(stderr,"\n\n---------------------------------------------\n"
|
|
"succeeded: %i\n"
|
|
"failed : %i\n\n", ok, failed);
|
|
return failed;
|
|
}
|
|
|
|
// ---------------------------------------------------
|
|
// Larger test functions
|
|
// ---------------------------------------------------
|
|
|
|
bool test_heap1() {
|
|
mi_heap_t* heap = mi_heap_new();
|
|
int* p1 = mi_heap_malloc_tp(heap,int);
|
|
int* p2 = mi_heap_malloc_tp(heap,int);
|
|
*p1 = *p2 = 43;
|
|
mi_heap_destroy(heap);
|
|
return true;
|
|
}
|
|
|
|
bool test_heap2() {
|
|
mi_heap_t* heap = mi_heap_new();
|
|
int* p1 = mi_heap_malloc_tp(heap,int);
|
|
int* p2 = mi_heap_malloc_tp(heap,int);
|
|
mi_heap_delete(heap);
|
|
*p1 = 42;
|
|
mi_free(p1);
|
|
mi_free(p2);
|
|
return true;
|
|
}
|
|
|
|
bool test_stl_allocator1() {
|
|
#ifdef __cplusplus
|
|
std::vector<int, mi_stl_allocator<int> > vec;
|
|
vec.push_back(1);
|
|
vec.pop_back();
|
|
return vec.size() == 0;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
struct some_struct { int i; int j; double z; };
|
|
|
|
bool test_stl_allocator2() {
|
|
#ifdef __cplusplus
|
|
std::vector<some_struct, mi_stl_allocator<some_struct> > vec;
|
|
vec.push_back(some_struct());
|
|
vec.pop_back();
|
|
return vec.size() == 0;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|