-
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Benchmarked memory models for Toy_Array, read more
The results can be found in 'tests/benchmarks/array_allocation/results.md' The results are disappointing, as 'malloc()' is simply faster in every possible situation compared to my custom arena allocator.
- Loading branch information
1 parent
8b5cc3b
commit 223db84
Showing
8 changed files
with
294 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#include "toy.h" | ||
|
||
//util macros | ||
#define TOY_ARRAY_EXPAND(array) ((array) = ((array) != NULL && (array)->count + 1 > (array)->capacity ? Toy_resizeArray((array), (array)->capacity * TOY_ARRAY_EXPANSION_RATE) : (array))) | ||
#define TOY_ARRAY_PUSHBACK(array, value) (TOY_ARRAY_EXPAND(array), (array)->data[(array)->count++] = (value)) | ||
|
||
void stress_fillArray(Toy_Array** array) { | ||
//Toy_Value is either 8 or 16 bytes | ||
for (int i = 0; i < 10 * 1000 * 1000; i++) { | ||
TOY_ARRAY_PUSHBACK(*array, TOY_VALUE_FROM_INTEGER(i)); | ||
} | ||
} | ||
|
||
int main() { | ||
//Compare different memory strategies for Toy_Array | ||
|
||
for (int i = 0; i < 100; i++) { | ||
/* | ||
//malloc | ||
Toy_Array* array = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY); | ||
stress_fillArray(&array); | ||
Toy_resizeArray(array, 0); | ||
/*/ | ||
|
||
//Toy_Bucket | ||
benchBucket = Toy_allocateBucket(1024 * 1024 * 200); //200MB | ||
|
||
Toy_Array* array = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY); | ||
stress_fillArray(&array); | ||
Toy_resizeArray(array, 0); | ||
|
||
Toy_freeBucket(&benchBucket); | ||
|
||
//*/ | ||
} | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
#compiler settings | ||
CC=gcc | ||
CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 | ||
LIBS+=-lm | ||
LDFLAGS+= | ||
|
||
ifeq ($(shell uname),Linux) | ||
LDFLAGS=-Wl,--gc-sections | ||
else ifeq ($(OS),Windows_NT) | ||
LDFLAGS=-Wl,--gc-sections | ||
else ifeq ($(shell uname),Darwin) | ||
LDFLAGS=-Wl,-dead_strip | ||
else | ||
@echo "LDFLAGS set failed - what platform is this?" | ||
endif | ||
|
||
#patched incl | ||
TOY_SOURCEDIR=source | ||
|
||
#directories | ||
TEST_ROOTDIR=../../.. | ||
TEST_SOURCEDIR=$(TEST_ROOTDIR)/$(TOY_SOURCEDIR) | ||
TEST_CASESDIR=. | ||
|
||
TEST_OUTDIR=out | ||
TEST_OBJDIR=obj | ||
|
||
#file names | ||
TEST_SOURCEFILES=$(wildcard $(TEST_SOURCEDIR)/*.c) | ||
TEST_CASESFILES=$(wildcard $(TEST_CASESDIR)/bench_*.c) | ||
|
||
#build the object files, compile the test cases, and run | ||
all: clean | ||
$(MAKE) build-source | ||
$(MAKE) build-cases | ||
$(MAKE) build-link | ||
$(MAKE) build-run | ||
|
||
#targets for each step | ||
.PHONY: build-source | ||
build-source: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_SOURCEFILES:.c=.o))) | ||
|
||
.PHONY: build-cases | ||
build-cases: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_CASESFILES:.c=.o))) | ||
|
||
.PHONY: build-link | ||
build-link: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe))) | ||
|
||
.PHONY: build-run | ||
build-run: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.run))) | ||
|
||
#compilation steps | ||
$(TEST_OBJDIR)/%.o: $(TEST_SOURCEDIR)/%.c | ||
$(CC) -c -o $@ $< $(addprefix -I,$(TEST_SOURCEDIR)) $(CFLAGS) -fdata-sections -ffunction-sections | ||
|
||
$(TEST_OBJDIR)/%.o: $(TEST_CASESDIR)/%.c | ||
$(CC) -c -o $@ $< $(addprefix -I,$(TEST_SOURCEDIR) $(TEST_CASESDIR)) $(CFLAGS) -fdata-sections -ffunction-sections | ||
|
||
$(TEST_OUTDIR)/%.exe: $(TEST_OBJDIR)/%.o | ||
@$(CC) -o $@ $< $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_SOURCEFILES:.c=.o))) $(CFLAGS) $(LIBS) $(LDFLAGS) | ||
strip $@ | ||
|
||
.PRECIOUS: $(TEST_OUTDIR)/%.run | ||
$(TEST_OUTDIR)/%.run: $(TEST_OUTDIR)/%.exe | ||
@/usr/bin/time --format "User System\n%U %E" $< | ||
|
||
#util targets | ||
$(TEST_OUTDIR): | ||
mkdir $(TEST_OUTDIR) | ||
|
||
$(TEST_OBJDIR): | ||
mkdir $(TEST_OBJDIR) | ||
|
||
#util commands | ||
.PHONY: clean | ||
clean: | ||
ifeq ($(shell uname),Linux) | ||
find . -type f -name '*.o' -delete | ||
find . -type f -name '*.a' -delete | ||
find . -type f -name '*.exe' -delete | ||
find . -type f -name '*.dll' -delete | ||
find . -type f -name '*.lib' -delete | ||
find . -type f -name '*.so' -delete | ||
find . -type f -name '*.dylib' -delete | ||
find . -type d -name 'out' -delete | ||
find . -type d -name 'obj' -delete | ||
else ifeq ($(OS),Windows_NT) | ||
$(RM) *.o *.a *.exe *.dll *.lib *.so *.dylib | ||
$(RM) out | ||
$(RM) obj | ||
else ifeq ($(shell uname),Darwin) | ||
find . -type f -name '*.o' -delete | ||
find . -type f -name '*.a' -delete | ||
find . -type f -name '*.exe' -delete | ||
find . -type f -name '*.dll' -delete | ||
find . -type f -name '*.lib' -delete | ||
find . -type f -name '*.so' -delete | ||
find . -type f -name '*.dylib' -delete | ||
find . -type d -name 'out' -delete | ||
find . -type d -name 'obj' -delete | ||
else | ||
@echo "Deletion failed - what platform is this?" | ||
endif | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
|
||
These tests compared two memory solutions for `Toy_Array`, under different conditions. 'Pushes' is the number of iterations used in the following stress function: | ||
|
||
```c | ||
//defined in toy_value.h | ||
#define TOY_VALUE_FROM_INTEGER(value) ((Toy_Value){{ .integer = value }, TOY_VALUE_INTEGER}) | ||
|
||
//util macros | ||
#define TOY_ARRAY_EXPAND(array) ((array) = ((array) != NULL && (array)->count + 1 > (array)->capacity ? Toy_resizeArray((array), (array)->capacity * TOY_ARRAY_EXPANSION_RATE) : (array))) | ||
#define TOY_ARRAY_PUSHBACK(array, value) (TOY_ARRAY_EXPAND(array), (array)->data[(array)->count++] = (value)) | ||
|
||
void stress_fillArray(Toy_Array** array) { | ||
//Toy_Value is either 8 or 16 bytes | ||
for (int i = 0; i < 10 * 1000 * 1000; i++) { | ||
TOY_ARRAY_PUSHBACK(*array, TOY_VALUE_FROM_INTEGER(i)); | ||
} | ||
} | ||
``` | ||
'Memory' is the capacity of the `Toy_Bucket` when used. In the first set of results, the stress function was called once, while the second set called it 100 times, clearing the memory entirely each time. 'malloc' and 'bucket' shows the measured time taken for each situation. | ||
``` | ||
1x run | ||
|
||
pushes: 1000 * 1000 | ||
memory: 1024 * 1024 * 20 | ||
malloc: 0.01 0:00.01 | ||
bucket: 0.00 0:00.02 | ||
|
||
pushes: 10 * 1000 * 1000 | ||
memory: 1024 * 1024 * 200 | ||
malloc: 0.08 0:00.14 | ||
bucket: 0.13 0:00.29 | ||
``` | ||
``` | ||
100x looped runs | ||
|
||
pushes: 1000 * 1000 | ||
memory: 1024 * 1024 * 20 | ||
malloc: 0.94 0:01.47 | ||
bucket: 1.02 0:02.60 | ||
|
||
pushes: 10 * 1000 * 1000 | ||
memory: 1024 * 1024 * 200 | ||
malloc: 8.28 0:15.77 | ||
bucket: 11.81 0:30.06 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
#include "toy_array.h" | ||
#include "toy_console_colors.h" | ||
|
||
#include "toy_bucket.h" | ||
|
||
#include <string.h> | ||
|
||
Toy_Bucket* benchBucket = NULL; | ||
|
||
Toy_Array* Toy_resizeArray(Toy_Array* paramArray, unsigned int capacity) { | ||
//allow the array to be 'lost', and freed with the bucket | ||
if (capacity == 0) { | ||
return NULL; | ||
} | ||
|
||
//initial allocation | ||
if (paramArray == NULL) { | ||
Toy_Array* array = Toy_partitionBucket(&benchBucket, capacity * sizeof(Toy_Value) + sizeof(Toy_Array)); | ||
|
||
array->capacity = capacity; | ||
array->count = 0; | ||
|
||
return array; | ||
} | ||
|
||
//if your array is growing, partition more space, then copy over the data | ||
if (paramArray->capacity < capacity) { | ||
Toy_Array* array = Toy_partitionBucket(&benchBucket, capacity * sizeof(Toy_Value) + sizeof(Toy_Array)); | ||
|
||
memcpy(array, paramArray, paramArray->count * sizeof(Toy_Value) + sizeof(Toy_Array)); //doesn't copy any blank space | ||
|
||
array->capacity = capacity; | ||
array->count = paramArray->count; | ||
return array; | ||
} | ||
|
||
//if some values will be removed, free them first, then return the result | ||
if (paramArray->count > capacity) { | ||
for (unsigned int i = capacity; i < paramArray->count; i++) { | ||
Toy_freeValue(paramArray->data[i]); | ||
} | ||
|
||
paramArray->capacity = capacity; //don't worry about another allocation, this is faster | ||
paramArray->count = capacity; | ||
|
||
return paramArray; | ||
} | ||
|
||
//unreachable | ||
return paramArray; | ||
} | ||
|
||
/* | ||
Note: This needs to be pasted in the header: | ||
``` | ||
struct Toy_Bucket; | ||
extern struct Toy_Bucket* benchBucket; | ||
``` | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#include "toy_array.h" | ||
#include "toy_console_colors.h" | ||
|
||
#include <stdio.h> | ||
#include <stdlib.h> | ||
|
||
Toy_Array* Toy_resizeArray(Toy_Array* paramArray, unsigned int capacity) { | ||
if (capacity == 0) { | ||
free(paramArray); | ||
return NULL; | ||
} | ||
|
||
//if some values will be removed, free them first | ||
if (paramArray != NULL && paramArray->count > capacity) { | ||
for (unsigned int i = capacity; i < paramArray->count; i++) { | ||
Toy_freeValue(paramArray->data[i]); | ||
} | ||
} | ||
|
||
unsigned int originalCapacity = paramArray == NULL ? 0 : paramArray->capacity; | ||
|
||
Toy_Array* array = realloc(paramArray, capacity * sizeof(Toy_Value) + sizeof(Toy_Array)); | ||
|
||
if (array == NULL) { | ||
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to resize a 'Toy_Array' from %d to %d capacity\n" TOY_CC_RESET, (int)originalCapacity, (int)capacity); | ||
exit(-1); | ||
} | ||
|
||
array->capacity = capacity; | ||
array->count = paramArray == NULL ? 0 : | ||
(array->count > capacity ? capacity : array->count); //truncate lost data | ||
|
||
return array; | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters