Skip to content

Commit

Permalink
Remove size classes, better precision <4096, ignore above
Browse files Browse the repository at this point in the history
  • Loading branch information
nicovank committed Oct 22, 2023
1 parent 2049ed2 commit b812be1
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 74 deletions.
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ include(FetchContent)

project(litterer)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

find_package(Threads REQUIRED)
Expand Down
69 changes: 39 additions & 30 deletions src/detector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,13 @@

#include <mimalloc.h>

#ifndef SIZE_CLASSES
// See http://jemalloc.net/jemalloc.3.html, up to 64MiB.
#define JEMALLOC_SIZE_CLASSES \
{ \
8, 16, 32, 48, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 640, 768, 896, 1024, 1280, 1536, \
1792, 2048, 2560, 3072, 3584, 4096, 5120, 6144, 7168, 8192, 10240, 12288, 14336, 16384, 20480, 24576, \
28672, 32768, 40960, 49152, 57344, 65536, 81920, 98304, 114688, 131072, 163840, 196608, 229376, 262144, \
327680, 393216, 458752, 524288, 655360, 786432, 917504, 1048576, 1310720, 1572864, 1835008, 2097152, \
2621440, 3145728, 3670016, 4194304, 5242880, 6291456, 7340032, 8388608, 10485760, 12582912, 14680064, \
16777216, 20971520, 25165824, 29360128, 33554432, 41943040, 50331648, 58720256, 67108864 \
}
#define SIZE_CLASSES JEMALLOC_SIZE_CLASSES
#endif
#define PAGE_SIZE 4096zu

namespace {
static std::atomic_bool ready{false};
static thread_local int busy{0};

static const std::vector<std::size_t> sizeClasses = SIZE_CLASSES;
static std::vector<std::atomic_int> bins(sizeClasses.size() + 1);
static std::vector<std::atomic_int> bins(PAGE_SIZE);

static std::atomic_uint64_t nAllocations{0};
static std::atomic<double> average{0};
Expand All @@ -35,20 +22,23 @@ static std::atomic_int64_t maxLiveAllocations{0};

template <bool addToTotal>
void processAllocation(std::size_t size) {
// This only does not mess with the statistics because we ignore malloc(0)
// and free(nullptr).
if (size == 0) {
return;
}

// Update average.
average = average + (size - average) / (nAllocations + 1);
nAllocations++;

// Increment histogram entry.
std::size_t index = 0;
while (size >= sizeClasses[index] && index < sizeClasses.size()) {
++index;
}
const std::size_t index = std::min(size, PAGE_SIZE) - 1;
bins[index]++;

if constexpr (addToTotal) {
// Increment total live allocations and possibly update maximum.
std::int64_t liveAllocationsSnapshot = liveAllocations.fetch_add(1) + 1;
const std::int64_t liveAllocationsSnapshot = liveAllocations.fetch_add(1) + 1;
std::int64_t maxLiveAllocationsSnapshot = maxLiveAllocations;
while (liveAllocationsSnapshot > maxLiveAllocationsSnapshot) {
maxLiveAllocations.compare_exchange_weak(maxLiveAllocationsSnapshot, liveAllocationsSnapshot);
Expand All @@ -70,16 +60,6 @@ class Initialization {

outputFile << "{" << std::endl;

if (sizeClasses.size() == 0) {
outputFile << "\t\"SizeClasses\": []," << std::endl;
} else {
outputFile << "\t\"SizeClasses\": [ " << sizeClasses[0];
for (std::size_t i = 1; i < sizeClasses.size(); ++i) {
outputFile << ", " << sizeClasses[i];
}
outputFile << " ]," << std::endl;
}

outputFile << "\t\"Bins\": [ " << bins[0];
for (std::size_t i = 1; i < bins.size(); ++i) {
outputFile << ", " << bins[i];
Expand All @@ -96,6 +76,10 @@ static Initialization _;
} // namespace

extern "C" void* malloc(std::size_t size) {
if (size == 0) {
return nullptr;
}

void* pointer = mi_malloc(size);

if (!busy && ready) {
Expand All @@ -108,6 +92,10 @@ extern "C" void* malloc(std::size_t size) {
}

extern "C" void free(void* pointer) {
if (pointer == nullptr) {
return;
}

if (!busy && ready) {
++busy;
liveAllocations--;
Expand All @@ -118,6 +106,10 @@ extern "C" void free(void* pointer) {
}

extern "C" void* calloc(std::size_t nmemb, std::size_t size) {
if (nmemb == 0 || size == 0) {
return nullptr;
}

void* pointer = mi_calloc(nmemb, size);

if (!busy && ready) {
Expand All @@ -132,6 +124,10 @@ extern "C" void* calloc(std::size_t nmemb, std::size_t size) {
extern "C" void* realloc(void* ptr, std::size_t size) {
void* pointer = mi_realloc(ptr, size);

if (size == 0) {
return nullptr;
}

if (!busy && ready) {
++busy;
processAllocation<false>(size);
Expand All @@ -144,6 +140,10 @@ extern "C" void* realloc(void* ptr, std::size_t size) {
extern "C" void* reallocarray(void* ptr, std::size_t nmemb, std::size_t size) {
void* pointer = mi_reallocarray(ptr, nmemb, size);

if (nmemb == 0 || size == 0) {
return nullptr;
}

if (!busy && ready) {
++busy;
processAllocation<false>(nmemb * size);
Expand All @@ -154,6 +154,11 @@ extern "C" void* reallocarray(void* ptr, std::size_t nmemb, std::size_t size) {
}

extern "C" int posix_memalign(void** memptr, std::size_t alignment, std::size_t size) {
if (size == 0) {
*memptr = nullptr;
return 0;
}

int result = mi_posix_memalign(memptr, alignment, size);

if (!busy && ready) {
Expand All @@ -166,6 +171,10 @@ extern "C" int posix_memalign(void** memptr, std::size_t alignment, std::size_t
}

extern "C" void* aligned_alloc(std::size_t alignment, std::size_t size) {
if (size == 0) {
return nullptr;
}

void* pointer = mi_aligned_alloc(alignment, size);

if (!busy && ready) {
Expand Down
82 changes: 39 additions & 43 deletions src/litterer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,59 +126,53 @@ void runLitterer() {
fprintf(log, "timestamp : %s %s\n", __DATE__, __TIME__);
fprintf(log, "==================================================================================\n");

if (data["Bins"].empty()) {
fprintf(log, "[WARNING] No allocations were recorded, hence littering is not possible.\n");
} else {
if (data["Bins"][data["SizeClasses"].size()].get<int>() != 0) {
fprintf(log, "[WARNING] Allocations of size greater than the maximum size class were recorded.\n");
fprintf(log, "[WARNING] There will be no littering for these allocations.\n");
fprintf(log, "[WARNING] This represents %.2f of all allocations recorded.\n",
(static_cast<double>(data["Bins"][data["SizeClasses"].size()]) / nAllocations) * 100);
}

const auto litterStart = std::chrono::high_resolution_clock::now();
assertOrExit(
[&] {
std::size_t sum = 0;
for (const auto& bin : data["Bins"]) {
sum += bin.get<std::size_t>();
}
return sum;
}() == nAllocations,
log, "The sum of all bins should equal nAllocations.");

std::uniform_int_distribution<std::size_t> distribution(
0, nAllocations - data["Bins"][data["SizeClasses"].size()].get<int>() - 1);
std::vector<void*> objects = *(new std::vector<void*>);
objects.reserve(nAllocationsLitter);
const auto litterStart = std::chrono::high_resolution_clock::now();

for (std::size_t i = 0; i < nAllocationsLitter; ++i) {
std::size_t minAllocationSize = 0;
std::size_t sizeClassIndex = 0;
std::int64_t offset = static_cast<int64_t>(distribution(generator)) - data["Bins"][0].get<std::int64_t>();
std::uniform_int_distribution<std::size_t> distribution(0, nAllocations - 1);
std::vector<void*> objects = *(new std::vector<void*>);
objects.reserve(nAllocationsLitter);

while (offset >= 0) {
minAllocationSize = data["SizeClasses"][sizeClassIndex].get<std::size_t>();
++sizeClassIndex;
offset -= static_cast<std::size_t>(data["Bins"][sizeClassIndex].get<int>());
}
const std::size_t maxAllocationSize = data["SizeClasses"][sizeClassIndex].get<std::size_t>() - 1;
std::uniform_int_distribution<std::size_t> allocationSizeDistribution(minAllocationSize, maxAllocationSize);
const std::size_t allocationSize = allocationSizeDistribution(generator);
for (std::size_t i = 0; i < nAllocationsLitter; ++i) {
std::size_t allocationSize = 1;
std::int64_t offset = static_cast<int64_t>(distribution(generator)) - data["Bins"][0].get<std::size_t>();

void* pointer = MALLOC(allocationSize);
objects.push_back(pointer);
while (offset >= 0) {
++allocationSize;
offset -= static_cast<std::size_t>(data["Bins"][allocationSize - 1].get<std::size_t>());
}

const std::size_t nObjectsToBeFreed = static_cast<std::size_t>((1 - occupancy) * nAllocationsLitter);
void* pointer = MALLOC(allocationSize);
objects.push_back(pointer);
}

if (shuffle) {
fprintf(log, "Shuffling %zu object(s) to be freed.\n", nObjectsToBeFreed);
partial_shuffle(objects, nObjectsToBeFreed, generator);
} else {
std::sort(objects.begin(), objects.end(), std::greater<void*>());
}
const std::size_t nObjectsToBeFreed = static_cast<std::size_t>((1 - occupancy) * nAllocationsLitter);

for (std::size_t i = 0; i < nObjectsToBeFreed; ++i) {
FREE(objects[i]);
}
if (shuffle) {
fprintf(log, "Shuffling %zu object(s) to be freed.\n", nObjectsToBeFreed);
partial_shuffle(objects, nObjectsToBeFreed, generator);
} else {
// TODO: We should maybe make this a third-option.
std::sort(objects.begin(), objects.end(), std::greater<void*>());
}

const auto litterEnd = std::chrono::high_resolution_clock::now();
const auto elapsed = std::chrono::duration_cast<std::chrono::seconds>((litterEnd - litterStart));
fprintf(log, "Finished littering. Time taken: %s seconds.\n", std::to_string(elapsed.count()).c_str());
for (std::size_t i = 0; i < nObjectsToBeFreed; ++i) {
FREE(objects[i]);
}

const auto litterEnd = std::chrono::high_resolution_clock::now();
const auto elapsed = std::chrono::duration_cast<std::chrono::seconds>((litterEnd - litterStart));
fprintf(log, "Finished littering. Time taken: %s seconds.\n", std::to_string(elapsed.count()).c_str());

if (sleepDelay) {
#ifdef _WIN32
const auto pid = GetCurrentProcessId();
Expand All @@ -191,5 +185,7 @@ void runLitterer() {
}

fprintf(log, "==================================================================================\n");
fclose(log);
if (log != stderr) {
fclose(log);
}
}

0 comments on commit b812be1

Please sign in to comment.