diff --git a/.gitignore b/.gitignore index 3a3f02351..2f46ae79a 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,9 @@ bld*/ # Visual Studio Code .vscode/ + +# Python +**/__pycache__/ + +# C/C++ LSP +.ccls-cache/ diff --git a/benchmark/python/benchmark.py b/benchmark/python/benchmark.py new file mode 100644 index 000000000..3932f3cc3 --- /dev/null +++ b/benchmark/python/benchmark.py @@ -0,0 +1,34 @@ +from kv.kv import benchmark_kv +from object.object import benchmark_object +from object.distributed_object import benchmark_distributed_object +from db.entry import benchmark_db_entry +from db.iterator import benchmark_db_iterator +from db.schema import benchmark_db_schema +from item.collection import benchmark_collection +from item.item import benchmark_item +from benchmarkrun import print_header +from sys import argv + +if __name__ == "__main__": + runs = [] + iterations = 1000 + taregt_time = 1000 + op_duration = 2 + machine_readable = ("-m" in argv) + print_header(machine_readable) + + # KV Client + benchmark_kv(runs, iterations, machine_readable, op_duration) + + # Object Client + benchmark_distributed_object(runs, iterations, machine_readable, op_duration) + benchmark_object(runs, iterations, machine_readable, op_duration) + + # DB Client + benchmark_db_entry(runs, iterations, machine_readable, op_duration) + benchmark_db_iterator(runs, iterations, machine_readable, op_duration) + benchmark_db_schema(runs, iterations, machine_readable, op_duration) + + # Item Client + benchmark_collection(runs, iterations, machine_readable, op_duration) + benchmark_item(runs, iterations, machine_readable, op_duration) diff --git a/benchmark/python/benchmarkrun.py b/benchmark/python/benchmarkrun.py new file mode 100644 index 000000000..32ce20d03 --- /dev/null +++ b/benchmark/python/benchmarkrun.py @@ -0,0 +1,119 @@ +from time import perf_counter_ns + +class BenchmarkRun: + def __init__(self, name, iterations, machine_readable, op_duration): + self.name = name + self.iterations = iterations + self.timer_started = False + self.start = None + self.stop = None + self.break_start = None + self.total_break = 0 + self.operations = iterations + self.machine_readable = machine_readable + self.iteration_count = 0 + self.i = -1 + self.op_duration = 1000 ** 3 * op_duration + self.batch_send = None + self.batch_clean = None + self.batch_setup = None + + def __iter__(self): + self.start_timer() + return self + + def __next__(self): + self.i += 1 + if self.i == self.iterations: + self.i = 0 + self.iteration_count += 1 + if self.batch_send is not None: + if not self.batch_send(): + raise RuntimeError("Failed to execute batch!") + if self.batch_clean is not None: + self.pause_timer() + if not self.batch_clean(): + raise RuntimeError("Failed to clean batch!") + self.continue_timer() + if self.get_runtime_ns() > self.op_duration: + self.stop_timer() + raise StopIteration + if self.i == 0 and self.batch_setup is not None: + self.pause_timer() + if not self.batch_setup(): + raise RuntimeError("Failed to setup batch!"); + self.continue_timer() + return self.i + def get_count(self): + return self.iterations + def pause_timer(self): + self.break_start = perf_counter_ns() + + def continue_timer(self): + self.total_break += perf_counter_ns() - self.break_start + self.break_start = None + + def start_timer(self): + self.timer_started = True + self.start = perf_counter_ns() + + def stop_timer(self): + self.timer_started = False + self.stop = perf_counter_ns() + + def get_runtime_ms(self): + val = self.get_runtime_ns() + return val / 1000000 if val != None else None + + def get_runtime_s(self): + val = self.get_runtime_ns() + return val / 1000000000 if val != None else None + + def get_runtime_ns(self): + if self.timer_started or self.stop == None: + return perf_counter_ns() - self.start - self.total_break + else: + return self.stop - self.start - self.total_break + + def print_result(self): + if self.iteration_count == 0: self.iteration_count = 1 + if self.machine_readable: + print(f"{self.name},{self.get_runtime_s()},{self.operations}") + else: + name_col = self.name.ljust(60," ") + runtime_col = f"{self.get_runtime_s():.3f}".rjust(8," ") + " seconds" + operations_col = f"{int(float(self.operations)*self.iteration_count/self.get_runtime_s())}/s".rjust(12," ") + print(f"{name_col} | {runtime_col} | {operations_col}") + + def print_empty(self): + if self.machine_readable: + print(f"{self.name},-,-") + else: + name_col = self.name.ljust(60," ") + runtime_col = "-".rjust(8," ") + " seconds" + operations_col = "-/s".rjust(12," ") + print(f"{name_col} | {runtime_col} | {operations_col}") + +def append_to_benchmark_list_and_run(_list, run, func): + _list.append(run) + # try: + func(run) + run.print_result() + # except: + # run.print_empty() + +def print_result_table_header(): + name_col = "Name".ljust(60," ") + runtime_col = "Duration".ljust(16," ") + operations_col = f"Operations/s".rjust(12," ") + header = f"{name_col} | {runtime_col} | {operations_col}" + print(header+"\n"+len(header)*"-") + +def print_machine_readable_header(): + print("name,elapsed,operations") + +def print_header(machine_readable): + if machine_readable: + print_machine_readable_header() + else: + print_result_table_header() diff --git a/benchmark/python/db/common.py b/benchmark/python/db/common.py new file mode 100644 index 000000000..37294f88a --- /dev/null +++ b/benchmark/python/db/common.py @@ -0,0 +1,150 @@ +from julea import lib, encode, ffi + +N = 1 << 12 +N_GET_DIVIDER = N >> 8 +N_PRIME = 11971 +N_MODULUS = 256 +CLASS_MODULUS = N >> 3 +CLASS_LIMIT = CLASS_MODULUS >> 1 +SIGNED_FACTOR = N_PRIME +USIGNED_FACTOR = N_PRIME +FLOAT_FACTOR = 3.1415926 + +def _benchmark_db_prepare_scheme(namespace_encoded, use_batch, use_index_all, + use_index_single, batch, delete_batch): + b_s_error_ptr = ffi.new("GError*") + b_s_error_ptr = ffi.NULL + table_name = encode("table") + string_name = encode("string") + float_name = encode("float") + uint_name = encode("uint") + sint_name = encode("sint") + blob_name = encode("blob") + b_scheme = lib.j_db_schema_new(namespace_encoded, table_name, b_s_error_ptr) + assert b_scheme != ffi.NULL + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_schema_add_field(b_scheme, string_name, + lib.J_DB_TYPE_STRING, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_schema_add_field(b_scheme, float_name, + lib.J_DB_TYPE_FLOAT64, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_schema_add_field(b_scheme, uint_name, lib.J_DB_TYPE_UINT64, + b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_schema_add_field(b_scheme, sint_name, lib.J_DB_TYPE_UINT64, + b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_schema_add_field(b_scheme, blob_name, lib.J_DB_TYPE_BLOB, + b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + if use_index_all: + names = ffi.new("char*[5]") + names[0] = encode("string") + names[1] = encode("float") + names[2] = encode("uint") + names[3] = encode("sint") + names[4] = ffi.NULL + assert lib.j_db_schema_add_index(b_scheme, names, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + if use_index_single: + names = ffi.new("char*[2]") + names[1] = ffi.NULL + names[0] = encode("string") + assert lib.j_db_schema_add_index(b_scheme, names, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + names[0] = encode("float") + assert lib.j_db_schema_add_index(b_scheme, names, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + names[0] = encode("uint") + assert lib.j_db_schema_add_index(b_scheme, names, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + names[0] = encode("sint") + assert lib.j_db_schema_add_index(b_scheme, names, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_schema_create(b_scheme, batch, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_schema_delete(b_scheme, delete_batch, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + if not use_batch: + assert lib.j_batch_execute(batch) + return lib.j_db_schema_ref(b_scheme) + +def _benchmark_db_get_identifier(i): + return f"{i * SIGNED_FACTOR % N_MODULUS:x}-benchmark-{i}" + +def _benchmark_db_insert(run, scheme, namespace, use_batch, use_index_all, + use_index_single, use_timer): + b_scheme = None + b_s_error_ptr = ffi.new("GError*") + b_s_error_ptr = ffi.NULL + namespace_encoded = encode(namespace) + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + delete_batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + + if use_timer: + assert scheme == None + assert run != None + b_scheme = _benchmark_db_prepare_scheme(namespace_encoded, use_batch, + use_index_all, use_index_single, + batch, delete_batch) + assert b_scheme != None + run.operations = 0 + run.start_timer() + else: + assert use_batch + assert run == None + lib.j_db_schema_ref(scheme) + b_scheme = scheme + while True: + for i in range(N): + i_signed_ptr = ffi.new("long*") + i_signed_ptr[0] = ((i * SIGNED_FACTOR) % CLASS_MODULUS) - CLASS_LIMIT + i_usigned_ptr = ffi.new("unsigned long*") + i_usigned_ptr[0] = ((i * USIGNED_FACTOR) % CLASS_MODULUS) + i_float_ptr = ffi.new("double*") + i_float_ptr[0] = i_signed_ptr[0] * FLOAT_FACTOR + string = encode(_benchmark_db_get_identifier(i)) + string_name = encode("string") + float_name = encode("float") + sint_name = encode("sint") + uint_name = encode("uint") + entry = lib.j_db_entry_new(b_scheme, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_entry_set_field(entry, string_name, string, 0, + b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_entry_set_field(entry, float_name, i_float_ptr, 0, + b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_entry_set_field(entry, sint_name, i_signed_ptr, 0, + b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_entry_set_field(entry, uint_name, i_usigned_ptr, 0, + b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_entry_insert(entry, batch, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + if not use_batch: + assert lib.j_batch_execute(batch) + if use_batch or not use_timer: + lib.j_batch_execute(batch) + if use_timer: + run.pause_timer() + assert lib.j_batch_execute(delete_batch) + run.operations += N + if run.get_runtime_ns() >= run.op_duration: + run.stop_timer() + break; + else: + b_scheme = _benchmark_db_prepare_scheme(namespace_encoded, use_batch, + use_index_all, use_index_single, + batch, delete_batch) + run.continue_timer() + else: + break; + + lib.j_batch_unref(batch) + lib.j_batch_unref(delete_batch) + lib.j_db_entry_unref(entry) + lib.j_db_schema_unref(b_scheme) diff --git a/benchmark/python/db/entry.py b/benchmark/python/db/entry.py new file mode 100644 index 000000000..4c629d470 --- /dev/null +++ b/benchmark/python/db/entry.py @@ -0,0 +1,205 @@ +from benchmarkrun import BenchmarkRun, append_to_benchmark_list_and_run +from julea import lib, encode, ffi +from db.common import _benchmark_db_insert, _benchmark_db_prepare_scheme, _benchmark_db_get_identifier, N, N_GET_DIVIDER, N_PRIME, SIGNED_FACTOR, CLASS_MODULUS, CLASS_LIMIT + +def benchmark_db_entry(benchmarkrun_list, iterations, machine_readable, op_duration): + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/insert", iterations, machine_readable, op_duration), benchmark_db_insert) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/insert-batch", iterations, machine_readable, op_duration), benchmark_db_insert_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/insert-index-single", iterations, machine_readable, op_duration), benchmark_db_insert_index_single) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/insert-batch-index-single", iterations, machine_readable, op_duration), benchmark_db_insert_batch_index_single) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/insert-index-all", iterations, machine_readable, op_duration), benchmark_db_insert_index_all) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/insert-batch-index-all", iterations, machine_readable, op_duration), benchmark_db_insert_batch_index_all) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/insert-index-mixed", iterations, machine_readable, op_duration), benchmark_db_insert_index_mixed) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/insert-batch-index-mixed", iterations, machine_readable, op_duration), benchmark_db_insert_batch_index_mixed) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/delete", iterations, machine_readable, op_duration), benchmark_db_delete) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/delete-batch", iterations, machine_readable, op_duration), benchmark_db_delete_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/delete-index-single", iterations, machine_readable, op_duration), benchmark_db_delete_index_single) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/delete-batch-index-single", iterations, machine_readable, op_duration), benchmark_db_delete_batch_index_single) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/delete-index-all", iterations, machine_readable, op_duration), benchmark_db_delete_index_all) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/delete-batch-index-all", iterations, machine_readable, op_duration), benchmark_db_delete_batch_index_all) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/delete-index-mixed", iterations, machine_readable, op_duration), benchmark_db_delete_index_mixed) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/delete-batch-index-mixed", iterations, machine_readable, op_duration), benchmark_db_delete_batch_index_mixed) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/update", iterations, machine_readable, op_duration), benchmark_db_update) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/update-batch", iterations, machine_readable, op_duration), benchmark_db_update_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/update-index-single", iterations, machine_readable, op_duration), benchmark_db_update_index_single) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/update-batch-index-single", iterations, machine_readable, op_duration), benchmark_db_update_batch_index_single) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/update-index-all", iterations, machine_readable, op_duration), benchmark_db_update_index_all) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/update-batch-index-all", iterations, machine_readable, op_duration), benchmark_db_update_batch_index_all) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/update-index-mixed", iterations, machine_readable, op_duration), benchmark_db_update_index_mixed) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/entry/update-batch-index-mixed", iterations, machine_readable, op_duration), benchmark_db_update_batch_index_mixed) + +def benchmark_db_insert(run): + _benchmark_db_insert(run, None, "benchmark_insert", False, False, False, + True) + +def benchmark_db_insert_batch(run): + _benchmark_db_insert(run, None, "benchmark_insert_batch", True, False, + False, True) + +def benchmark_db_insert_index_single(run): + _benchmark_db_insert(run, None, "benchmark_insert_index_single", False, + False, True, True) + +def benchmark_db_insert_batch_index_single(run): + _benchmark_db_insert(run, None, "benchmark_insert_batch_index_single", True, + False, True, True) + +def benchmark_db_insert_index_all(run): + _benchmark_db_insert(run, None, "benchmark_insert_index_all", False, True, + False, True) + +def benchmark_db_insert_batch_index_all(run): + _benchmark_db_insert(run, None, "benchmark_insert_batch_index_all", True, + True, False, True) + +def benchmark_db_insert_index_mixed(run): + _benchmark_db_insert(run, None, "benchmakr_insert_index_mixed", False, True, + True, True) + +def benchmark_db_insert_batch_index_mixed(run): + _benchmark_db_insert(run, None, "benchmakr_insert_batch_index_mixed", True, + True, True, True) + +def benchmark_db_delete(run): + _benchmark_db_delete(run, "benchmark_delete", False, False, False) + +def benchmark_db_delete_batch(run): + _benchmark_db_delete(run, "benchmark_delete_batch", True, False, False) + +def benchmark_db_delete_index_single(run): + _benchmark_db_delete(run, "benchmark_delete_index_single", False, False, + True) + +def benchmark_db_delete_batch_index_single(run): + _benchmark_db_delete(run, "benchmark_delete_batch_index_single", True, + False, True) + +def benchmark_db_delete_index_all(run): + _benchmark_db_delete(run, "benchmark_delete_index_all", False, True, False) + +def benchmark_db_delete_batch_index_all(run): + _benchmark_db_delete(run, "benchmark_delete_batch_index_all", True, True, + False) + +def benchmark_db_delete_index_mixed(run): + _benchmark_db_delete(run, "benchmark_delete_index_mixed", False, True, True) + +def benchmark_db_delete_batch_index_mixed(run): + _benchmark_db_delete(run, "benchmark_delete_batch_index_mixed", True, True, + True) + +def _benchmark_db_delete(run, namespace, use_batch, use_index_all, + use_index_single): + namespace_encoded = encode(namespace) + b_s_error_ptr = ffi.new("GError*") + b_s_error_ptr = ffi.NULL + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + delete_batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + b_scheme = _benchmark_db_prepare_scheme(namespace_encoded, False, + use_index_all, use_index_single, + batch, delete_batch) + assert b_scheme != ffi.NULL + assert run != None + _benchmark_db_insert(None, b_scheme, "\0", True, False, False, False) + run.iterations = N if use_index_all or use_index_single else int(N / N_GET_DIVIDER) + run.oerations = run.iterations + + run.batch_setup = lambda: _benchmark_db_insert(None, b_scheme, "\0", True, False, False, False) or True + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + for i in run: + entry = lib.j_db_entry_new(b_scheme, b_s_error_ptr) + string = encode(_benchmark_db_get_identifier(i)) + string_name = encode("string") + selector = lib.j_db_selector_new(b_scheme, lib.J_DB_SELECTOR_MODE_AND, + b_s_error_ptr) + assert lib.j_db_selector_add_field(selector, string_name, + lib.J_DB_SELECTOR_OPERATOR_EQ, + string, 0, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_entry_delete(entry, selector, batch, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_db_entry_unref(entry) + lib.j_db_selector_unref(selector) + + assert lib.j_batch_execute(delete_batch) + lib.j_batch_unref(batch) + lib.j_batch_unref(delete_batch) + lib.j_db_schema_unref(b_scheme) + +def benchmark_db_update(run): + _benchmark_db_update(run, "benchmark_update", False, False, False) + +def benchmark_db_update_batch(run): + _benchmark_db_update(run, "benchmark_update_batch", True, False, False) + +def benchmark_db_update_index_single(run): + _benchmark_db_update(run, "benchmark_update_index_single", False, False, + True) + +def benchmark_db_update_batch_index_single(run): + _benchmark_db_update(run, "benchmark_update_batch_index_single", True, + False, True) + +def benchmark_db_update_index_all(run): + _benchmark_db_update(run, "benchmark_update_index_all", False, True, False) + +def benchmark_db_update_batch_index_all(run): + _benchmark_db_update(run, "benchmark_update_batch_index_all", True, True, + False) + +def benchmark_db_update_index_mixed(run): + _benchmark_db_update(run, "benchmark_update_index_mixed", False, True, True) + +def benchmark_db_update_batch_index_mixed(run): + _benchmark_db_update(run, "benchmark_update_batch_index_mixed", True, True, + True) + +def _benchmark_db_update(run, namespace, use_batch, use_index_all, + use_index_single): + namespace_encoded = encode(namespace) + b_s_error_ptr = ffi.new("GError*") + b_s_error_ptr = ffi.NULL + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + delete_batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + b_scheme = _benchmark_db_prepare_scheme(namespace_encoded, False, + use_index_all, use_index_single, + batch, delete_batch) + assert b_scheme != ffi.NULL + assert run != None + _benchmark_db_insert(None, b_scheme, "\0", True, False, False, False) + run.iterations = N if use_index_all or use_index_single else int(N / N_GET_DIVIDER) + run.operations = run.iterations + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + cnt = 0 + for i in run: + cnt += 1 + sint_name = encode("sint") + i_signed_ptr = ffi.new("long*") + i_signed_ptr[0] = (((i + N_PRIME) * SIGNED_FACTOR * cnt) & CLASS_MODULUS) - CLASS_LIMIT + selector = lib.j_db_selector_new(b_scheme, lib.J_DB_SELECTOR_MODE_AND, + b_s_error_ptr) + entry = lib.j_db_entry_new(b_scheme, b_s_error_ptr) + string_name = encode("string") + string = encode(_benchmark_db_get_identifier(i)) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_entry_set_field(entry, sint_name, i_signed_ptr, 0, + b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_selector_add_field(selector, string_name, + lib.J_DB_SELECTOR_OPERATOR_EQ, + string, 0, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_entry_update(entry, selector, batch, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_db_selector_unref(selector) + lib.j_db_entry_unref(entry) + + assert lib.j_batch_execute(delete_batch) + lib.j_batch_unref(batch) + lib.j_batch_unref(delete_batch) + lib.j_db_schema_unref(b_scheme) diff --git a/benchmark/python/db/iterator.py b/benchmark/python/db/iterator.py new file mode 100644 index 000000000..a4119183a --- /dev/null +++ b/benchmark/python/db/iterator.py @@ -0,0 +1,132 @@ +from benchmarkrun import BenchmarkRun, append_to_benchmark_list_and_run +from julea import lib, encode, ffi +from db.common import _benchmark_db_prepare_scheme, _benchmark_db_insert, _benchmark_db_get_identifier, N, N_GET_DIVIDER, CLASS_MODULUS, CLASS_LIMIT + +def benchmark_db_iterator(benchmarkrun_list, iterations, machine_readable, op_duration): + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/iterator/get-simple", iterations, machine_readable, op_duration), benchmark_db_get_simple) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/iterator/get-simple-index-single", iterations, machine_readable, op_duration), benchmark_db_get_simple_index_single) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/iterator/get-simple-index-all", iterations, machine_readable, op_duration), benchmark_db_get_simple_index_all) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/iterator/get-simple-index-mixed", iterations, machine_readable, op_duration), benchmark_db_get_simple_index_mixed) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/iterator/get-range", iterations, machine_readable, op_duration), benchmark_db_get_range) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/iterator/get-range-index-single", iterations, machine_readable, op_duration), benchmark_db_get_range_index_single) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/iterator/get-range-index-all", iterations, machine_readable, op_duration), benchmark_db_get_range_index_all) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/iterator/get-range-index-mixed", iterations, machine_readable, op_duration), benchmark_db_get_range_index_mixed) + +def benchmark_db_get_simple(run): + _benchmark_db_get_simple(run, "benchmark_get_simple", False, False) + +def benchmark_db_get_simple_index_single(run): + _benchmark_db_get_simple(run, "benchmark_get_simple_index_single", False, + True) + +def benchmark_db_get_simple_index_all(run): + _benchmark_db_get_simple(run, "benchmark_get_simple_index_all", True, False) + +def benchmark_db_get_simple_index_mixed(run): + _benchmark_db_get_simple(run, "benchmark_get_simple_index_mixed", True, True) + +def _benchmark_db_get_simple(run, namespace, use_index_all, use_index_single): + namespace_encoded = encode(namespace) + b_s_error_ptr = ffi.new("GError*") + b_s_error_ptr = ffi.NULL + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + delete_batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + b_scheme = _benchmark_db_prepare_scheme(namespace_encoded, False, + use_index_all, use_index_single, + batch, delete_batch) + assert b_scheme != ffi.NULL + assert run != None + _benchmark_db_insert(None, b_scheme, "\0", True, False, False, False) + run.iterations = N if use_index_all or use_index_single else int(N / N_GET_DIVIDER) + run.operations = run.iterations + + for i in run: + field_type_ptr = ffi.new("JDBType*") + field_value_ptr = ffi.new("void**") + field_length_ptr = ffi.new("unsigned long*") + string_name = encode("string") + string = encode(_benchmark_db_get_identifier(i)) + selector = lib.j_db_selector_new(b_scheme, lib.J_DB_SELECTOR_MODE_AND, + b_s_error_ptr) + assert lib.j_db_selector_add_field(selector, string_name, + lib.J_DB_SELECTOR_OPERATOR_EQ, + string, 0, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + iterator = lib.j_db_iterator_new(b_scheme, selector, b_s_error_ptr) + assert iterator != ffi.NULL + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_iterator_next(iterator, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_iterator_get_field(iterator, ffi.NULL, string_name, field_type_ptr, + field_value_ptr, field_length_ptr, + b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + lib.j_db_selector_unref(selector) + lib.j_db_iterator_unref(iterator) + + lib.j_batch_unref(batch) + lib.j_batch_unref(delete_batch) + lib.j_db_schema_unref(b_scheme) + +def benchmark_db_get_range(run): + _benchmark_db_get_range(run, "benchmark_get_range", False, False) + +def benchmark_db_get_range_index_single(run): + _benchmark_db_get_range(run, "benchmark_get_range_index_single", False, + True) + +def benchmark_db_get_range_index_all(run): + _benchmark_db_get_range(run, "benchmark_get_range_index_all", True, False) + +def benchmark_db_get_range_index_mixed(run): + _benchmark_db_get_range(run, "benchmark_get_range_index_mixed", True, True) + +def _benchmark_db_get_range(run, namespace, use_index_all, use_index_single): + namespace_encoded = encode(namespace) + b_s_error_ptr = ffi.new("GError*") + b_s_error_ptr = ffi.NULL + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + delete_batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + b_scheme = _benchmark_db_prepare_scheme(namespace_encoded, False, + use_index_all, use_index_single, + batch, delete_batch) + assert b_scheme != ffi.NULL + assert run != None + _benchmark_db_insert(None, b_scheme, "\0", True, False, False, False) + run.iterations = N_GET_DIVIDER + run.operations = run.iterations + + for i in run: + field_type_ptr = ffi.new("JDBType*") + field_value_ptr = ffi.new("void**") + field_length_ptr = ffi.new("unsigned long*") + sint_name = encode("sint") + string_name = encode("string") + range_begin_ptr = ffi.new("long*") + range_begin_ptr[0] = int((i * (CLASS_MODULUS / N_GET_DIVIDER)) - CLASS_LIMIT) + range_end_ptr = ffi.new("long*") + range_end_ptr[0] = int(((i + 1) * (CLASS_MODULUS / N_GET_DIVIDER)) - (CLASS_LIMIT + 1)) + selector = lib.j_db_selector_new(b_scheme, lib.J_DB_SELECTOR_MODE_AND, + b_s_error_ptr) + assert lib.j_db_selector_add_field(selector, sint_name, + lib.J_DB_SELECTOR_OPERATOR_GE, + range_begin_ptr, 0 , b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_selector_add_field(selector, sint_name, + lib.J_DB_SELECTOR_OPERATOR_LE, + range_end_ptr, 0, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + iterator = lib.j_db_iterator_new(b_scheme, selector, b_s_error_ptr) + assert iterator != ffi.NULL + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_iterator_next(iterator, b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + assert lib.j_db_iterator_get_field(iterator, ffi.NULL, string_name, field_type_ptr, + field_value_ptr, field_length_ptr, + b_s_error_ptr) + assert b_s_error_ptr == ffi.NULL + + assert lib.j_batch_execute(delete_batch) + lib.j_db_schema_unref(b_scheme) + lib.j_batch_unref(batch) + lib.j_batch_unref(delete_batch) diff --git a/benchmark/python/db/schema.py b/benchmark/python/db/schema.py new file mode 100644 index 000000000..ad8576e96 --- /dev/null +++ b/benchmark/python/db/schema.py @@ -0,0 +1,78 @@ +from benchmarkrun import BenchmarkRun, append_to_benchmark_list_and_run +from julea import lib, encode, ffi + +def benchmark_db_schema(benchmarkrun_list, iterations, machine_readable, op_duration): + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/schema/create", iterations, machine_readable, op_duration), benchmark_db_schema_create) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/schema/create-batch", iterations, machine_readable, op_duration), benchmark_db_schema_create_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/schema/delete", iterations, machine_readable, op_duration), benchmark_db_schema_delete) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/db/schema/delete-batch", iterations, machine_readable, op_duration), benchmark_db_schema_delete_batch) + +def benchmark_db_schema_create(run): + _benchmark_db_schema_create(run, False) + +def benchmark_db_schema_create_batch(run): + _benchmark_db_schema_create(run, True) + +def _benchmark_db_schema_create(run, use_batch): + run.iterations = int(run.iterations / 10) + run.operations = run.iterations + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + delete_batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + run.batch_clean = lambda: lib.j_batch_execute(delete_batch) + + for i in run: + error_ptr_ptr = ffi.new("GError**") + error_ptr_ptr[0] = ffi.NULL + name = encode(f"benchmark-schema-{i}") + namespace = encode("benchmark-ns") + schema = lib.j_db_schema_new(namespace, name, error_ptr_ptr) + for j in range(10): + fname = encode(f"field{j}") + lib.j_db_schema_add_field(schema, fname, lib.J_DB_TYPE_STRING, + error_ptr_ptr) + lib.j_db_schema_create(schema, batch, ffi.NULL) + lib.j_db_schema_delete(schema, delete_batch, ffi.NULL) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_db_schema_unref(schema) + + lib.j_batch_unref(batch) + lib.j_batch_unref(delete_batch) + +def benchmark_db_schema_delete(run): + _benchmark_db_schema_delete(run, False) + +def benchmark_db_schema_delete_batch(run): + _benchmark_db_schema_delete(run, True) + +def _benchmark_db_schema_delete(run, use_batch): + run.iterations = int(run.iterations / 10) + run.operations = run.iterations + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + + def setup(): + for i in range(run.iterations): + name = encode(f"benchmark-schema-{i}") + namespace = encode("benchmark-ns") + schema = lib.j_db_schema_new(namespace, name, ffi.NULL) + for j in range(10): + fname = encode(f"field{j}") + lib.j_db_schema_add_field(schema, fname, lib.J_DB_TYPE_STRING, + ffi.NULL) + lib.j_db_schema_create(schema, batch, ffi.NULL) + lib.j_db_schema_unref(schema) + return lib.j_batch_execute(batch) + + run.batch_setup = setup + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + for i in run: + name = encode(f"benchmark-schema-{i}") + namespace = encode("benchmark-ns") + schema = lib.j_db_schema_new(namespace, name, ffi.NULL) + lib.j_db_schema_delete(schema, batch, ffi.NULL) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_db_schema_unref(schema) + lib.j_batch_unref(batch) diff --git a/benchmark/python/item/collection.py b/benchmark/python/item/collection.py new file mode 100644 index 000000000..b4e64bab8 --- /dev/null +++ b/benchmark/python/item/collection.py @@ -0,0 +1,104 @@ +from benchmarkrun import BenchmarkRun, append_to_benchmark_list_and_run +from julea import lib, encode, ffi + +def benchmark_collection(benchmarkrun_list, iterations, machine_readable, op_duration): + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/collection/create", iterations, machine_readable, op_duration), benchmark_collection_create) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/collection/create-batch", iterations, machine_readable, op_duration), benchmark_collection_create_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/collection/delete", iterations, machine_readable, op_duration), benchmark_collection_delete) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/collection/delete-batch", iterations, machine_readable, op_duration), benchmark_collection_delete_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/collection/delete-batch-without-get", iterations, machine_readable, op_duration), benchmark_collection_delete_batch_without_get) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/collection/unordered-create-delete", iterations, machine_readable, op_duration), benchmark_collection_unordered_create_delete) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/collection/unordered-create-delete-batch", iterations, machine_readable, op_duration), benchmark_collection_unordered_create_delete_batch) + # TODO: benchmark get (also missing in c benchmark) + +def benchmark_collection_create(run): + _benchmark_collection_create(run, False) + +def benchmark_collection_create_batch(run): + _benchmark_collection_create(run, True) + +def _benchmark_collection_create(run, use_batch): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + delete_batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + run.batch_clean = lambda: lib.j_batch_execute(delete_batch) + for i in run: + name = encode(f"benchmark{i}") + collection = lib.j_collection_create(name, batch) + lib.j_collection_delete(collection, delete_batch) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_collection_unref(collection) + + lib.j_batch_unref(batch) + lib.j_batch_unref(delete_batch) + +def benchmark_collection_delete(run): + _benchmark_collection_delete(run, False) + +def benchmark_collection_delete_batch(run): + _benchmark_collection_delete(run, True) + +def _benchmark_collection_delete(run, use_batch): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + for i in range(run.iterations): + name = encode(f"benchmark-{i}") + collection = lib.j_collection_create(name, batch) + lib.j_collection_unref(collection) + assert lib.j_batch_execute(batch) + + run.start_timer() + for i in range(run.iterations): + collection_ptr = ffi.new("JCollection**") + name = encode(f"benchmark-{i}") + lib.j_collection_get(collection_ptr, name, batch) + assert lib.j_batch_execute(batch) # FIXME ignores use_batch!! + lib.j_collection_delete(collection_ptr[0], batch) + if not use_batch: + assert lib.j_batch_execute(batch) + if use_batch: + assert lib.j_batch_execute(batch) + run.stop_timer() + lib.j_batch_unref(batch) + +def benchmark_collection_delete_batch_without_get(run): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + delete_batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + iterations = run.iterations + def setup(): + for i in range(iterations): + name = encode(f"benchmark-{i}") + collection = lib.j_collection_create(name, batch) + lib.j_collection_delete(collection, delete_batch) + lib.j_collection_unref(collection) + return lib.j_batch_execute(batch) + + run.batch_setup = setup + run.iterations = 1 + for i in run: + assert lib.j_batch_execute(delete_batch) + + lib.j_batch_unref(batch) + lib.j_batch_unref(delete_batch) + +def benchmark_collection_unordered_create_delete(run): + _benchmark_collection_unordered_create_delete(run, False) + +def benchmark_collection_unordered_create_delete_batch(run): + _benchmark_collection_unordered_create_delete(run, True) + +def _benchmark_collection_unordered_create_delete(run, use_batch): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + for i in run: + name = encode(f"benchmark-{i}") + collection = lib.j_collection_create(name, batch) + lib.j_collection_delete(collection, batch) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_collection_unref(collection) + + run.operations = run.iterations * 2 + lib.j_batch_unref(batch) diff --git a/benchmark/python/item/item.py b/benchmark/python/item/item.py new file mode 100644 index 000000000..65abbaed4 --- /dev/null +++ b/benchmark/python/item/item.py @@ -0,0 +1,231 @@ +from benchmarkrun import BenchmarkRun, append_to_benchmark_list_and_run +from julea import lib, encode, ffi + +def benchmark_item(benchmarkrun_list, iterations, machine_readable, op_duration): + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/item/create", iterations, machine_readable, op_duration), benchmark_item_create) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/item/create-batch", iterations, machine_readable, op_duration), benchmark_item_create_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/item/delete", iterations, machine_readable, op_duration), benchmark_item_delete) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/item/delete-batch", iterations, machine_readable, op_duration), benchmark_item_delete_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/item/delete-batch-without-get", iterations, machine_readable, op_duration), benchmark_item_delete_batch_without_get) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/item/get-status", iterations, machine_readable, op_duration), benchmark_item_get_status) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/item/get-status-batch", iterations, machine_readable, op_duration), benchmark_item_get_status_batch) + # TODO: benchmark get (also missing in c benchmark) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/item/read", iterations, machine_readable, op_duration), benchmark_item_read) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/item/read-batch", iterations, machine_readable, op_duration), benchmark_item_read_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/item/write", iterations, machine_readable, op_duration), benchmark_item_write) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/item/write-batch", iterations, machine_readable, op_duration), benchmark_item_write_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/item/unordered-create-delete", iterations, machine_readable, op_duration), benchmark_item_unordered_create_delete) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/item/item/unordered-create-delete-batch", iterations, machine_readable, op_duration), benchmark_item_unordered_create_delete_batch) + +def benchmark_item_create(run): + _benchmark_item_create(run, False) + +def benchmark_item_create_batch(run): + _benchmark_item_create(run, True) + +def _benchmark_item_create(run, use_batch): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + delete_batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + collection_name = encode("benchmark") + collection = lib.j_collection_create(collection_name, batch) + assert lib.j_batch_execute(batch) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + run.batch_clean: lambda: lib.j_batch_execute(delete_batch) + for i in run: + name = encode(f"benchmark-{i}") + item = lib.j_item_create(collection, name, ffi.NULL, batch) + lib.j_item_delete(item, delete_batch) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_item_unref(item) + + lib.j_collection_unref(collection) + lib.j_batch_unref(batch) + lib.j_batch_unref(delete_batch) + +def benchmark_item_delete(run): + _benchmark_item_delete(run, False) + +def benchmark_item_delete_batch(run): + _benchmark_item_delete(run, True) + +def _benchmark_item_delete(run, use_batch): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + get_batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + collection_name = encode("benchmark") + collection = lib.j_collection_create(collection_name, batch) + assert lib.j_batch_execute(batch) + + def setup(): + for i in range(run.iterations): + name = encode(f"benchmark-{i}") + item = lib.j_item_create(collection, name, ffi.NULL, batch) + lib.j_item_unref(item) + return lib.j_batch_execute(batch) + + run.batch_setup = setup + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + for i in run: + ptr = ffi.new("int**") + item_ptr = ffi.cast("JItem**", ptr) + name = encode(f"benchmark-{i}") + lib.j_item_get(collection, item_ptr, name, get_batch) + assert lib.j_batch_execute(get_batch) + lib.j_item_delete(item_ptr[0], batch) + if not use_batch: + assert lib.j_batch_execute(batch) + + lib.j_collection_delete(collection, batch) + assert lib.j_batch_execute(batch) + lib.j_collection_unref(collection) + lib.j_batch_unref(batch) + lib.j_batch_unref(get_batch) + +def benchmark_item_delete_batch_without_get(run): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + delete_batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + + collection_name = encode("benchmark") + collection = lib.j_collection_create(collection_name, batch) + assert lib.j_batch_execute(batch) + def setup(): + for i in range(run.operations): + name = encode(f"benchmark-{i}") + item = lib.j_item_create(collection, name, ffi.NULL, batch) + lib.j_item_delete(item, delete_batch) + lib.j_item_unref(item) + return lib.j_batch_execute(batch) + + run.batch_setup = setup + run.batch_send = lambda: lib.j_batch_execute(delete_batch) + for _ in run: + pass + + lib.j_collection_delete(collection, batch) + lib.j_collection_unref(collection) + lib.j_batch_unref(batch) + lib.j_batch_unref(delete_batch) + +def benchmark_item_get_status(run): + _benchmark_item_get_status(run, False) + +def benchmark_item_get_status_batch(run): + _benchmark_item_get_status(run, True) + +def _benchmark_item_get_status(run, use_batch): + run.iterations = 10 * run.iterations if use_batch else run.iterations + run.operations = run.iterations + dummy = ffi.new("char[1]") + nb_ptr = ffi.new("unsigned long*") + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + collection_name = encode("benchmark") + collection = lib.j_collection_create(collection_name, batch) + item = lib.j_item_create(collection, collection_name, ffi.NULL, batch) + lib.j_item_write(item, dummy, 1, 0, nb_ptr, batch) + assert lib.j_batch_execute(batch) + + if use_batch: batch_send = lambda: lib.j_batch_execute(batch) + for i in run: + lib.j_item_get_status(item, batch) + if not use_batch: + assert lib.j_batch_execute(batch) + + lib.j_item_delete(item, batch) + lib.j_collection_delete(collection, batch) + assert lib.j_batch_execute(batch) + lib.j_item_unref(item) + lib.j_collection_unref(collection) + lib.j_batch_unref(batch) + +def benchmark_item_read(run): + _benchmark_item_read(run, False, 4 * 1024) + +def benchmark_item_read_batch(run): + _benchmark_item_read(run, True, 4 * 1024) + +def _benchmark_item_read(run, use_batch, block_size): + run.iterations = 10 * run.iterations if use_batch else run.iterations + run.operations = run.iterations + nb_ptr = ffi.new("unsigned long*") + dummy = ffi.new("char[]", block_size) + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + collection_name = encode("benchmark") + collection = lib.j_collection_create(collection_name, batch) + item = lib.j_item_create(collection, collection_name, ffi.NULL, batch) + for i in range(run.iterations): + lib.j_item_write(item, dummy, block_size, i * block_size, nb_ptr, batch) + assert lib.j_batch_execute(batch) + assert nb_ptr[0] == run.iterations * block_size + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) & (nb_ptr[0] == run.iterations * block_size) + for i in run: + lib.j_item_read(item, dummy, block_size, i * block_size, nb_ptr, batch) + if not use_batch: + assert lib.j_batch_execute(batch) + assert nb_ptr[0] == block_size + + lib.j_item_delete(item, batch) + lib.j_collection_delete(collection, batch) + assert lib.j_batch_execute(batch) + lib.j_item_unref(item) + lib.j_collection_unref(collection) + lib.j_batch_unref(batch) + +def benchmark_item_write(run): + _benchmark_item_write(run, False, 4 * 1024) + +def benchmark_item_write_batch(run): + _benchmark_item_write(run, True, 4 * 1024) + +def _benchmark_item_write(run, use_batch, block_size): + run.iterations = 10 * run.iterations if use_batch else run.iterations + run.operations = run.iterations + dummy = ffi.new("char[]", block_size) + nb_ptr = ffi.new("unsigned long*") + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + collection_name = encode("benchmark") + collection = lib.j_collection_create(collection_name, batch) + item = lib.j_item_create(collection, collection_name, ffi.NULL, batch) + assert lib.j_batch_execute(batch) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) & (nb_ptr[0] == run.iterations * block_size) + for i in run: + lib.j_item_write(item, dummy, block_size, i * block_size, nb_ptr, batch) + if not use_batch: + assert lib.j_batch_execute(batch) + assert nb_ptr[0] == block_size + + lib.j_item_delete(item, batch) + lib.j_collection_delete(collection, batch) + assert lib.j_batch_execute(batch) + lib.j_item_unref(item) + lib.j_collection_unref(collection) + lib.j_batch_unref(batch) + +def benchmark_item_unordered_create_delete(run): + _benchmark_item_unordered_create_delete(run, False) + +def benchmark_item_unordered_create_delete_batch(run): + _benchmark_item_unordered_create_delete(run, True) + +def _benchmark_item_unordered_create_delete(run, use_batch): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + collection_name = encode("benchmark") + collection = lib.j_collection_create(collection_name, batch) + assert lib.j_batch_execute(batch) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + for i in run: + name = encode(f"benchmark-{i}") + item = lib.j_item_create(collection, name, ffi.NULL, batch) + lib.j_item_delete(item, batch) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_item_unref(item) + + lib.j_collection_delete(collection, batch) + assert lib.j_batch_execute(batch) + lib.j_collection_unref(collection) + lib.j_batch_unref(batch) + run.operations = run.iterations * 2 diff --git a/benchmark/python/kv/kv.py b/benchmark/python/kv/kv.py new file mode 100644 index 000000000..4a691071c --- /dev/null +++ b/benchmark/python/kv/kv.py @@ -0,0 +1,129 @@ +from benchmarkrun import BenchmarkRun, append_to_benchmark_list_and_run +from julea import lib, encode, ffi + +def benchmark_kv(benchmarkrun_list, iterations, machine_readable, op_duration): + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/kv/put", iterations, machine_readable, op_duration), benchmark_kv_put) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/kv/put_batch", iterations, machine_readable, op_duration), benchmark_kv_put_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/kv/get", iterations, machine_readable, op_duration), benchmark_kv_get) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/kv/get-batch", iterations, machine_readable, op_duration), benchmark_kv_get_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/kv/delete", iterations, machine_readable, op_duration), benchmark_kv_delete) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/kv/delete-batch", iterations, machine_readable, op_duration), benchmark_kv_delete_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/kv/unordered_put_delete", iterations, machine_readable, op_duration), benchmark_kv_unordered_put_delete) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/kv/unordered_put_delete_batch", iterations, machine_readable, op_duration), benchmark_kv_unordered_put_delete_batch) + +def benchmark_kv_put(run): + _benchmark_kv_put(run, False) + +def benchmark_kv_put_batch(run): + _benchmark_kv_put(run, True) + +def _benchmark_kv_put(run, use_batch): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + deletebatch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + run.batch_clean = lambda: lib.j_batch_execute(deletebatch) + + for i in run: + name = encode(f"benchmark-{i}") + namespace = encode("benchmark") + kv = lib.j_kv_new(namespace, name) + empty = encode("empty") + lib.j_kv_put(kv, empty, 6, ffi.NULL, batch) + lib.j_kv_delete(kv, deletebatch) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_kv_unref(kv) + lib.j_batch_unref(batch) + lib.j_batch_unref(deletebatch) + +def benchmark_kv_get(run): + _benchmark_kv_get(run, False) + +def benchmark_kv_get_batch(run): + _benchmark_kv_get(run, True) + +@ffi.def_extern() +def cffi_j_kv_get_function(pointer1, integer, pointer2): + return + +def _benchmark_kv_get(run, use_batch): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + deletebatch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + for i in range(run.iterations): + name = encode(f"benchmark-{i}") + namespace = encode("benchmark") + kv = lib.j_kv_new(namespace, name) + lib.j_kv_put(kv, name, len(name), ffi.NULL, batch) + lib.j_kv_delete(kv, deletebatch) + lib.j_kv_unref(kv) + assert lib.j_batch_execute(batch) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + + for i in run: + name = encode(f"benchmark-{i}") + namespace = encode("benchmark") + kv = lib.j_kv_new(namespace, name) + lib.j_kv_get_callback(kv, lib.cffi_j_kv_get_function, ffi.NULL, batch) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_kv_unref(kv) + + assert lib.j_batch_execute(deletebatch) + lib.j_batch_unref(batch) + lib.j_batch_unref(deletebatch) + +def benchmark_kv_delete(run): + _benchmark_kv_delete(run, False) + +def benchmark_kv_delete_batch(run): + _benchmark_kv_delete(run, True) + +def _benchmark_kv_delete(run, use_batch): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + createbatch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + empty = encode("empty") + + for i in range(run.iterations): + name = encode(f"benchmark-{i}") + namespace = encode("benchmark") + kv = lib.j_kv_new(namespace, name) + lib.j_kv_put(kv, empty, 6, ffi.NULL, createbatch) + lib.j_kv_unref(kv) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + run.batch_setup = lambda: lib.j_batch_execute(createbatch) + for i in run: + name = encode(f"benchmark-{i}") + namespace = encode("benchmark") + kv = lib.j_kv_new(namespace, name) + lib.j_kv_delete(kv, batch) + lib.j_kv_put(kv, empty, 6, ffi.NULL, createbatch) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_kv_unref(kv) + lib.j_batch_unref(batch) + lib.j_batch_unref(createbatch) + +def benchmark_kv_unordered_put_delete(run): + _benchmark_kv_unordered_put_delete(run, False) + +def benchmark_kv_unordered_put_delete_batch(run): + _benchmark_kv_unordered_put_delete(run, True) + +def _benchmark_kv_unordered_put_delete(run, use_batch): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + for i in run: + name = encode(f"benchmark-{i}") + namespace = encode("benchmark") + kv = lib.j_kv_new(namespace, name) + empty = encode("empty") + lib.j_kv_put(kv, empty, 6, ffi.NULL, batch) + lib.j_kv_delete(kv, batch) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_kv_unref(kv) + lib.j_batch_unref(batch) + run.operations = run.iterations * 2 diff --git a/benchmark/python/object/distributed_object.py b/benchmark/python/object/distributed_object.py new file mode 100644 index 000000000..33a4226b1 --- /dev/null +++ b/benchmark/python/object/distributed_object.py @@ -0,0 +1,200 @@ +from benchmarkrun import BenchmarkRun, append_to_benchmark_list_and_run +from julea import lib, encode, ffi + +def benchmark_distributed_object(benchmarkrun_list, iterations, machine_readable, op_duration): + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/distributed_object/create", iterations, machine_readable, op_duration), benchmark_distributed_object_create) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/distributed_object/create-batch", iterations, machine_readable, op_duration), benchmark_distributed_object_create_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/distributed_object/delete", iterations, machine_readable, op_duration), benchmark_distributed_object_delete) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/distributed_object/delete-batch", iterations, machine_readable, op_duration), benchmark_distributed_object_delete_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/distributed_object/status", iterations, machine_readable, op_duration), benchmark_distributed_object_status) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/distributed_object/status-batch", iterations, machine_readable, op_duration), benchmark_distributed_object_status_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/distributed_object/read", iterations, machine_readable, op_duration), benchmark_distributed_object_read) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/distributed_object/read-batch", iterations, machine_readable, op_duration), benchmark_distributed_object_read_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/distributed_object/write", iterations, machine_readable, op_duration), benchmark_distributed_object_write) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/distributed_object/write-batch", iterations, machine_readable, op_duration), benchmark_distributed_object_write_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/distributed_object/unordered-create-delete", iterations, machine_readable, op_duration), benchmark_distributed_object_unordered_create_delete) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/distributed_object/unordered-create-delete-batch", iterations, machine_readable, op_duration), benchmark_distributed_object_unordered_create_delete_batch) + +def benchmark_distributed_object_create(run): + _benchmark_distributed_object_create(run, False) + +def benchmark_distributed_object_create_batch(run): + _benchmark_distributed_object_create(run, True) + +def _benchmark_distributed_object_create(run, use_batch): + distribution = lib.j_distribution_new(lib.J_DISTRIBUTION_ROUND_ROBIN) + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + deletebatch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + run.batch_clean = lambda: lib.j_batch_execute(deletebatch) + for i in run: + name = encode(f"benchmark-{i}") + namespace = encode("benchmark") + obj = lib.j_distributed_object_new(namespace, name, distribution) + lib.j_distributed_object_create(obj, batch) + lib.j_distributed_object_delete(obj, deletebatch) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_distributed_object_unref(obj) + + lib.j_batch_unref(batch) + lib.j_batch_unref(deletebatch) + lib.j_distribution_unref(distribution) + +def benchmark_distributed_object_delete(run): + _benchmark_distributed_object_delete(run, False) + +def benchmark_distributed_object_delete_batch(run): + _benchmark_distributed_object_delete(run, True) + +def _benchmark_distributed_object_delete(run, use_batch): + distribution = lib.j_distribution_new(lib.J_DISTRIBUTION_ROUND_ROBIN) + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + createbatch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + for i in range(run.iterations): + name = encode(f"benchmark-{i}") + namespace = encode("benchmark") + obj = lib.j_distributed_object_new(namespace, name, distribution) + lib.j_distributed_object_create(obj, createbatch) + lib.j_distributed_object_unref(obj) + + run.batch_setup = lambda: lib.j_batch_execute(createbatch) + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + for i in run: + name = encode(f"benchmark-{i}") + namespace = encode("benchmark") + obj = lib.j_distributed_object_new(namespace, name, distribution) + lib.j_distributed_object_delete(obj, batch) + lib.j_distributed_object_create(obj, createbatch) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_distributed_object_unref(obj) + + lib.j_batch_unref(batch) + lib.j_distribution_unref(distribution) + +def benchmark_distributed_object_status(run): + _benchmark_distributed_object_status(run, False) + +def benchmark_distributed_object_status_batch(run): + _benchmark_distributed_object_status(run, True) + +def _benchmark_distributed_object_status(run, use_batch): + run.iterations = 10 * run.iterations if use_batch else run.iterations + run.operations = run.iterations + distribution = lib.j_distribution_new(lib.J_DISTRIBUTION_ROUND_ROBIN) + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + name = encode("benchmark") + obj = lib.j_distributed_object_new(name, name, distribution) + lib.j_distributed_object_create(obj, batch) + size_ptr = ffi.new("unsigned long*") + modification_time_ptr = ffi.new("long*") + character = encode("A") + lib.j_distributed_object_write(obj, character, 1, 0, size_ptr, batch) + assert lib.j_batch_execute(batch) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + for i in run: + lib.j_distributed_object_status(obj, modification_time_ptr, size_ptr, batch) + if not use_batch: + assert lib.j_batch_execute(batch) + + lib.j_distributed_object_delete(obj, batch) + assert lib.j_batch_execute(batch) + lib.j_distributed_object_unref(obj) + lib.j_batch_unref(batch) + lib.j_distribution_unref(distribution) + +def benchmark_distributed_object_read(run): + _benchmark_distributed_object_read(run, False, 4 * 1024) + +def benchmark_distributed_object_read_batch(run): + _benchmark_distributed_object_read(run, True, 4 * 1024) + +def _benchmark_distributed_object_read(run, use_batch, block_size): + run.iterations = 10 * run.iterations if use_batch else run.iterations + run.operations = run.iterations + distribution = lib.j_distribution_new(lib.J_DISTRIBUTION_ROUND_ROBIN) + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + name = encode("benchmark") + obj = lib.j_distributed_object_new(name, name, distribution) + dummy = ffi.new("char[]", block_size) + size_ptr = ffi.new("unsigned long*") + lib.j_distributed_object_create(obj, batch) + for i in range(run.iterations): + lib.j_distributed_object_write(obj, dummy, block_size, i*block_size, + size_ptr, batch) + assert lib.j_batch_execute(batch) + assert size_ptr[0] == run.iterations * block_size + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) & (size_ptr[0] == run.iterations * block_size) + for i in run: + lib.j_distributed_object_read(obj, dummy, block_size, i * block_size, + size_ptr, batch) + if not use_batch: + assert lib.j_batch_execute(batch) + assert size_ptr[0] == block_size + + lib.j_distributed_object_delete(obj, batch) + assert lib.j_batch_execute(batch) + lib.j_distribution_unref(distribution) + lib.j_batch_unref(batch) + lib.j_distributed_object_unref(obj) + +def benchmark_distributed_object_write(run): + _benchmark_distributed_object_write(run, False, 4 * 1024) + +def benchmark_distributed_object_write_batch(run): + _benchmark_distributed_object_write(run, True, 4 * 1024) + +def _benchmark_distributed_object_write(run, use_batch, block_size): + run.iterations = 10 * run.iterations if use_batch else run.iterations + run.operations = run.iterations + distribution = lib.j_distribution_new(lib.J_DISTRIBUTION_ROUND_ROBIN) + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + name = encode("benchmark") + obj = lib.j_distributed_object_new(name, name, distribution) + lib.j_distributed_object_create(obj, batch) + dummy = ffi.new("char[]", block_size) + size_ptr = ffi.new("unsigned long*") + assert lib.j_batch_execute(batch) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) & (size_ptr[0] == run.iterations * block_size) + for i in run: + lib.j_distributed_object_write(obj, dummy, block_size, i*block_size, size_ptr, batch) + if not use_batch: + assert lib.j_batch_execute(batch) + assert size_ptr[0] == block_size + + lib.j_distributed_object_delete(obj, batch) + assert lib.j_batch_execute(batch) + + lib.j_distributed_object_unref(obj) + lib.j_batch_unref(batch) + lib.j_distribution_unref(distribution) + +def benchmark_distributed_object_unordered_create_delete(run): + _benchmark_distributed_object_unordered_create_delete(run, False) + +def benchmark_distributed_object_unordered_create_delete_batch(run): + _benchmark_distributed_object_unordered_create_delete(run, True) + +def _benchmark_distributed_object_unordered_create_delete(run, use_batch): + distribution = lib.j_distribution_new(lib.J_DISTRIBUTION_ROUND_ROBIN) + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + for i in run: + namespace = encode("benchmark") + name = encode(f"benchmark-{i}") + obj = lib.j_distributed_object_new(namespace, name, distribution) + lib.j_distributed_object_create(obj, batch) + lib.j_distributed_object_delete(obj, batch) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_distributed_object_unref(obj) + + lib.j_batch_unref(batch) + lib.j_distribution_unref(distribution) + run.operations = run.iterations * 2 diff --git a/benchmark/python/object/object.py b/benchmark/python/object/object.py new file mode 100644 index 000000000..61909de76 --- /dev/null +++ b/benchmark/python/object/object.py @@ -0,0 +1,187 @@ +from benchmarkrun import BenchmarkRun, append_to_benchmark_list_and_run +from julea import lib, encode, ffi + +def benchmark_object(benchmarkrun_list, iterations, machine_readable, op_duration): + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/object/create", iterations, machine_readable, op_duration), benchmark_object_create) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/object/create-batch", iterations, machine_readable, op_duration), benchmark_object_create_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/object/delete", iterations, machine_readable, op_duration), benchmark_object_delete) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/object/delete-batch", iterations, machine_readable, op_duration), benchmark_object_delete_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/object/status", iterations, machine_readable, op_duration), benchmark_object_status) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/object/status-batch", iterations, machine_readable, op_duration), benchmark_object_status_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/object/read", iterations, machine_readable, op_duration), benchmark_object_read) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/object/read-batch", iterations, machine_readable, op_duration), benchmark_object_read_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/object/write", iterations, machine_readable, op_duration), benchmark_object_write) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/object/write-batch", iterations, machine_readable, op_duration), benchmark_object_write_batch) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/object/unordered-create-delete", iterations, machine_readable, op_duration), benchmark_object_unordered_create_delete) + append_to_benchmark_list_and_run(benchmarkrun_list, BenchmarkRun("/object/object/unordered-create-delete-batch", iterations, machine_readable, op_duration), benchmark_object_unordered_create_delete_batch) + + +def benchmark_object_create(run): + _benchmark_object_create(run, False) + +def benchmark_object_create_batch(run): + _benchmark_object_create(run, True) + +def _benchmark_object_create(run, use_batch): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + deletebatch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + run.batch_clean = lambda: lib.j_batch_execute(deletebatch) + for i in run: + name = encode(f"benchmark-{i}") + namespace = encode("benchmark") + obj = lib.j_object_new(namespace, name) + lib.j_object_create(obj, batch) + lib.j_object_delete(obj, deletebatch) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_object_unref(obj) + + lib.j_batch_unref(batch) + lib.j_batch_unref(deletebatch) + + +def benchmark_object_delete(run): + _benchmark_object_delete(run, False) + +def benchmark_object_delete_batch(run): + _benchmark_object_delete(run, True) + +def _benchmark_object_delete(run, use_batch): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + createbatch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + for i in range(run.iterations): + name = encode(f"benchmark-{i}") + namespace = encode("benchmark") + obj = lib.j_object_new(namespace, name) + lib.j_object_create(obj, createbatch) + lib.j_object_unref(obj) + + run.batch_setup = lambda: lib.j_batch_execute(createbatch) + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + for i in run: + name = encode(f"benchmark-{i}") + namespace = encode("benchmark") + obj = lib.j_object_new(namespace, name) + lib.j_object_delete(obj, batch) + lib.j_object_create(obj, createbatch) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_object_unref(obj) + lib.j_batch_unref(batch) + +def benchmark_object_status(run): + _benchmark_object_status(run, False) + +def benchmark_object_status_batch(run): + _benchmark_object_status(run, True) + +def _benchmark_object_status(run, use_batch): + run.iterations = 10 * run.iterations if use_batch else run.iterations + run.operations = run.iterations + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + name = encode("benchmark") + obj = lib.j_object_new(name, name) + lib.j_object_create(obj, batch) + size_ptr = ffi.new("unsigned long*") + modification_time_ptr = ffi.new("long*") + character = encode("A") + lib.j_object_write(obj, character, 1, 0, size_ptr, batch) + assert lib.j_batch_execute(batch) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + for i in run: + lib.j_object_status(obj, modification_time_ptr, size_ptr, batch) + if not use_batch: + assert lib.j_batch_execute(batch) + + lib.j_object_delete(obj, batch) + assert lib.j_batch_execute(batch) + lib.j_object_unref(obj) + lib.j_batch_unref(batch) + +def benchmark_object_read(run): + _benchmark_object_read(run, False, 4 * 1024) + +def benchmark_object_read_batch(run): + _benchmark_object_read(run, True, 4 * 1024) + +def _benchmark_object_read(run, use_batch, block_size): + run.iterations = 10 * run.iterations if use_batch else run.iterations + run.operations = run.iterations + dummy = ffi.new("char[]", block_size) + nb_ptr = ffi.new("unsigned long*") + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + name = encode("benchmark") + obj = lib.j_object_new(name, name) + lib.j_object_create(obj, batch) + for i in range(run.iterations): + lib.j_object_write(obj, dummy, block_size, i * block_size, nb_ptr, + batch) + assert lib.j_batch_execute(batch) + assert nb_ptr[0] == run.iterations * block_size + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) & (nb_ptr[0] == run.iterations * block_size) + for i in run: + lib.j_object_read(obj, dummy, block_size, i * block_size, nb_ptr, batch) + if not use_batch: + assert lib.j_batch_execute(batch) + assert nb_ptr[0] == block_size + + lib.j_object_delete(obj, batch) + assert lib.j_batch_execute(batch) + lib.j_object_unref(obj) + lib.j_batch_unref(batch) + +def benchmark_object_write(run): + _benchmark_object_write(run, False, 4 * 1024) + +def benchmark_object_write_batch(run): + _benchmark_object_write(run, True, 4 * 1024) + +def _benchmark_object_write(run, use_batch, block_size): + run.iterations = 10 * run.iterations if use_batch else run.iterations + run.operations = run.iterations + dummy = ffi.new("char[]", block_size) + nb_ptr = ffi.new("unsigned long*") + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + name = encode("benchmark") + obj = lib.j_object_new(name, name) + lib.j_object_create(obj, batch) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) & (nb_ptr[0] == run.iterations * block_size) + for i in run: + lib.j_object_write(obj, dummy, block_size, i * block_size, nb_ptr, + batch) + if not use_batch: + assert lib.j_batch_execute(batch) + assert nb_ptr[0] == block_size + + lib.j_object_delete(obj, batch) + assert lib.j_batch_execute(batch) + lib.j_object_unref(obj) + lib.j_batch_unref(batch) + +def benchmark_object_unordered_create_delete(run): + _benchmark_object_unordered_create_delete(run, False) + +def benchmark_object_unordered_create_delete_batch(run): + _benchmark_object_unordered_create_delete(run, True) + +def _benchmark_object_unordered_create_delete(run, use_batch): + batch = lib.j_batch_new_for_template(lib.J_SEMANTICS_TEMPLATE_DEFAULT) + + if use_batch: run.batch_send = lambda: lib.j_batch_execute(batch) + for i in run: + name = encode(f"benchmark-{i}") + namespace = encode("benchmark") + obj = lib.j_object_new(namespace, name) + lib.j_object_create(obj, batch) + lib.j_object_delete(obj, batch) + if not use_batch: + assert lib.j_batch_execute(batch) + lib.j_object_unref(obj) + + lib.j_batch_unref(batch) + run.operations = run.iterations * 2 diff --git a/doc/README.md b/doc/README.md index 8533cdde1..1c279a717 100644 --- a/doc/README.md +++ b/doc/README.md @@ -19,3 +19,4 @@ Its goal is to provide a solid foundation for storage research and teaching. * [Implementing a Backend](implementing-backend.md) * [JULEA-DB Details](db-code.md) * [HDF5 Support](hdf5.md) +* [Python Library](python.md) diff --git a/doc/python.md b/doc/python.md new file mode 100644 index 000000000..f16eb04e8 --- /dev/null +++ b/doc/python.md @@ -0,0 +1,32 @@ +# Python Bindings + +The JULEA client python bindings are using cffi created bindings. +For that the spack package `py-cffi` must be installed and loaded. + +```sh +. ./scripts/environment.sh +ninja -C bld # ensure binaries are up to date +spack install py-cffi # if not already installed +spack load py-cffi +python python/build.py bld # bld... your building directory +``` + +This will create a python library with c bindings resident in the build directory. +After this you can import the library with `import julea` as long as the environment and `py-cffi` is loaded. +A usage example can be found in `python/example/hello-world.py` + +The JULEA module contains the following parts: + +* `julea.ffi`: basic c defines and functions. Used for null value (`julea.ffi.NULL`) and to allocate heap variables. + (`p = julea.ffi.new(guint*)` will allocate `guint` and return a pointer to it) +* `julea.lib`: The JULEA API, for further information please read the [API-documentation](https://julea-io.github.io/julea/). (`julea.lib.j_object_new(julea.encode('hello'), julea.encode('world'))`) +* `encode(srt) -> c_str, read_string(c_str) -> str`: string helper to convert python to c strings and the other way around. +* `JBatch` and `JBatchResult`: JULEA works in batches, therefore commands will be queued and then executed at once. + ```python + result = JBatchResult() + with JBatch(result) as batch: + lib.j_object_create(..., batch) + ... + if result.IsSuccess: + ... + ``` diff --git a/python/build.py b/python/build.py new file mode 100644 index 000000000..b4f868f75 --- /dev/null +++ b/python/build.py @@ -0,0 +1,94 @@ +from build_helper import build, copy_main_module +import sys +import os + +# types used in JULEA include headers +# use '...' for types which are not accessed in header files +typedefs = { + # basic glib types + 'gint': 'int', + 'guint': 'unsigned int', + 'gboolean': 'int', + 'gchar': 'char', + 'gdouble': 'double', + 'gfloat': 'float', + 'guint16': 'unsigned short', + 'gint32': 'signed int', + 'guint32': 'unsigned int', + 'gint64': 'signed long', + 'guint64': 'unsigned long', + 'gpointer': 'void*', + 'gconstpointer': 'const void*', + 'gsize': 'unsigned long', + + # glib types + 'GQuark': 'guint32', + 'GError': 'struct _GError', # struct used in static value + 'GModule': '...', + 'GInputStream': '...', + 'GOutputStream': '...', + 'GKeyFile': '...', + 'GSocketConnection': '...', + 'void (*GDestroyNotify) (gpointer data)': '', # function definition + + 'bson_t': 'struct _bson_t', # sturct used in static value + + 'JBatch': '...', +} + +# list of macros to be ignored +macros = [ + 'G_DEFINE_AUTOPTR_CLEANUP_FUNC(x, y)', + 'G_END_DECLS', + 'G_BEGIN_DECLS', + 'G_GNUC_WARN_UNUSED_RESULT', + 'G_GNUC_PRINTF(x, y)' +] + +# JULEA modules which available through the interface +# it is expected that 'include/.h' is a header file! +libraries = [ + "julea", + "julea-object", + "julea-kv", + "julea-db", + "julea-item" +] + +# callbacks to python functions to use julea callback functions +callback_functions = [ + "void cffi_j_kv_get_function(gpointer, guint32, gpointer)", + "void cffi_j_batch_async_callback(JBatch*, gboolean, gpointer)", +] + +# header files which are needed to satisfy includes but to complex to parse +dummy_headers = [ + "glib.h", + "gmodule.h", + "bson.h", + "gio/gio.h", +] + +def usage(): + print("Build python bindings for JULEA with cffi.") + print() + print(f"python {sys.argv[0]} ") + print() + print("Make sure to execute it from the JULEA-repo root directory!") + exit(1) + +def err(msg): + print(msg) + exit(1) + +if __name__ == "__main__": + if len(sys.argv) < 2: + usage() + build_dir = os.path.abspath(sys.argv[1]) + if not os.path.isdir(build_dir): + err(f"Build directory: '{build_dir}' does not exist!") + if not os.path.isfile(os.path.abspath('.') + "/meson.build"): + err("The execution directory is apperntly not the JULEA-repo root directory") + print(build_dir) + build(build_dir, libraries, typedefs, macros, callback_functions, dummy_headers, debug=False) + copy_main_module(build_dir) diff --git a/python/build_helper.py b/python/build_helper.py new file mode 100644 index 000000000..76a798567 --- /dev/null +++ b/python/build_helper.py @@ -0,0 +1,90 @@ +from os import popen, system +from os.path import dirname +import cffi +import json +import os +import itertools + +def create_header(filename, libraries, typedefs, callbacks): + content = "" + for (k, v) in typedefs.items(): + content += f"typedef {v} {k};\n" + if 'struct' in v: + content += f"{v} {{ ...; }};\n" + for library in libraries: + content += f"#include <{library}.h>\n" + for callback in callbacks: + content += f'extern "Python" {callback};' + with open(filename, "w") as file: + file.write(content) + +def get_additional_compiler_flags(libraries, build_dir): + flags = list(set(itertools.chain.from_iterable(map( + lambda x: x["target_sources"][0]["parameters"], + [x for x in json.load(os.popen(f"meson introspect --targets {build_dir}")) if x["name"] in libraries])))) + clean = [] + for s in flags: + if "-W" in s: + pass + elif "-fsanitize" in s: + pass + else: + clean.append(s) + return clean + +def get_include_dirs(flags): + return [ str.strip("-I") for str in flags if "-I" in str ] + +def collect_julea(filename, libraries, build_dir, typedefs, macros, callbacks, dummy_headers, debug): + temp_filename = "temp.h" + create_header(temp_filename, libraries, typedefs, callbacks) + includes = get_additional_compiler_flags(libraries, build_dir) + flags = list(filter(lambda entry: not "dependencies" in entry, includes)) + # create dummy headers for files intentionally not included + for header in dummy_headers: + if path := os.path.dirname(header): + os.makedirs(path, exist_ok=True) + with open(header, "w") as file: + file.write("") + macro_flags = map(lambda x: f"-D'{x}='", macros) + # let preprocessor collect all declarations + system(f"cc -E -P {' '.join(macro_flags)} {temp_filename} -I. {' '.join(flags)} -o {filename}") + # remove temporary files needed to please the preprocessor + system(f"rm -rf glib.h gmodule.h bson.h gio {temp_filename}") + +def process(build_dir, libs, tempheader, debug=False): + ffi = cffi.FFI() + libraryname = "julea_wrapper" + with open(tempheader, "r") as file: + header_content = file.read() + includes = get_additional_compiler_flags(libs+["glib-2.0"], build_dir) + include_dirs = get_include_dirs(includes) + try: + ffi.cdef(header_content, override=True) + except cffi.CDefError as err: + print(err) + + outdir = build_dir + headerincludes = "" + for lib in libs: + headerincludes += f'#include "{lib}.h"\n' + ffi.set_source( + libraryname, + headerincludes, + libraries=libs+["kv-null"], + include_dirs=include_dirs, + library_dirs=[outdir], + extra_compile_args=includes, + extra_link_args=["-Wl,-rpath,."] + ) + ffi.compile(tmpdir=outdir, verbose=debug) + if not debug: + system(f"rm -f {tempheader} {outdir+libraryname}.o {outdir+libraryname}.c") + +def copy_main_module(build_dir): + system(f"cp {dirname(__file__)}/julea.py {build_dir}/julea.py") + +def build(build_dir, include_libs, typedefs, macros, callbacks, dummy_headers, debug=False): + header_name = "header_julea.h" + collect_julea(header_name, include_libs, build_dir, typedefs, macros, callbacks, dummy_headers, debug) + process(build_dir, include_libs, header_name, debug) diff --git a/python/cffi-requirements.txt b/python/cffi-requirements.txt new file mode 100644 index 000000000..8cb00ea70 --- /dev/null +++ b/python/cffi-requirements.txt @@ -0,0 +1,2 @@ +cffi==1.15.1 +pycparser==2.21 diff --git a/python/example/Makefile b/python/example/Makefile new file mode 100644 index 000000000..943e3ba6f --- /dev/null +++ b/python/example/Makefile @@ -0,0 +1,8 @@ +run: + ../../scripts/setup.sh start + python hello-world.py + ../../scripts/setup.sh stop + +clean: + ../../scripts/setup.sh stop + ../../scripts/setup.sh clean diff --git a/python/example/hello-world.py b/python/example/hello-world.py new file mode 100644 index 000000000..f7499294c --- /dev/null +++ b/python/example/hello-world.py @@ -0,0 +1,69 @@ +from julea import JBatchResult, JBatch, ffi, lib, encode, read_string + +if __name__ == "__main__": + try: + value_obj = encode("Hello Object!") + value_kv = encode("Hello Key-Value!") + value_db = encode("Hello Database!") + + result = JBatchResult() + hello = encode("hello") + world = encode("world") + nbytes_ptr = ffi.new("unsigned long*") + _object = lib.j_object_new(hello, world) + kv = lib.j_kv_new(hello, world) + schema = lib.j_db_schema_new(hello, world, ffi.NULL) + lib.j_db_schema_add_field(schema, hello, lib.J_DB_TYPE_STRING, ffi.NULL) + entry = lib.j_db_entry_new(schema, ffi.NULL) + + lib.j_db_entry_set_field(entry, hello, value_db, len(value_db), ffi.NULL) + + with JBatch(result) as batch: + lib.j_object_create(_object, batch) + lib.j_object_write(_object, value_obj, len(value_obj), 0, nbytes_ptr, batch) + lib.j_kv_put(kv, value_kv, len(value_kv), ffi.NULL, batch) + lib.j_db_schema_create(schema, batch, ffi.NULL) + lib.j_db_entry_insert(entry, batch, ffi.NULL) + if result.IsSuccess: + result = JBatchResult() + buffer = ffi.new("gchar[]", 128) + with JBatch(result) as batch: + lib.j_object_read(_object, buffer, 128, 0, nbytes_ptr, batch) + if result.IsSuccess: + print(f"Object contains: '{read_string(buffer)}' ({nbytes_ptr[0]} bytes)") + with JBatch(result) as batch: + lib.j_object_delete(_object, batch) + + result = JBatchResult() + with JBatch(result) as batch: + buffer_ptr = ffi.new("void**") + length = ffi.new("unsigned int *") + lib.j_kv_get(kv, buffer_ptr, length, batch) + if result.IsSuccess: + char_buff_ptr = ffi.cast("char**", buffer_ptr) + print(f"KV contains: '{read_string(char_buff_ptr[0])}' ({length[0]} bytes)") + with JBatch(result) as batch: + lib.j_kv_delete(kv, batch) + + try: + selector = lib.j_db_selector_new(schema, lib.J_DB_SELECTOR_MODE_AND, ffi.NULL) + lib.j_db_selector_add_field(selector, hello, lib.J_DB_SELECTOR_OPERATOR_EQ, value_db, len(value_db), ffi.NULL) + iterator = lib.j_db_iterator_new(schema, selector, ffi.NULL) + + while lib.j_db_iterator_next(iterator, ffi.NULL): + _type = ffi.new("JDBType *") + db_field_ptr = ffi.new("void**") + db_length_ptr = ffi.new("unsigned long*") + lib.j_db_iterator_get_field(iterator, schema, hello, _type, db_field_ptr, db_length_ptr, ffi.NULL) + print(f"DB contains: '{read_string(db_field_ptr[0])}' ({db_length_ptr[0]} bytes)") + finally: + with JBatch(result) as batch: + lib.j_db_entry_delete(entry, selector, batch, ffi.NULL) + lib.j_db_schema_delete(schema, batch, ffi.NULL) + lib.j_db_selector_unref(selector) + lib.j_db_iterator_unref(iterator) + finally: + lib.j_kv_unref(kv) + lib.j_object_unref(_object) + lib.j_db_schema_unref(schema) + lib.j_db_entry_unref(entry) diff --git a/python/julea.py b/python/julea.py new file mode 100644 index 000000000..9cee7c0e6 --- /dev/null +++ b/python/julea.py @@ -0,0 +1,50 @@ +from julea_wrapper import lib, ffi + +encoding = 'utf-8' + +def encode(string): + result = ffi.new('char[]', string.encode(encoding)) + return result + +def read_string(buffer): + char = ffi.cast('char*', buffer) + bytearr = b'' + i = 0 + byte = char[i] + while byte != b'\x00': + bytearr += byte + i += 1 + byte = char[i] + return bytearr.decode() + +class JBatchResult: + IsSuccess = False + + def __init__(self): + pass + + def Succeed(self): + self.IsSuccess = True + + def Fail(self): + self.IsSuccess = False; + +class JBatch: + def __init__(self, result): + template = lib.J_SEMANTICS_TEMPLATE_DEFAULT + self.batch = lib.j_batch_new_for_template(template) + self.result = result + + def __enter__(self): + return self.batch + + def __exit__(self, exc_type, exc_value, tb): + if lib.j_batch_execute(self.batch): + self.result.Succeed() + else: + self.result.Fail() + lib.j_batch_unref(self.batch) + if exc_type is not None: + return False + else: + return True diff --git a/scripts/benchmark.sh b/scripts/benchmark.sh index 45d0d83a4..c57a3cff2 100755 --- a/scripts/benchmark.sh +++ b/scripts/benchmark.sh @@ -37,6 +37,7 @@ usage () #export G_SLICE=debug-blocks set_path +set_python_path set_library_path set_backend_path set_hdf_path diff --git a/scripts/common b/scripts/common index 8ac6b9fc5..136f9925e 100644 --- a/scripts/common +++ b/scripts/common @@ -65,6 +65,31 @@ set_path () export PATH } +set_python_path() +{ + local build_dir + local old_python_path + + build_dir="$(get_directory "${SELF_DIR}/..")/bld" + old_python_path="${PYTHONPATH}" + + if test -n "${JULEA_PREFIX}" + then + PYTHONPATH="${JULEA_PREFIX}/bin" + else + test -d "${build_dir}" || error_build_directory_not_found + + PYTHONPATH="${build_dir}" + fi + + if test -n "${old_python_path}" + then + PYTHONPATH="${PYTHONPATH}:${old_python_path}" + fi + + export PYTHONPATH +} + set_library_path () { local build_dir diff --git a/scripts/environment.sh b/scripts/environment.sh index 2e5012576..e3e8b03e3 100755 --- a/scripts/environment.sh +++ b/scripts/environment.sh @@ -53,6 +53,7 @@ fi JULEA_ENVIRONMENT=1 set_path +set_python_path set_library_path set_pkg_config_path set_backend_path diff --git a/scripts/setup.sh b/scripts/setup.sh index 4bfe95be2..481812dea 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -68,6 +68,7 @@ MODE="$1" #export G_MESSAGES_DEBUG=JULEA set_path +set_python_path set_library_path set_backend_path diff --git a/scripts/test.sh b/scripts/test.sh index dbd991289..4697275ff 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -37,6 +37,7 @@ usage () export G_SLICE=debug-blocks set_path +set_python_path set_library_path set_backend_path set_hdf_path