nlib
heap/nmalloc_simple/nmalloc_simple.cpp

This extremely simple program compares the performance of the default malloc and free functions against nmalloc and nfree.

/*--------------------------------------------------------------------------------*
Project: CrossRoad
Copyright (C)Nintendo All rights reserved.
These coded instructions, statements, and computer programs contain proprietary
information of Nintendo and/or its licensed developers and are protected by
national and international copyright laws. They may not be disclosed to third
parties or copied or duplicated in any form, in whole or in part, without the
prior written consent of Nintendo.
The content herein is highly confidential and should be handled accordingly.
*--------------------------------------------------------------------------------*/
#include <stdlib.h>
using ::nlib_ns::threading::Thread;
#if defined(NN_PLATFORM_CTR)
static const int kRepeatConst = 1;
#elif defined(CAFE)
static const int kRepeatConst = 10;
#else
static const int kRepeatConst = 100; // others
#endif
//
// alloc/free 8bytes regions
//
class Malloc8Bytes {
public:
Malloc8Bytes(void* (*malloc_func)(size_t), void (*free_func)(void*))
: malloc_(malloc_func), free_(free_func) {}
bool operator()() {
bool success = true;
for (int loop_cnt = 0; loop_cnt < 50; ++loop_cnt) {
const size_t n = kRepeatConst * 1000;
void** ptrs = (void**)malloc_(n * sizeof(void*)); // NOLINT
if (!ptrs) return false;
memset(ptrs, 0, n * sizeof(void*)); // NOLINT
for (size_t i = 0; i < n; ++i) {
ptrs[i] = malloc_(8);
if (!ptrs[i]) {
success = false;
break;
}
}
for (size_t i = 0; i < n; ++i) {
free_(ptrs[i]);
}
free_(ptrs);
if (!success) return false;
}
return true;
}
private:
void* (*malloc_)(size_t);
void (*free_)(void*);
};
//
// it causes severe fragmentation if naive malloc/free is used
//
class MallocFragmentation {
public:
MallocFragmentation(void* (*malloc_func)(size_t), void (*free_func)(void*))
: malloc_(malloc_func), free_(free_func) {}
bool operator()() {
const int nloop = kRepeatConst * 30;
bool success = true;
for (int loop_cnt = 0; loop_cnt < nloop; ++loop_cnt) {
const size_t n = 10000;
void** ptrs = (void**)malloc_(n * sizeof(void*)); // NOLINT
if (!ptrs) return false;
memset(ptrs, 0, n * sizeof(void*)); // NOLINT
for (size_t i = 0; i < n; ++i) {
ptrs[i] = malloc_(8);
if (!ptrs[i]) {
success = false;
break;
}
}
for (size_t i = 0; i < n; i += 2) {
free_(ptrs[i]);
ptrs[i] = NULL;
}
for (size_t i = 0; i < n; i += 2) {
ptrs[i] = malloc_(16);
if (!ptrs[i]) {
success = false;
break;
}
}
for (size_t i = 0; i < n; ++i) {
free_(ptrs[i]);
}
free_(ptrs);
if (!success) return false;
}
return true;
}
private:
void* (*malloc_)(size_t);
void (*free_)(void*);
};
//
// realloc from 1 byte to 100000 bytes
//
class MallocRealloc {
public:
MallocRealloc(void* (*realloc_func)(void*, size_t), void (*free_func)(void*))
: realloc_(realloc_func), free_(free_func) {}
bool operator()() {
for (int loop_cnt = 0; loop_cnt < kRepeatConst; ++loop_cnt) {
const size_t n = 100000;
void* p = NULL;
for (size_t i = 1; i < n; ++i) {
void* pnew = realloc_(p, i);
if (!pnew) {
free_(p);
return false;
}
p = pnew;
}
free_(p);
}
return true;
}
private:
void* (*realloc_)(void*, size_t);
void (*free_)(void*);
};
const int kNumThread = 10;
Thread g_th[kNumThread];
volatile bool g_success;
class MtSmallMem {
public:
MtSmallMem(void* malloc_func(size_t), void free_func(void*))
: malloc_(malloc_func), free_(free_func) {}
void operator()() {
const int n = 1000;
void* p[n];
for (int j = 0; j < kRepeatConst * 10; ++j) {
for (int i = 0; i < n; ++i) {
p[i] = malloc_(8);
if (!p[i]) g_success = false;
}
for (int i = 0; i < n; ++i) {
free_(p[i]);
}
}
}
private:
void* (*malloc_)(size_t);
void (*free_)(void*);
};
class MtLargeMem {
public:
MtLargeMem(void* malloc_func(size_t), void free_func(void*))
: malloc_(malloc_func), free_(free_func) {}
void operator()() {
const int n = 40;
void* p[n];
for (int j = 0; j < kRepeatConst * 10; ++j) {
for (int i = 0; i < n; ++i) {
p[i] = malloc_(16384);
if (!p[i]) g_success = false;
}
for (int i = 0; i < n; ++i) {
free_(p[i]);
}
}
}
private:
void* (*malloc_)(size_t);
void (*free_)(void*);
};
//
// multithread performance
//
template<class FUNC>
class MallocMt {
public:
explicit MallocMt(const FUNC& obj) : obj_(obj) {}
bool operator()() {
g_success = true;
for (int i = 0; i < kNumThread; ++i) {
g_th[i].Start(obj_);
}
for (int i = 0; i < kNumThread; ++i) {
g_th[i].Join();
}
return g_success;
}
private:
FUNC obj_;
};
//
// lock / unock mutex
//
class MutexLoop {
public:
bool operator()() {
for (int i = 0; i < kRepeatConst * 50000; ++i) {
nlib_mutex_lock(&g_mutex);
nlib_mutex_unlock(&g_mutex);
}
return true;
}
};
static void* g_Ptr = NULL;
class NmallocLoop {
public:
bool operator()() {
for (int i = 0; i < kRepeatConst * 50000; ++i) {
g_Ptr = nmalloc(8);
nfree(g_Ptr);
}
return true;
}
};
template<class FUNC>
class DoTest {
public:
DoTest(const char* title, const FUNC& func) : title_(title), func_(func) {}
bool operator()() {
uint64_t from = ::nlib_ns::GetTickTime();
bool result = func_();
uint64_t to = ::nlib_ns::GetTickTime();
nlib_printf("%s: %" PRIu64 " msec\n", title_, to - from);
return result;
}
private:
const char* title_;
FUNC func_;
};
static bool ClearCache() {
// free thread-cache nmalloc has, returning them to CentralHeap.
nmalloc_query(kNmallocQueryFinalizeCache);
int isclean;
nmalloc_query(kNmallocQueryIsClean, &isclean);
if (!isclean) {
nmalloc_query(kNmallocQueryDump, kNmallocDumpAll, 1);
return false;
}
return true;
}
static bool CompareSpeed() {
bool result;
//
// 8 bytes alloc/free
//
result = DoTest<Malloc8Bytes>("malloc: Malloc8Bytes() loop",
Malloc8Bytes(malloc, free))();
if (!result) return false;
result = DoTest<Malloc8Bytes>("nmalloc: Malloc8Bytes() loop",
Malloc8Bytes(nmalloc, nfree))();
if (!result) return false;
if (!ClearCache()) return false;
//
// fragmentation
//
result = DoTest<MallocFragmentation>("malloc: MallocFragmentation() loop",
MallocFragmentation(malloc, free))();
if (!result) return false;
result = DoTest<MallocFragmentation>("nmalloc: MallocFragmentation() loop",
MallocFragmentation(nmalloc, nfree))();
if (!result) return false;
if (!ClearCache()) return false;
//
// realloc
//
result = DoTest<MallocRealloc>("malloc: realloc() loop",
MallocRealloc(realloc, free))();
if (!result) return false;
result = DoTest<MallocRealloc>("nmalloc: realloc() loop",
MallocRealloc(nrealloc, nfree))();
if (!result) return false;
if (!ClearCache()) return false;
//
// multi-thread malloc/free small mem
//
result = DoTest<MallocMt<MtSmallMem> >(
"malloc: mt-small loop",
MallocMt<MtSmallMem>(MtSmallMem(malloc, free)))();
if (!result) return false;
result = DoTest<MallocMt<MtSmallMem> >(
"nmalloc: mt-small loop",
MallocMt<MtSmallMem>(MtSmallMem(nmalloc, nfree)))();
if (!result) return false;
if (!ClearCache()) return false;
//
// multi-thread malloc/free big mem
//
result = DoTest<MallocMt<MtLargeMem> >(
"malloc: mt-large loop",
MallocMt<MtLargeMem>(MtLargeMem(malloc, free)))();
if (!result) return false;
result = DoTest<MallocMt<MtLargeMem> >(
"nmalloc: mt-large loop",
MallocMt<MtLargeMem>(MtLargeMem(nmalloc, nfree)))();
if (!result) return false;
if (!ClearCache()) return false;
//
// compare mutex_lock/mutex_unlock loop and nmalloc/nfree loop
//
result = DoTest<MutexLoop>("nlib_mutex: lock/unlock loop", MutexLoop())();
result = DoTest<NmallocLoop>("nmalloc: nmalloc/nfree loop", NmallocLoop())();
return true;
}
#ifdef NLIB_HAS_VIRTUALMEMORY
extern "C" void nmalloc_get_settings(NMallocSettings* settings) {
settings->addr = NULL;
settings->size = 1024 * 1024 * 10;
settings->heap_option = 0;
}
#else
const size_t heapmem_size = 1024 * 1024 * 10;
NLIB_ALIGNAS(4096) static char heapmem[heapmem_size];
extern "C" void nmalloc_get_settings(NMallocSettings* settings) {
settings->addr = heapmem;
settings->size = heapmem_size;
settings->heap_option = 0;
}
#endif
static bool SampleMain(int, char**) { return CompareSpeed(); }
NLIB_MAINFUNC