-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #60 from codrutiftode/libmpeff-first-benchmarks
Libmpeff first benchmarks
- Loading branch information
Showing
10 changed files
with
661 additions
and
8 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,56 @@ | ||
test: | ||
# TODO | ||
LIBMPEFF := /home/ubuntu/libmprompt | ||
LIBMPEFF_H := $(LIBMPEFF)/include | ||
LIBMPEFF_LIB := $(LIBMPEFF)/out/release | ||
|
||
CC := clang-14 | ||
CC_COMPILE_FLAGS := -std=gnu99 -O3 -I$(LIBMPEFF_H) -flto | ||
CC_WARN_FLAGS := -Wall -Wextra \ | ||
-Wformat=2 -Wno-unused-parameter -Wshadow \ | ||
-Wwrite-strings \ | ||
-Wredundant-decls -Wnested-externs -Wmissing-include-dirs | ||
CC_LINK_FLAGS := -std=gnu99 -O3 -flto | ||
|
||
LD_FLAGS := -l:libmpeff.a -l:libmprompt.a -L$(LIBMPEFF_LIB) | ||
|
||
compile_cmd = $(CC) $(CC_COMPILE_FLAGS) $(CC_WARN_FLAGS) -o main.o -c main.c | ||
link_cmd = $(CC) $(CC_LINK_FLAGS) -o main main.o $(LD_FLAGS) | ||
build_cmd = cd $(1) ; $(call compile_cmd, $(1)); $(call link_cmd, $(1)) | ||
test_cmd = cd $(1) ; ./main $(2) > actual ; echo $(3) > expected ; diff expected actual | ||
|
||
bench: build | ||
hyperfine --export-csv results.csv \ | ||
'./countdown/main 200000000' \ | ||
'./fibonacci_recursive/main 42' \ | ||
'./product_early/main 100000' \ | ||
'./iterator/main 40000000' \ | ||
'./generator/main 25' \ | ||
'./parsing_dollars/main 20000' \ | ||
'./resume_nontail/main 20000' \ | ||
'./handler_sieve/main 60000' | ||
|
||
test: build | ||
$(call test_cmd, countdown, 5, 0) | ||
$(call test_cmd, fibonacci_recursive, 5, 5) | ||
$(call test_cmd, product_early, 5, 0) | ||
$(call test_cmd, iterator, 5, 15) | ||
$(call test_cmd, generator, 5, 57) | ||
$(call test_cmd, parsing_dollars, 10, 55) | ||
$(call test_cmd, resume_nontail, 5, 37) | ||
$(call test_cmd, handler_sieve, 10, 17) | ||
|
||
build: | ||
$(call build_cmd, countdown) | ||
$(call build_cmd, fibonacci_recursive) | ||
$(call build_cmd, product_early) | ||
$(call build_cmd, iterator) | ||
$(call build_cmd, generator) | ||
$(call build_cmd, parsing_dollars) | ||
$(call build_cmd, resume_nontail) | ||
$(call build_cmd, handler_sieve) | ||
|
||
clean: | ||
-rm */main | ||
-rm */expected | ||
-rm */actual | ||
-rm */*.o | ||
-rm */*.out |
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,37 @@ | ||
#include "mpeff.h" | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
|
||
MPE_DEFINE_EFFECT2(state, get, put) | ||
MPE_DEFINE_OP0(state, get, long) | ||
MPE_DEFINE_VOIDOP1(state, put, long) | ||
|
||
static void* _state_get(mpe_resume_t* r, void* local, void* arg) { | ||
return mpe_resume_tail(r, local, local); | ||
} | ||
|
||
static void* _state_put(mpe_resume_t* r, void* local, void* arg) { | ||
return mpe_resume_tail(r, arg, NULL); | ||
} | ||
|
||
static const mpe_handlerdef_t state_hdef = { MPE_EFFECT(state), NULL, { | ||
{ MPE_OP_TAIL_NOOP, MPE_OPTAG(state,get), &_state_get }, | ||
{ MPE_OP_TAIL_NOOP, MPE_OPTAG(state,put), &_state_put }, | ||
{ MPE_OP_NULL, mpe_op_null, NULL } | ||
}}; | ||
|
||
static void* countdown(void* parameter) { | ||
long state = state_get(); | ||
while (state > 0) { | ||
state_put(state - 1); | ||
state = state_get(); | ||
} | ||
return mpe_voidp_long(state); | ||
} | ||
|
||
int main(int argc, char** argv) { | ||
int64_t n = argc != 2 ? 5 : atoi(argv[1]); | ||
int64_t result = mpe_long_voidp(mpe_handle(&state_hdef, mpe_voidp_long(n), &countdown, NULL)); | ||
printf("%ld\n", result); | ||
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,20 @@ | ||
#include "mpeff.h" | ||
#include <stdlib.h> | ||
#include <stdio.h> | ||
|
||
int64_t fib(int n) { | ||
if (n == 0) return 0; | ||
if (n == 1) return 1; | ||
return fib(n - 1) + fib(n - 2); | ||
} | ||
|
||
int main(int argc, char** argv) { | ||
int64_t n = argc != 2 ? 5 : atoi(argv[1]); | ||
int64_t result = fib(n); | ||
|
||
// Increase output buffer size to increase performance | ||
char buffer[8192]; | ||
setvbuf(stdout, buffer, _IOFBF, sizeof(buffer)); | ||
printf("%ld\n", result); | ||
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,122 @@ | ||
#include "mpeff.h" | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
|
||
MPE_DEFINE_EFFECT1(yield, yield) | ||
MPE_DEFINE_VOIDOP1(yield, yield, long) | ||
|
||
// Tree data structure | ||
struct node_t { | ||
struct node_t* l; | ||
int64_t v; | ||
struct node_t* r; | ||
}; | ||
typedef struct node_t node_t; | ||
typedef struct node_t* tree_t; | ||
|
||
// Allocate and free a tree node | ||
static inline node_t* alloc_node() { return malloc(sizeof(node_t));} | ||
static inline void free_node(node_t* node) { free(node); } | ||
|
||
// Generator data structure | ||
struct thunk_t { | ||
int64_t v; | ||
mpe_resume_t *k; | ||
}; | ||
typedef struct thunk_t thunk_t; | ||
typedef thunk_t generator_t; | ||
|
||
// Allocate and free a thunk | ||
static inline thunk_t* alloc_thunk() { return malloc(sizeof(thunk_t));} | ||
static inline void free_thunk(thunk_t* thunk) { free(thunk); } | ||
|
||
// Allocate a tree on the heap | ||
static tree_t makeTree(int n) { | ||
if (n == 0) return NULL; | ||
tree_t node = alloc_node(); | ||
tree_t t = makeTree (n - 1); | ||
|
||
// Note: both left and right pointers point to the same memory, | ||
// to match the other languages | ||
node->l = t; | ||
node->r = t; | ||
node->v = n; | ||
return node; | ||
} | ||
|
||
// Free tree from heap | ||
static void free_tree(tree_t tree) { | ||
if (tree == NULL) return; | ||
free_tree(tree->l); | ||
free_node(tree); | ||
} | ||
|
||
static void* handler_yield(mpe_resume_t* r, void* local, void* arg) { | ||
thunk_t* thunk = alloc_thunk(); | ||
thunk->v = mpe_long_voidp(arg); | ||
thunk->k = r; | ||
return mpe_voidp_ptr(thunk); | ||
} | ||
|
||
static void* handler_result(void* local, void* arg) { | ||
thunk_t* thunk = alloc_thunk(); | ||
thunk->v = mpe_long_voidp(arg); | ||
thunk->k = NULL; | ||
return mpe_voidp_ptr(thunk); | ||
} | ||
|
||
static const mpe_handlerdef_t yield_hdef = { | ||
MPE_EFFECT(yield), &handler_result, { | ||
{ MPE_OP_ONCE, MPE_OPTAG(yield,yield), &handler_yield }, | ||
{ MPE_OP_NULL, mpe_op_null, NULL } | ||
}}; | ||
|
||
static inline thunk_t* resume_yield(thunk_t* t) { | ||
mpe_resume_t* k = t->k; | ||
free_thunk(t); | ||
return (thunk_t*) mpe_resume(k, NULL, NULL); | ||
} | ||
|
||
// Performs yield for every node in tree | ||
static void* iterate(void* parameter) { | ||
if (parameter == NULL) return NULL; | ||
tree_t tree = parameter; | ||
iterate(tree->l); | ||
yield_yield(tree->v); | ||
iterate(tree->r); | ||
return NULL; | ||
} | ||
|
||
// Sums all values generated | ||
static int64_t sum(generator_t* generator) { | ||
int64_t acc = 0; | ||
while (generator->k != NULL) { | ||
acc += generator->v; | ||
generator = resume_yield(generator); | ||
} | ||
free_thunk(generator); | ||
return acc; | ||
} | ||
|
||
static int64_t run(int64_t n) { | ||
tree_t tree = makeTree(n); | ||
generator_t* generator = mpe_handle(&yield_hdef, NULL, &iterate, tree); | ||
|
||
// Sum generated values | ||
int64_t result = sum(generator); | ||
|
||
// Free memory | ||
free_tree(tree); | ||
return result; | ||
} | ||
|
||
int main(int argc, char** argv) { | ||
int64_t n = argc != 2 ? 25 : atoi(argv[1]); | ||
int64_t r = run(n); | ||
|
||
// Increase size of output buffer for performance | ||
char buffer[8192]; | ||
setvbuf(stdout, buffer, _IOFBF, sizeof(buffer)); | ||
printf("%ld\n", r); | ||
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,84 @@ | ||
#include "mpeff.h" | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
|
||
MPE_DEFINE_EFFECT1(sieve, prime) | ||
MPE_DEFINE_OP1(sieve, prime, bool, long) | ||
|
||
typedef struct primes_params_t { | ||
int64_t i; | ||
int64_t n; | ||
int64_t a; | ||
} primes_params_t; | ||
|
||
static void* handle_sieve_prime(mpe_resume_t* r, void* local, void* arg) { | ||
bool res; | ||
if (mpe_long_voidp(arg) % mpe_long_voidp(local) == 0) res = false; | ||
else res = mpe_bool_voidp(sieve_prime(mpe_long_voidp(arg))); | ||
return mpe_resume_tail(r, local, mpe_voidp_bool(res)); | ||
} | ||
|
||
static void* handle_top_sieve_prime(mpe_resume_t* r, void* local, void* arg) { | ||
return mpe_resume_tail(r, local, mpe_voidp_bool(true)); | ||
} | ||
|
||
// NOTE: We exepected to be able to use MPE_OP_TAIL here, but when used we | ||
// enter an infinite loop of the same handler handling the next emitted prime | ||
// effect instead of the next one up the stack. | ||
// See https://github.com/effect-handlers/effect-handlers-bench/pull/60#issuecomment-2167645757 | ||
static const mpe_handlerdef_t sieve_hdef = { | ||
MPE_EFFECT(sieve), NULL, | ||
{ | ||
{ MPE_OP_SCOPED_ONCE, MPE_OPTAG(sieve,prime), &handle_sieve_prime }, | ||
{ MPE_OP_NULL, mpe_op_null, NULL } | ||
} | ||
}; | ||
|
||
static const mpe_handlerdef_t top_sieve_hdef = { | ||
MPE_EFFECT(sieve), NULL, | ||
{ | ||
{ MPE_OP_SCOPED_ONCE, MPE_OPTAG(sieve,prime), &handle_top_sieve_prime }, | ||
{ MPE_OP_NULL, mpe_op_null, NULL } | ||
} | ||
}; | ||
|
||
static void* primes(void* parameter) { | ||
primes_params_t* params = (primes_params_t*) mpe_voidp_ptr(parameter); | ||
if (params->i >= params->n) return mpe_voidp_long(params->a); | ||
|
||
int64_t i = params->i; | ||
if (sieve_prime(params->i)) { | ||
params->a += i; | ||
params->i++; | ||
return mpe_handle(&sieve_hdef, | ||
mpe_voidp_long(i), | ||
&primes, | ||
mpe_voidp_ptr(params)); | ||
} | ||
else { | ||
params->i++; | ||
return primes(mpe_voidp_ptr(params)); | ||
} | ||
} | ||
|
||
static int64_t run(int64_t n) { | ||
primes_params_t params = { | ||
.i = 2, | ||
.n = n, | ||
.a = 0 | ||
}; | ||
return mpe_long_voidp( | ||
mpe_handle(&top_sieve_hdef, NULL, &primes, mpe_voidp_ptr(¶ms)) | ||
); | ||
} | ||
|
||
int main(int argc, char** argv) { | ||
int64_t n = argc != 2 ? 10 : atoi(argv[1]); | ||
int64_t r = run(n); | ||
|
||
// Increase output buffer size to increase performance | ||
char buffer[8192]; | ||
setvbuf(stdout, buffer, _IOFBF, sizeof(buffer)); | ||
printf("%ld\n", r); | ||
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,59 @@ | ||
#include "mpeff.h" | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
|
||
MPE_DEFINE_EFFECT1(iterator, emit); | ||
MPE_DEFINE_VOIDOP1(iterator, emit, long); | ||
|
||
static void* handle_iterator_emit(mpe_resume_t* r, void* local, void* arg) { | ||
long result = mpe_long_voidp(local) + mpe_long_voidp(arg); | ||
return mpe_resume_tail(r, mpe_voidp_long(result), NULL); | ||
} | ||
|
||
static void* handle_iterator_result(void* local, void* arg) { | ||
return local; | ||
} | ||
|
||
static const mpe_handlerdef_t iterator_hdef = { | ||
MPE_EFFECT(iterator), | ||
&handle_iterator_result, | ||
{ | ||
{ MPE_OP_TAIL_NOOP, MPE_OPTAG(iterator,emit), &handle_iterator_emit }, | ||
{ MPE_OP_NULL, mpe_op_null, NULL } | ||
} | ||
}; | ||
|
||
struct range_args { | ||
int64_t l; | ||
int64_t u; | ||
}; | ||
|
||
static void* range(void* args) { | ||
struct range_args* range_args = mpe_ptr_voidp(args); | ||
for (int l = range_args->l; l <= range_args->u; l++) { | ||
iterator_emit(l); | ||
} | ||
return NULL; | ||
} | ||
|
||
static int64_t run(int64_t n) { | ||
struct range_args args = { | ||
.l = 0, | ||
.u = n | ||
}; | ||
return mpe_long_voidp(mpe_handle(&iterator_hdef, | ||
mpe_voidp_long(0), | ||
&range, | ||
mpe_voidp_ptr(&args))); | ||
} | ||
|
||
int main(int argc, char** argv) { | ||
int64_t n = argc != 2 ? 5 : atoi(argv[1]); | ||
int64_t r = run(n); | ||
|
||
// Increase output buffer size to increase performance | ||
char buffer[8192]; | ||
setvbuf(stdout, buffer, _IOFBF, sizeof(buffer)); | ||
printf("%ld\n", r); | ||
return 0; | ||
} |
Oops, something went wrong.