Skip to content

Commit

Permalink
Merge pull request #60 from codrutiftode/libmpeff-first-benchmarks
Browse files Browse the repository at this point in the history
Libmpeff first benchmarks
  • Loading branch information
jasigal authored Jul 25, 2024
2 parents b6a0583 + 4a3659f commit 4593c4d
Show file tree
Hide file tree
Showing 10 changed files with 661 additions and 8 deletions.
58 changes: 56 additions & 2 deletions benchmarks/libmpeff/Makefile
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
37 changes: 37 additions & 0 deletions benchmarks/libmpeff/countdown/main.c
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;
}
20 changes: 20 additions & 0 deletions benchmarks/libmpeff/fibonacci_recursive/main.c
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;
}
122 changes: 122 additions & 0 deletions benchmarks/libmpeff/generator/main.c
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;
}
84 changes: 84 additions & 0 deletions benchmarks/libmpeff/handler_sieve/main.c
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(&params))
);
}

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;
}
59 changes: 59 additions & 0 deletions benchmarks/libmpeff/iterator/main.c
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;
}
Loading

0 comments on commit 4593c4d

Please sign in to comment.