From 422bd243dd93feb5af8669a97f7ce477ab2ceb54 Mon Sep 17 00:00:00 2001 From: Konrad Ueltzen Date: Thu, 2 May 2024 16:25:15 +0200 Subject: [PATCH 1/9] Add read-write benchmarks --- benchmark/benchmark.c | 2 + benchmark/benchmark.h | 1 + benchmark/object/bench-object-rw.c | 256 +++++++++++++++++++++++++++++ meson.build | 1 + 4 files changed, 260 insertions(+) create mode 100644 benchmark/object/bench-object-rw.c diff --git a/benchmark/benchmark.c b/benchmark/benchmark.c index 43ec2978d..1c1be724f 100644 --- a/benchmark/benchmark.c +++ b/benchmark/benchmark.c @@ -357,6 +357,8 @@ main(int argc, char** argv) // Object client benchmark_distributed_object(); benchmark_object(); + // additional read-write benches + benchmark_object_rw(); // DB client benchmark_db_entry(); diff --git a/benchmark/benchmark.h b/benchmark/benchmark.h index 091c6b914..5a74be43c 100644 --- a/benchmark/benchmark.h +++ b/benchmark/benchmark.h @@ -56,6 +56,7 @@ void benchmark_kv(void); void benchmark_distributed_object(void); void benchmark_object(void); +void benchmark_object_rw(void); void benchmark_db_entry(void); void benchmark_db_iterator(void); diff --git a/benchmark/object/bench-object-rw.c b/benchmark/object/bench-object-rw.c new file mode 100644 index 000000000..a343d65f9 --- /dev/null +++ b/benchmark/object/bench-object-rw.c @@ -0,0 +1,256 @@ +/* + * JULEA - Flexible storage framework + * Copyright (C) 2017-2024 Michael Kuhn + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include + +#include + +#include + +#include +#include + +#include "benchmark.h" + +#define min(x, y) (((x) <= (y)) ? (x) : (y)) + +static const guint BLOCK_SIZE = 4 * 1024; + +static const guint N = 5; + +static void +_benchmark_object_read(BenchmarkRun* run, gboolean use_batch, guint* pattern, guint block_size, guint n) +{ + g_autoptr(JObject) object = NULL; + g_autoptr(JBatch) batch = NULL; + g_autoptr(JSemantics) semantics = NULL; + g_autofree gchar* dummy = NULL; + guint64 nb = 0; + gboolean ret; + + dummy = g_malloc0(block_size); + + semantics = j_benchmark_get_semantics(); + batch = j_batch_new(semantics); + + object = j_object_new("benchmark", "benchmark"); + j_object_create(object, batch); + + for (guint i = 0; i < n; i++) + { + j_object_write(object, dummy, block_size, i * block_size, &nb, batch); + } + + ret = j_batch_execute(batch); + g_assert_true(ret); + g_assert_cmpuint(nb, ==, n * block_size); + + j_benchmark_timer_start(run); + + while (j_benchmark_iterate(run)) + { + for (guint i = 0; i < n; i++) + { + j_object_read(object, dummy, block_size, pattern[i] * block_size, &nb, batch); + + if (!use_batch) + { + ret = j_batch_execute(batch); + g_assert_true(ret); + g_assert_cmpuint(nb, ==, block_size); + } + } + + if (use_batch) + { + ret = j_batch_execute(batch); + g_assert_true(ret); + g_assert_cmpuint(nb, ==, n * block_size); + } + } + + j_benchmark_timer_stop(run); + + j_object_delete(object, batch); + ret = j_batch_execute(batch); + g_assert_true(ret); + + run->operations = n; + run->bytes = n * block_size; +} + +/* +Fills a buffer of a given length with incrementing values starting at 0. +*/ +static void +generate_pattern_seq(guint* buf, guint len) +{ + guint i; + for (i = 0; i < len; i++) + { + buf[i] = i; + } +} + +/* +Fills a buffer of a given length with the values in the range [0, len) shuffled using a set seed. +*/ +static void +generate_pattern_rand(guint* buf, guint len) +{ + // insert sequence + generate_pattern_seq(buf, len); + + // set seed + srand(42); + + // shuffle + for (guint i = 0; i < len; i++) + { + guint j = rand() % (i + 1); + + guint temp = buf[j]; + buf[j] = buf[i]; + buf[i] = temp; + } +} + +/* +Calculates the nth harmonic number +`H_N = 𝚺(k=1, N) 1/k`. +*/ +static double +calc_harmonic(guint n) +{ + double sum = 0.0; + for (guint i = 1; i <= n; i++) + { + sum += (1.0 / i); + } + + return sum; +} + +/* +Fills a buffer of a given length with values in the range of [0, len] +using a distribution following Zipf's law. + +This is realized by adding the same value multiple times +based on the frequency derived from the value's rank. + +The range [0, len) serves as a basis with the range being considered to already be ordered. + +The resulting range is then shuffled. +*/ +static void +generate_pattern_zipf(guint* buf, guint len) +{ + guint j = 0; + const double HARMONIC = calc_harmonic(len); + + g_autofree guint* template = g_malloc0(len * sizeof(guint)); + generate_pattern_seq(template, len); + + // apply distribution + for (guint i = 0; i < len; i++) + { + double f = (1 / HARMONIC) * (1.0 / (i + 1)); + + // always round up to get atleast 1 occurence + guint n_occurences = (f * len) + 1; + + printf("H_N = %.4f, f = %.4f\n", HARMONIC, f); + printf("Adding '%d' in range %d times at %d\n", template[i], n_occurences, j); + + for (guint k = j; k < min(len, j + n_occurences); k++) + { + buf[k] = template[i]; + } + + j += n_occurences; + + if (j >= len) + { + break; + } + } +} + +static void +benchmark_object_read(BenchmarkRun* run, gboolean use_batch, void (*generator)(guint*, guint)) +{ + guint n = (use_batch) ? 10 * N : N; + g_autofree guint* pattern = g_malloc0(n * sizeof(guint)); + (*generator)(pattern, n); + + printf("======= Pattern =======\n"); + for (guint i = 0; i < n; i++) + { + printf("%d ", pattern[i]); + } + printf("\n"); + + _benchmark_object_read(run, use_batch, pattern, BLOCK_SIZE, n); +} + +static void +benchmark_object_read_seq(BenchmarkRun* run) +{ + benchmark_object_read(run, FALSE, generate_pattern_seq); +} + +static void +benchmark_object_read_batch_seq(BenchmarkRun* run) +{ + benchmark_object_read(run, TRUE, generate_pattern_seq); +} + +static void +benchmark_object_read_rand(BenchmarkRun* run) +{ + benchmark_object_read(run, FALSE, generate_pattern_rand); +} + +static void +benchmark_object_read_rand_batch(BenchmarkRun* run) +{ + benchmark_object_read(run, TRUE, generate_pattern_rand); +} + +static void +benchmark_object_read_zipf(BenchmarkRun* run) +{ + benchmark_object_read(run, FALSE, generate_pattern_zipf); +} + +static void +benchmark_object_read_zipf_batch(BenchmarkRun* run) +{ + benchmark_object_read(run, TRUE, generate_pattern_zipf); +} + +void +benchmark_object_rw(void) +{ + j_benchmark_add("/object/object/rw/read-seq", benchmark_object_read_seq); + j_benchmark_add("/object/object/rw/read-seq-batch", benchmark_object_read_batch_seq); + j_benchmark_add("/object/object/rw/read-rand", benchmark_object_read_rand); + j_benchmark_add("/object/object/rw/read-rand-batch", benchmark_object_read_rand_batch); + j_benchmark_add("/object/object/rw/read-zipf", benchmark_object_read_zipf); + j_benchmark_add("/object/object/rw/read-zipf-batch", benchmark_object_read_zipf_batch); +} diff --git a/meson.build b/meson.build index e5133417d..9e9ebd7c8 100644 --- a/meson.build +++ b/meson.build @@ -585,6 +585,7 @@ julea_benchmark_srcs = files([ 'benchmark/message.c', 'benchmark/object/distributed-object.c', 'benchmark/object/object.c', + 'benchmark/object/bench-object-rw.c', ]) executable('julea-benchmark', julea_benchmark_srcs, From 7f2af0cc03595c91fe10f90e3aaeac35c5e57367 Mon Sep 17 00:00:00 2001 From: Konrad Ueltzen Date: Fri, 3 May 2024 15:42:52 +0200 Subject: [PATCH 2/9] Add variable block sizes --- benchmark/benchmark.h | 1 + benchmark/object/bench-object-rw.c | 39 +++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/benchmark/benchmark.h b/benchmark/benchmark.h index 5a74be43c..bda71a336 100644 --- a/benchmark/benchmark.h +++ b/benchmark/benchmark.h @@ -30,6 +30,7 @@ struct BenchmarkRun guint iterations; guint64 operations; guint64 bytes; + void* additional_data; }; typedef struct BenchmarkRun BenchmarkRun; diff --git a/benchmark/object/bench-object-rw.c b/benchmark/object/bench-object-rw.c index a343d65f9..3682ff80d 100644 --- a/benchmark/object/bench-object-rw.c +++ b/benchmark/object/bench-object-rw.c @@ -31,6 +31,12 @@ static const guint BLOCK_SIZE = 4 * 1024; +static const guint NUM_SIZES = 3; + +static guint index = 0; + +static const guint BLOCK_SIZES[5] = { 1024, 2 * 1024, 4 * 1024 }; + static const guint N = 5; static void @@ -174,9 +180,6 @@ generate_pattern_zipf(guint* buf, guint len) // always round up to get atleast 1 occurence guint n_occurences = (f * len) + 1; - printf("H_N = %.4f, f = %.4f\n", HARMONIC, f); - printf("Adding '%d' in range %d times at %d\n", template[i], n_occurences, j); - for (guint k = j; k < min(len, j + n_occurences); k++) { buf[k] = template[i]; @@ -195,15 +198,22 @@ static void benchmark_object_read(BenchmarkRun* run, gboolean use_batch, void (*generator)(guint*, guint)) { guint n = (use_batch) ? 10 * N : N; + guint block_size = BLOCK_SIZES[index]; g_autofree guint* pattern = g_malloc0(n * sizeof(guint)); + + printf("block size: %d\n", block_size); + index = (index + 1) % NUM_SIZES; + (*generator)(pattern, n); + /* printf("======= Pattern =======\n"); for (guint i = 0; i < n; i++) { printf("%d ", pattern[i]); } printf("\n"); + */ _benchmark_object_read(run, use_batch, pattern, BLOCK_SIZE, n); } @@ -244,13 +254,24 @@ benchmark_object_read_zipf_batch(BenchmarkRun* run) benchmark_object_read(run, TRUE, generate_pattern_zipf); } +static void +add_benches(const gchar* path, BenchmarkFunc benchmark) +{ + for (guint i = 0; i < NUM_SIZES; i++) + { + g_autofree gchar* buf = g_malloc0(64 * sizeof(gchar)); + g_snprintf(buf, 64ul, "%s > %dB", path, BLOCK_SIZES[i]); + j_benchmark_add(buf, benchmark); + } +} + void benchmark_object_rw(void) { - j_benchmark_add("/object/object/rw/read-seq", benchmark_object_read_seq); - j_benchmark_add("/object/object/rw/read-seq-batch", benchmark_object_read_batch_seq); - j_benchmark_add("/object/object/rw/read-rand", benchmark_object_read_rand); - j_benchmark_add("/object/object/rw/read-rand-batch", benchmark_object_read_rand_batch); - j_benchmark_add("/object/object/rw/read-zipf", benchmark_object_read_zipf); - j_benchmark_add("/object/object/rw/read-zipf-batch", benchmark_object_read_zipf_batch); + add_benches("/object/object/read-seq", benchmark_object_read_seq); + add_benches("/object/object/rw/read-seq-batch", benchmark_object_read_batch_seq); + add_benches("/object/object/rw/read-rand", benchmark_object_read_rand); + add_benches("/object/object/rw/read-rand-batch", benchmark_object_read_rand_batch); + add_benches("/object/object/rw/read-zipf", benchmark_object_read_zipf); + add_benches("/object/object/rw/read-zipf-batch", benchmark_object_read_zipf_batch); } From 62478e49b91faeb0100ae44b9448cbfe15a8f61f Mon Sep 17 00:00:00 2001 From: Konrad Ueltzen Date: Fri, 3 May 2024 15:57:51 +0200 Subject: [PATCH 3/9] Fix variable block size --- benchmark/object/bench-object-rw.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/benchmark/object/bench-object-rw.c b/benchmark/object/bench-object-rw.c index 3682ff80d..b995ebbf3 100644 --- a/benchmark/object/bench-object-rw.c +++ b/benchmark/object/bench-object-rw.c @@ -27,17 +27,15 @@ #include "benchmark.h" -#define min(x, y) (((x) <= (y)) ? (x) : (y)) - -static const guint BLOCK_SIZE = 4 * 1024; +#define NUM_SIZES 3 -static const guint NUM_SIZES = 3; +#define min(x, y) (((x) <= (y)) ? (x) : (y)) static guint index = 0; -static const guint BLOCK_SIZES[5] = { 1024, 2 * 1024, 4 * 1024 }; +static const guint BLOCK_SIZES[NUM_SIZES] = { 1024, 2 * 1024, 4 * 1024 }; -static const guint N = 5; +static const guint N = 1000; static void _benchmark_object_read(BenchmarkRun* run, gboolean use_batch, guint* pattern, guint block_size, guint n) @@ -201,7 +199,6 @@ benchmark_object_read(BenchmarkRun* run, gboolean use_batch, void (*generator)(g guint block_size = BLOCK_SIZES[index]; g_autofree guint* pattern = g_malloc0(n * sizeof(guint)); - printf("block size: %d\n", block_size); index = (index + 1) % NUM_SIZES; (*generator)(pattern, n); @@ -215,7 +212,7 @@ benchmark_object_read(BenchmarkRun* run, gboolean use_batch, void (*generator)(g printf("\n"); */ - _benchmark_object_read(run, use_batch, pattern, BLOCK_SIZE, n); + _benchmark_object_read(run, use_batch, pattern, block_size, n); } static void From c6747e66426e55a7df15af85c01657673fc90062 Mon Sep 17 00:00:00 2001 From: Konrad Ueltzen Date: Fri, 3 May 2024 16:12:02 +0200 Subject: [PATCH 4/9] Fix read-seq path --- benchmark/object/bench-object-rw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/object/bench-object-rw.c b/benchmark/object/bench-object-rw.c index b995ebbf3..061992a0e 100644 --- a/benchmark/object/bench-object-rw.c +++ b/benchmark/object/bench-object-rw.c @@ -265,7 +265,7 @@ add_benches(const gchar* path, BenchmarkFunc benchmark) void benchmark_object_rw(void) { - add_benches("/object/object/read-seq", benchmark_object_read_seq); + add_benches("/object/object/rw/read-seq", benchmark_object_read_seq); add_benches("/object/object/rw/read-seq-batch", benchmark_object_read_batch_seq); add_benches("/object/object/rw/read-rand", benchmark_object_read_rand); add_benches("/object/object/rw/read-rand-batch", benchmark_object_read_rand_batch); From f86dc78181c5640e7e05eb1f8369ef1947f8481c Mon Sep 17 00:00:00 2001 From: Konrad Ueltzen Date: Thu, 23 May 2024 12:45:24 +0200 Subject: [PATCH 5/9] Add read-write ratio --- benchmark/object/bench-object-rw.c | 281 +++++++++++++++++++++++++---- 1 file changed, 242 insertions(+), 39 deletions(-) diff --git a/benchmark/object/bench-object-rw.c b/benchmark/object/bench-object-rw.c index 061992a0e..d402333d3 100644 --- a/benchmark/object/bench-object-rw.c +++ b/benchmark/object/bench-object-rw.c @@ -29,16 +29,26 @@ #define NUM_SIZES 3 +#define NUM_RW_RATIOS 5 + +#define READ_FLAG 0 + +#define WRITE_FLAG 1 + #define min(x, y) (((x) <= (y)) ? (x) : (y)) static guint index = 0; +static guint rw_index = 0; + static const guint BLOCK_SIZES[NUM_SIZES] = { 1024, 2 * 1024, 4 * 1024 }; +static const gdouble RW_RATIOS[NUM_RW_RATIOS] = { 0.0, 0.25, 0.5, 0.75, 1.0 }; + static const guint N = 1000; static void -_benchmark_object_read(BenchmarkRun* run, gboolean use_batch, guint* pattern, guint block_size, guint n) +_benchmark_object_read_write(BenchmarkRun* run, gboolean use_batch, guint* pattern, guint* rw_pattern, guint block_size, guint n) { g_autoptr(JObject) object = NULL; g_autoptr(JBatch) batch = NULL; @@ -46,6 +56,7 @@ _benchmark_object_read(BenchmarkRun* run, gboolean use_batch, guint* pattern, gu g_autofree gchar* dummy = NULL; guint64 nb = 0; gboolean ret; + guint rcounter = 0, wcounter = 0; dummy = g_malloc0(block_size); @@ -70,7 +81,71 @@ _benchmark_object_read(BenchmarkRun* run, gboolean use_batch, guint* pattern, gu { for (guint i = 0; i < n; i++) { - j_object_read(object, dummy, block_size, pattern[i] * block_size, &nb, batch); + // decide read or write + if (rw_pattern[i] == 0) + { + j_object_read(object, dummy, block_size, pattern[i] * block_size, &nb, batch); + rcounter++; + } + else + { + j_object_write(object, dummy, block_size, pattern[i] * block_size, &nb, batch); + wcounter++; + } + + if (!use_batch) + { + ret = j_batch_execute(batch); + g_assert_true(ret); + g_assert_cmpuint(nb, ==, block_size); + } + } + + if (use_batch) + { + ret = j_batch_execute(batch); + g_assert_true(ret); + g_assert_cmpuint(nb, ==, n * block_size); + } + } + + j_benchmark_timer_stop(run); + + j_object_delete(object, batch); + ret = j_batch_execute(batch); + g_assert_true(ret); + + run->operations = n; + run->bytes = n * block_size; +} + +static void +_benchmark_object_write(BenchmarkRun* run, gboolean use_batch, guint* pattern, guint block_size, guint n) +{ + g_autoptr(JObject) object = NULL; + g_autoptr(JBatch) batch = NULL; + g_autoptr(JSemantics) semantics = NULL; + g_autofree gchar* dummy = NULL; + guint64 nb = 0; + gboolean ret; + + dummy = g_malloc0(block_size); + + semantics = j_benchmark_get_semantics(); + batch = j_batch_new(semantics); + + object = j_object_new("benchmark", "benchmark"); + j_object_create(object, batch); + ret = j_batch_execute(batch); + g_assert_true(ret); + + j_benchmark_timer_start(run); + + while (j_benchmark_iterate(run)) + { + for (guint i = 0; i < n; i++) + { + j_object_write(object, dummy, block_size, pattern[i] * block_size, &nb, batch); if (!use_batch) { @@ -111,15 +186,9 @@ generate_pattern_seq(guint* buf, guint len) } } -/* -Fills a buffer of a given length with the values in the range [0, len) shuffled using a set seed. -*/ static void -generate_pattern_rand(guint* buf, guint len) +shuffle(guint* buf, guint len) { - // insert sequence - generate_pattern_seq(buf, len); - // set seed srand(42); @@ -134,6 +203,50 @@ generate_pattern_rand(guint* buf, guint len) } } +static void +generate_rw_pattern(guint* buf, guint n, gdouble rw_ratio) +{ + guint read_ops = 0; + if (rw_ratio < 0.0001) + { + read_ops = n; + } + else if (rw_ratio > 0.9999) + { + read_ops = 0; + } + else + { + read_ops = (int)(rw_ratio * n); + } + + for (guint i = 0; i < n; i++) + { + if (i < read_ops) + { + buf[i] = READ_FLAG; + } + else + { + buf[i] = WRITE_FLAG; + } + } + + shuffle(buf, n); +} + +/* +Fills a buffer of a given length with the values in the range [0, len) shuffled using a set seed. +*/ +static void +generate_pattern_rand(guint* buf, guint len) +{ + // insert sequence + generate_pattern_seq(buf, len); + + shuffle(buf, len); +} + /* Calculates the nth harmonic number `H_N = 𝚺(k=1, N) 1/k`. @@ -167,8 +280,9 @@ generate_pattern_zipf(guint* buf, guint len) guint j = 0; const double HARMONIC = calc_harmonic(len); + // generate template of randomly ordered indices g_autofree guint* template = g_malloc0(len * sizeof(guint)); - generate_pattern_seq(template, len); + generate_pattern_rand(template, len); // apply distribution for (guint i = 0; i < len; i++) @@ -190,10 +304,13 @@ generate_pattern_zipf(guint* buf, guint len) break; } } + + // shuffle distribution + shuffle(buf, len); } static void -benchmark_object_read(BenchmarkRun* run, gboolean use_batch, void (*generator)(guint*, guint)) +benchmark_object_write(BenchmarkRun* run, gboolean use_batch, void (*generator)(guint*, guint)) { guint n = (use_batch) ? 10 * N : N; guint block_size = BLOCK_SIZES[index]; @@ -203,52 +320,116 @@ benchmark_object_read(BenchmarkRun* run, gboolean use_batch, void (*generator)(g (*generator)(pattern, n); - /* - printf("======= Pattern =======\n"); - for (guint i = 0; i < n; i++) - { - printf("%d ", pattern[i]); - } - printf("\n"); - */ + _benchmark_object_write(run, use_batch, pattern, block_size, n); +} + +static void +benchmark_object_read_write(BenchmarkRun* run, gboolean use_batch, void (*generator)(guint*, guint)) +{ + guint n = (use_batch) ? 10 * N : N; + guint block_size = BLOCK_SIZES[index]; + gfloat ratio = RW_RATIOS[rw_index]; + g_autofree guint* pattern = g_malloc0(n * sizeof(guint)); + g_autofree guint* rw_pattern = g_malloc0(n * sizeof(guint)); + + index = (index + 1) % NUM_SIZES; + rw_index = (rw_index + 1) % NUM_RW_RATIOS; + + generate_rw_pattern(rw_pattern, n, ratio); + (*generator)(pattern, n); + + _benchmark_object_read_write(run, use_batch, pattern, rw_pattern, block_size, n); +} + +// WRITE + +static void +benchmark_object_write_seq(BenchmarkRun* run) +{ + benchmark_object_write(run, FALSE, generate_pattern_seq); +} + +static void +benchmark_object_write_batch_seq(BenchmarkRun* run) +{ + benchmark_object_write(run, TRUE, generate_pattern_seq); +} + +static void +benchmark_object_write_rand(BenchmarkRun* run) +{ + benchmark_object_write(run, FALSE, generate_pattern_rand); +} + +static void +benchmark_object_write_rand_batch(BenchmarkRun* run) +{ + benchmark_object_write(run, TRUE, generate_pattern_rand); +} + +static void +benchmark_object_write_zipf(BenchmarkRun* run) +{ + benchmark_object_write(run, FALSE, generate_pattern_zipf); +} - _benchmark_object_read(run, use_batch, pattern, block_size, n); +static void +benchmark_object_write_zipf_batch(BenchmarkRun* run) +{ + benchmark_object_write(run, TRUE, generate_pattern_zipf); } +// RW + static void -benchmark_object_read_seq(BenchmarkRun* run) +benchmark_object_rw_seq(BenchmarkRun* run) { - benchmark_object_read(run, FALSE, generate_pattern_seq); + benchmark_object_read_write(run, FALSE, generate_pattern_seq); } static void -benchmark_object_read_batch_seq(BenchmarkRun* run) +benchmark_object_rw_batch_seq(BenchmarkRun* run) { - benchmark_object_read(run, TRUE, generate_pattern_seq); + benchmark_object_read_write(run, TRUE, generate_pattern_seq); } static void -benchmark_object_read_rand(BenchmarkRun* run) +benchmark_object_rw_rand(BenchmarkRun* run) { - benchmark_object_read(run, FALSE, generate_pattern_rand); + benchmark_object_read_write(run, FALSE, generate_pattern_rand); } static void -benchmark_object_read_rand_batch(BenchmarkRun* run) +benchmark_object_rw_rand_batch(BenchmarkRun* run) { - benchmark_object_read(run, TRUE, generate_pattern_rand); + benchmark_object_read_write(run, TRUE, generate_pattern_rand); } static void -benchmark_object_read_zipf(BenchmarkRun* run) +benchmark_object_rw_zipf(BenchmarkRun* run) { - benchmark_object_read(run, FALSE, generate_pattern_zipf); + benchmark_object_read_write(run, FALSE, generate_pattern_zipf); } static void -benchmark_object_read_zipf_batch(BenchmarkRun* run) +benchmark_object_rw_zipf_batch(BenchmarkRun* run) { - benchmark_object_read(run, TRUE, generate_pattern_zipf); + benchmark_object_read_write(run, TRUE, generate_pattern_zipf); +} + +static void +add_rw_benches(const gchar* path, BenchmarkFunc benchmark) +{ + for (guint i = 0; i < NUM_SIZES; i++) + { + for (guint j = 0; j < NUM_RW_RATIOS; j++) + { + g_autofree gchar* buf = g_malloc0(64 * sizeof(gchar)); + g_snprintf(buf, 64ul, "%s <%.2fw> %dB", path, (double)RW_RATIOS[j], BLOCK_SIZES[i]); + + j_benchmark_add(buf, benchmark); + } + } } static void @@ -257,18 +438,40 @@ add_benches(const gchar* path, BenchmarkFunc benchmark) for (guint i = 0; i < NUM_SIZES; i++) { g_autofree gchar* buf = g_malloc0(64 * sizeof(gchar)); - g_snprintf(buf, 64ul, "%s > %dB", path, BLOCK_SIZES[i]); + g_snprintf(buf, 64ul, "%s %dB", path, BLOCK_SIZES[i]); + j_benchmark_add(buf, benchmark); } } +/* + * The mixed benchmark pre-writes the entire file to guarantee that it can be read from. + * This has performance implications as the relevant data will be (partially) present in the page cache. + * Some interfaces are also affected. To illustrate the problem on an example: + * 1. mmap empty file (assume the application artificially increases the size of the file to N bytes to avoid remapping later on) + * 2. write N+1 bytes to file + * - 3a. without pre-writing remapping is required + * - 3b. with pre-writing the remapping has already occured or has been mmap'ed at the appropriate size to begin with. No remapping is required. + * + * Therefore, the benchmark also includes pure write operations where no pre-writing takes place. + * This can be used to estimate the impact of pre-writing in the mixed benchmark by comparing the write benchmark with the corresponding mixed benchmark at ratio=1.00. +*/ void benchmark_object_rw(void) { - add_benches("/object/object/rw/read-seq", benchmark_object_read_seq); - add_benches("/object/object/rw/read-seq-batch", benchmark_object_read_batch_seq); - add_benches("/object/object/rw/read-rand", benchmark_object_read_rand); - add_benches("/object/object/rw/read-rand-batch", benchmark_object_read_rand_batch); - add_benches("/object/object/rw/read-zipf", benchmark_object_read_zipf); - add_benches("/object/object/rw/read-zipf-batch", benchmark_object_read_zipf_batch); + // MIXED + add_rw_benches("/object/object/rw/seq", benchmark_object_rw_seq); + add_rw_benches("/object/object/rw/seq-batch", benchmark_object_rw_batch_seq); + add_rw_benches("/object/object/rw/rand", benchmark_object_rw_rand); + add_rw_benches("/object/object/rw/rand-batch", benchmark_object_rw_rand_batch); + add_rw_benches("/object/object/rw/zipf", benchmark_object_rw_zipf); + add_rw_benches("/object/object/rw/zipf-batch", benchmark_object_rw_zipf_batch); + + // WRITE + add_benches("/object/object/rw/write-seq", benchmark_object_write_seq); + add_benches("/object/object/rw/write-seq-batch", benchmark_object_write_batch_seq); + add_benches("/object/object/rw/write-rand", benchmark_object_write_rand); + add_benches("/object/object/rw/write-rand-batch", benchmark_object_write_rand_batch); + add_benches("/object/object/rw/write-zipf", benchmark_object_write_zipf); + add_benches("/object/object/rw/write-zipf-batch", benchmark_object_write_zipf_batch); } From 27196740ba4889805fe4281c7491315aa8d8ffe8 Mon Sep 17 00:00:00 2001 From: Konrad Ueltzen Date: Tue, 28 May 2024 22:28:09 +0200 Subject: [PATCH 6/9] Modify bench, add cluster bench --- benchmark/benchmark.c | 1 + benchmark/benchmark.h | 1 + benchmark/object/bench-object-rw.c | 49 +++- benchmark/object/cluster-benches.c | 445 +++++++++++++++++++++++++++++ meson.build | 1 + 5 files changed, 484 insertions(+), 13 deletions(-) create mode 100644 benchmark/object/cluster-benches.c diff --git a/benchmark/benchmark.c b/benchmark/benchmark.c index 1c1be724f..fb3d0a74f 100644 --- a/benchmark/benchmark.c +++ b/benchmark/benchmark.c @@ -359,6 +359,7 @@ main(int argc, char** argv) benchmark_object(); // additional read-write benches benchmark_object_rw(); + benchmark_object_cluster(); // DB client benchmark_db_entry(); diff --git a/benchmark/benchmark.h b/benchmark/benchmark.h index bda71a336..7a4a47459 100644 --- a/benchmark/benchmark.h +++ b/benchmark/benchmark.h @@ -58,6 +58,7 @@ void benchmark_kv(void); void benchmark_distributed_object(void); void benchmark_object(void); void benchmark_object_rw(void); +void benchmark_object_cluster(void); void benchmark_db_entry(void); void benchmark_db_iterator(void); diff --git a/benchmark/object/bench-object-rw.c b/benchmark/object/bench-object-rw.c index d402333d3..e0178f3b9 100644 --- a/benchmark/object/bench-object-rw.c +++ b/benchmark/object/bench-object-rw.c @@ -27,7 +27,7 @@ #include "benchmark.h" -#define NUM_SIZES 3 +#define NUM_SIZES 7 #define NUM_RW_RATIOS 5 @@ -41,7 +41,15 @@ static guint index = 0; static guint rw_index = 0; -static const guint BLOCK_SIZES[NUM_SIZES] = { 1024, 2 * 1024, 4 * 1024 }; +static const guint BLOCK_SIZES[NUM_SIZES] = { + 1 * 1024, + 2 * 1024, + 4 * 1024, + 16 * 1024, + 64 * 1024, + 512 * 1024, + 1 * 1024 * 1024, +}; static const gdouble RW_RATIOS[NUM_RW_RATIOS] = { 0.0, 0.25, 0.5, 0.75, 1.0 }; @@ -56,7 +64,6 @@ _benchmark_object_read_write(BenchmarkRun* run, gboolean use_batch, guint* patte g_autofree gchar* dummy = NULL; guint64 nb = 0; gboolean ret; - guint rcounter = 0, wcounter = 0; dummy = g_malloc0(block_size); @@ -73,7 +80,7 @@ _benchmark_object_read_write(BenchmarkRun* run, gboolean use_batch, guint* patte ret = j_batch_execute(batch); g_assert_true(ret); - g_assert_cmpuint(nb, ==, n * block_size); + g_assert_cmpuint(nb, ==, ((guint64)n) * block_size); j_benchmark_timer_start(run); @@ -85,12 +92,10 @@ _benchmark_object_read_write(BenchmarkRun* run, gboolean use_batch, guint* patte if (rw_pattern[i] == 0) { j_object_read(object, dummy, block_size, pattern[i] * block_size, &nb, batch); - rcounter++; } else { j_object_write(object, dummy, block_size, pattern[i] * block_size, &nb, batch); - wcounter++; } if (!use_batch) @@ -103,9 +108,11 @@ _benchmark_object_read_write(BenchmarkRun* run, gboolean use_batch, guint* patte if (use_batch) { + guint64 expected = n; ret = j_batch_execute(batch); + expected *= block_size; g_assert_true(ret); - g_assert_cmpuint(nb, ==, n * block_size); + g_assert_cmpuint(nb, ==, expected); } } @@ -159,7 +166,7 @@ _benchmark_object_write(BenchmarkRun* run, gboolean use_batch, guint* pattern, g { ret = j_batch_execute(batch); g_assert_true(ret); - g_assert_cmpuint(nb, ==, n * block_size); + g_assert_cmpuint(nb, ==, ((guint64)n) * block_size); } } @@ -331,10 +338,13 @@ benchmark_object_read_write(BenchmarkRun* run, gboolean use_batch, void (*genera gfloat ratio = RW_RATIOS[rw_index]; g_autofree guint* pattern = g_malloc0(n * sizeof(guint)); g_autofree guint* rw_pattern = g_malloc0(n * sizeof(guint)); - - index = (index + 1) % NUM_SIZES; rw_index = (rw_index + 1) % NUM_RW_RATIOS; + if (rw_index == 0) + { + index = (index + 1) % NUM_SIZES; + } + generate_rw_pattern(rw_pattern, n, ratio); (*generator)(pattern, n); @@ -425,7 +435,14 @@ add_rw_benches(const gchar* path, BenchmarkFunc benchmark) for (guint j = 0; j < NUM_RW_RATIOS; j++) { g_autofree gchar* buf = g_malloc0(64 * sizeof(gchar)); - g_snprintf(buf, 64ul, "%s <%.2fw> %dB", path, (double)RW_RATIOS[j], BLOCK_SIZES[i]); + if (BLOCK_SIZES[i] >= 1024 * 1024) + { + g_snprintf(buf, 64ul, "%s <%.2fw> %dMiB", path, (double)RW_RATIOS[j], BLOCK_SIZES[i] / (1024 * 1024)); + } + else + { + g_snprintf(buf, 64ul, "%s <%.2fw> %dKiB", path, (double)RW_RATIOS[j], BLOCK_SIZES[i] / 1024); + } j_benchmark_add(buf, benchmark); } @@ -438,8 +455,14 @@ add_benches(const gchar* path, BenchmarkFunc benchmark) for (guint i = 0; i < NUM_SIZES; i++) { g_autofree gchar* buf = g_malloc0(64 * sizeof(gchar)); - g_snprintf(buf, 64ul, "%s %dB", path, BLOCK_SIZES[i]); - + if (BLOCK_SIZES[i] >= 1024 * 1024) + { + g_snprintf(buf, 64ul, "%s %dMiB", path, BLOCK_SIZES[i] / (1024 * 1024)); + } + else + { + g_snprintf(buf, 64ul, "%s %dKiB", path, BLOCK_SIZES[i] / 1024); + } j_benchmark_add(buf, benchmark); } } diff --git a/benchmark/object/cluster-benches.c b/benchmark/object/cluster-benches.c new file mode 100644 index 000000000..a47fe280a --- /dev/null +++ b/benchmark/object/cluster-benches.c @@ -0,0 +1,445 @@ +/* + * JULEA - Flexible storage framework + * Copyright (C) 2017-2024 Michael Kuhn + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include + +#include + +#include + +#include +#include + +#include "benchmark.h" + +#define NUM_SIZES 9 + +#define NUM_RW_RATIOS 5 + +#define READ_FLAG 0 + +#define WRITE_FLAG 1 + +#define min(x, y) (((x) <= (y)) ? (x) : (y)) + +static guint index = 0; + +static guint rw_index = 0; + +static const guint BLOCK_SIZES[NUM_SIZES] = { + 1 * 1024, + 4 * 1024, + 16 * 1024, + 64 * 1024, + 128 * 1024, + 256 * 1024, + 512 * 1024, + 1 * 1024 * 1024, + 4 * 1024 * 1024 +}; + +static const gdouble RW_RATIOS[NUM_RW_RATIOS] = { 0.0, 0.25, 0.5, 0.75, 1.0 }; + +static const guint N = 1000; + +static void +_benchmark_object_read_write(BenchmarkRun* run, gboolean use_batch, guint* pattern, guint* rw_pattern, guint block_size, guint n) +{ + printf("BLock Size: %d\n", block_size); + + g_autoptr(JObject) object = NULL; + g_autoptr(JBatch) batch = NULL; + g_autoptr(JSemantics) semantics = NULL; + g_autofree gchar* dummy = NULL; + guint64 nb = 0; + gboolean ret; + + dummy = g_malloc0(block_size); + + semantics = j_benchmark_get_semantics(); + batch = j_batch_new(semantics); + + object = j_object_new("benchmark", "benchmark"); + j_object_create(object, batch); + + for (guint i = 0; i < n; i++) + { + j_object_write(object, dummy, block_size, i * block_size, &nb, batch); + } + + ret = j_batch_execute(batch); + g_assert_true(ret); + g_assert_cmpuint(nb, ==, ((guint64)n) * block_size); + + j_benchmark_timer_start(run); + + while (j_benchmark_iterate(run)) + { + for (guint i = 0; i < n; i++) + { + // decide read or write + if (rw_pattern[i] == 0) + { + j_object_read(object, dummy, block_size, pattern[i] * block_size, &nb, batch); + } + else + { + j_object_write(object, dummy, block_size, pattern[i] * block_size, &nb, batch); + } + + if (!use_batch) + { + ret = j_batch_execute(batch); + g_assert_true(ret); + g_assert_cmpuint(nb, ==, block_size); + } + } + + if (use_batch) + { + guint64 expected = n; + ret = j_batch_execute(batch); + expected *= block_size; + g_assert_true(ret); + g_assert_cmpuint(nb, ==, expected); + } + } + + j_benchmark_timer_stop(run); + + j_object_delete(object, batch); + ret = j_batch_execute(batch); + g_assert_true(ret); + + run->operations = n; + run->bytes = n * block_size; +} + +/* +Fills a buffer of a given length with incrementing values starting at 0. +*/ +static void +generate_pattern_seq(guint* buf, guint len) +{ + guint i; + for (i = 0; i < len; i++) + { + buf[i] = i; + } +} + +static void +shuffle(guint* buf, guint len) +{ + // set seed + srand(42); + + // shuffle + for (guint i = 0; i < len; i++) + { + guint j = rand() % (i + 1); + + guint temp = buf[j]; + buf[j] = buf[i]; + buf[i] = temp; + } +} + +static void +generate_rw_pattern(guint* buf, guint n, gdouble rw_ratio) +{ + guint read_ops = 0; + if (rw_ratio < 0.0001) + { + read_ops = n; + } + else if (rw_ratio > 0.9999) + { + read_ops = 0; + } + else + { + read_ops = (int)(rw_ratio * n); + } + + for (guint i = 0; i < n; i++) + { + if (i < read_ops) + { + buf[i] = READ_FLAG; + } + else + { + buf[i] = WRITE_FLAG; + } + } + + shuffle(buf, n); +} + +/* +Fills a buffer of a given length with the values in the range [0, len) shuffled using a set seed. +*/ +static void +generate_pattern_rand(guint* buf, guint len) +{ + // insert sequence + generate_pattern_seq(buf, len); + + shuffle(buf, len); +} + +/* +Calculates the nth harmonic number +`H_N = 𝚺(k=1, N) 1/k`. +*/ +static double +calc_harmonic(guint n) +{ + double sum = 0.0; + for (guint i = 1; i <= n; i++) + { + sum += (1.0 / i); + } + + return sum; +} + +/* +Fills a buffer of a given length with values in the range of [0, len] +using a distribution following Zipf's law. + +This is realized by adding the same value multiple times +based on the frequency derived from the value's rank. + +The range [0, len) serves as a basis with the range being considered to already be ordered. + +The resulting range is then shuffled. +*/ +static void +generate_pattern_zipf(guint* buf, guint len) +{ + guint j = 0; + const double HARMONIC = calc_harmonic(len); + + // generate template of randomly ordered indices + g_autofree guint* template = g_malloc0(len * sizeof(guint)); + generate_pattern_rand(template, len); + + // apply distribution + for (guint i = 0; i < len; i++) + { + double f = (1 / HARMONIC) * (1.0 / (i + 1)); + + // always round up to get atleast 1 occurence + guint n_occurences = (f * len) + 1; + + for (guint k = j; k < min(len, j + n_occurences); k++) + { + buf[k] = template[i]; + } + + j += n_occurences; + + if (j >= len) + { + break; + } + } + + // shuffle distribution + shuffle(buf, len); +} + +static void +benchmark_object_read_write(BenchmarkRun* run, gboolean use_batch, void (*generator)(guint*, guint)) +{ + guint n = (use_batch) ? 10 * N : N; + guint block_size = BLOCK_SIZES[index]; + gfloat ratio = RW_RATIOS[rw_index]; + g_autofree guint* pattern = g_malloc0(n * sizeof(guint)); + g_autofree guint* rw_pattern = g_malloc0(n * sizeof(guint)); + rw_index = (rw_index + 1) % NUM_RW_RATIOS; + + if (rw_index == 0) + { + index = (index + 1) % NUM_SIZES; + } + + generate_rw_pattern(rw_pattern, n, ratio); + (*generator)(pattern, n); + + _benchmark_object_read_write(run, use_batch, pattern, rw_pattern, block_size, n); +} + +static void +benchmark_object_read(BenchmarkRun* run, gboolean use_batch, void (*generator)(guint*, guint)) +{ + guint n = (use_batch) ? 10 * N : N; + guint block_size = BLOCK_SIZES[index]; + g_autofree guint* pattern = g_malloc0(n * sizeof(guint)); + g_autofree guint* rw_pattern = g_malloc0(n * sizeof(guint)); + + index = (index + 1) % NUM_SIZES; + + generate_rw_pattern(rw_pattern, n, 0.0); + (*generator)(pattern, n); + + _benchmark_object_read_write(run, use_batch, pattern, rw_pattern, block_size, n); +} + +static void +benchmark_object_write(BenchmarkRun* run, gboolean use_batch, void (*generator)(guint*, guint)) +{ + guint n = (use_batch) ? 10 * N : N; + guint block_size = BLOCK_SIZES[index]; + g_autofree guint* pattern = g_malloc0(n * sizeof(guint)); + g_autofree guint* rw_pattern = g_malloc0(n * sizeof(guint)); + + index = (index + 1) % NUM_SIZES; + + generate_rw_pattern(rw_pattern, n, 1.0); + (*generator)(pattern, n); + + _benchmark_object_read_write(run, use_batch, pattern, rw_pattern, block_size, n); +} + +// RW + +static void +benchmark_object_write_seq(BenchmarkRun* run) +{ + benchmark_object_write(run, FALSE, generate_pattern_seq); +} + +static void +benchmark_object_read_seq(BenchmarkRun* run) +{ + benchmark_object_read(run, FALSE, generate_pattern_seq); +} + +static void +benchmark_object_rw_batch_seq(BenchmarkRun* run) +{ + benchmark_object_read_write(run, TRUE, generate_pattern_seq); +} + +static void +benchmark_object_read_rand_batch(BenchmarkRun* run) +{ + benchmark_object_read(run, TRUE, generate_pattern_rand); +} + +static void +benchmark_object_read_zipf_batch(BenchmarkRun* run) +{ + benchmark_object_read(run, TRUE, generate_pattern_zipf); +} + +static void +benchmark_object_write_rand_batch(BenchmarkRun* run) +{ + benchmark_object_write(run, TRUE, generate_pattern_rand); +} + +static void +benchmark_object_write_zipf_batch(BenchmarkRun* run) +{ + benchmark_object_write(run, TRUE, generate_pattern_zipf); +} + +static void +add_write_benches(const gchar* path, BenchmarkFunc benchmark) +{ + for (guint i = 0; i < NUM_SIZES; i++) + { + g_autofree gchar* buf = g_malloc0(64 * sizeof(gchar)); + if (BLOCK_SIZES[i] >= 1024 * 1024) + { + g_snprintf(buf, 64ul, "%s <%.2fw> %dMiB", path, (double)1, BLOCK_SIZES[i] / (1024 * 1024)); + } + else + { + g_snprintf(buf, 64ul, "%s <%.2fw> %dKiB", path, (double)1, BLOCK_SIZES[i] / 1024); + } + + j_benchmark_add(buf, benchmark); + } +} + +static void +add_read_benches(const gchar* path, BenchmarkFunc benchmark) +{ + for (guint i = 0; i < NUM_SIZES; i++) + { + g_autofree gchar* buf = g_malloc0(64 * sizeof(gchar)); + if (BLOCK_SIZES[i] >= 1024 * 1024) + { + g_snprintf(buf, 64ul, "%s <%.2fw> %dMiB", path, (double)0, BLOCK_SIZES[i] / (1024 * 1024)); + } + else + { + g_snprintf(buf, 64ul, "%s <%.2fw> %dKiB", path, (double)0, BLOCK_SIZES[i] / 1024); + } + + j_benchmark_add(buf, benchmark); + } +} + +static void +add_rw_benches(const gchar* path, BenchmarkFunc benchmark) +{ + for (guint i = 0; i < NUM_SIZES; i++) + { + for (guint j = 0; j < NUM_RW_RATIOS; j++) + { + g_autofree gchar* buf = g_malloc0(64 * sizeof(gchar)); + if (BLOCK_SIZES[i] >= 1024 * 1024) + { + g_snprintf(buf, 64ul, "%s <%.2fw> %dMiB", path, (double)RW_RATIOS[j], BLOCK_SIZES[i] / (1024 * 1024)); + } + else + { + g_snprintf(buf, 64ul, "%s <%.2fw> %dKiB", path, (double)RW_RATIOS[j], BLOCK_SIZES[i] / 1024); + } + + j_benchmark_add(buf, benchmark); + } + } +} + +void +benchmark_object_cluster(void) +{ + // SINGLE + add_read_benches("/object/object/cluster/seq", benchmark_object_read_seq); + add_write_benches("/object/object/cluster/seq", benchmark_object_write_seq); + + // BATCH + + // MIXED + add_rw_benches("/object/object/cluster/seq-batch", benchmark_object_rw_batch_seq); + + // READ + add_read_benches("/object/object/cluster/rand-batch", benchmark_object_read_rand_batch); + add_read_benches("/object/object/cluster/zipf-batch", benchmark_object_read_zipf_batch); + + // WRITE + add_write_benches("/object/object/cluster/rand-batch", benchmark_object_write_rand_batch); + add_write_benches("/object/object/cluster/zipf-batch", benchmark_object_write_zipf_batch); +} diff --git a/meson.build b/meson.build index 9e9ebd7c8..b550199ba 100644 --- a/meson.build +++ b/meson.build @@ -586,6 +586,7 @@ julea_benchmark_srcs = files([ 'benchmark/object/distributed-object.c', 'benchmark/object/object.c', 'benchmark/object/bench-object-rw.c', + 'benchmark/object/cluster-benches.c' ]) executable('julea-benchmark', julea_benchmark_srcs, From effad3cd48caf813e333b50931b17a94566da949 Mon Sep 17 00:00:00 2001 From: Konrad Ueltzen Date: Fri, 31 May 2024 19:45:54 +0200 Subject: [PATCH 7/9] Update benchmarks --- benchmark/object/cluster-benches.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/benchmark/object/cluster-benches.c b/benchmark/object/cluster-benches.c index a47fe280a..aff6d6616 100644 --- a/benchmark/object/cluster-benches.c +++ b/benchmark/object/cluster-benches.c @@ -60,8 +60,6 @@ static const guint N = 1000; static void _benchmark_object_read_write(BenchmarkRun* run, gboolean use_batch, guint* pattern, guint* rw_pattern, guint block_size, guint n) { - printf("BLock Size: %d\n", block_size); - g_autoptr(JObject) object = NULL; g_autoptr(JBatch) batch = NULL; g_autoptr(JSemantics) semantics = NULL; @@ -363,12 +361,25 @@ benchmark_object_write_zipf_batch(BenchmarkRun* run) benchmark_object_write(run, TRUE, generate_pattern_zipf); } +static void +benchmark_object_write_rand(BenchmarkRun* run) +{ + benchmark_object_write(run, FALSE, generate_pattern_rand); +} + +static void +benchmark_object_write_zipf(BenchmarkRun* run) +{ + benchmark_object_write(run, FALSE, generate_pattern_zipf); +} + static void add_write_benches(const gchar* path, BenchmarkFunc benchmark) { for (guint i = 0; i < NUM_SIZES; i++) { g_autofree gchar* buf = g_malloc0(64 * sizeof(gchar)); + if (BLOCK_SIZES[i] >= 1024 * 1024) { g_snprintf(buf, 64ul, "%s <%.2fw> %dMiB", path, (double)1, BLOCK_SIZES[i] / (1024 * 1024)); @@ -429,6 +440,8 @@ benchmark_object_cluster(void) // SINGLE add_read_benches("/object/object/cluster/seq", benchmark_object_read_seq); add_write_benches("/object/object/cluster/seq", benchmark_object_write_seq); + add_write_benches("/object/object/custer/rand", benchmark_object_write_rand); + add_write_benches("/object/object/cluster/zipf", benchmark_object_write_zipf); // BATCH From f95d7ae579150af2bb6247bccc0f93d9db33ffc2 Mon Sep 17 00:00:00 2001 From: Konrad Ueltzen Date: Tue, 4 Jun 2024 10:42:02 +0200 Subject: [PATCH 8/9] Sanitization --- benchmark/object/cluster-benches.c | 458 ----------------------------- 1 file changed, 458 deletions(-) delete mode 100644 benchmark/object/cluster-benches.c diff --git a/benchmark/object/cluster-benches.c b/benchmark/object/cluster-benches.c deleted file mode 100644 index aff6d6616..000000000 --- a/benchmark/object/cluster-benches.c +++ /dev/null @@ -1,458 +0,0 @@ -/* - * JULEA - Flexible storage framework - * Copyright (C) 2017-2024 Michael Kuhn - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include - -#include - -#include - -#include -#include - -#include "benchmark.h" - -#define NUM_SIZES 9 - -#define NUM_RW_RATIOS 5 - -#define READ_FLAG 0 - -#define WRITE_FLAG 1 - -#define min(x, y) (((x) <= (y)) ? (x) : (y)) - -static guint index = 0; - -static guint rw_index = 0; - -static const guint BLOCK_SIZES[NUM_SIZES] = { - 1 * 1024, - 4 * 1024, - 16 * 1024, - 64 * 1024, - 128 * 1024, - 256 * 1024, - 512 * 1024, - 1 * 1024 * 1024, - 4 * 1024 * 1024 -}; - -static const gdouble RW_RATIOS[NUM_RW_RATIOS] = { 0.0, 0.25, 0.5, 0.75, 1.0 }; - -static const guint N = 1000; - -static void -_benchmark_object_read_write(BenchmarkRun* run, gboolean use_batch, guint* pattern, guint* rw_pattern, guint block_size, guint n) -{ - g_autoptr(JObject) object = NULL; - g_autoptr(JBatch) batch = NULL; - g_autoptr(JSemantics) semantics = NULL; - g_autofree gchar* dummy = NULL; - guint64 nb = 0; - gboolean ret; - - dummy = g_malloc0(block_size); - - semantics = j_benchmark_get_semantics(); - batch = j_batch_new(semantics); - - object = j_object_new("benchmark", "benchmark"); - j_object_create(object, batch); - - for (guint i = 0; i < n; i++) - { - j_object_write(object, dummy, block_size, i * block_size, &nb, batch); - } - - ret = j_batch_execute(batch); - g_assert_true(ret); - g_assert_cmpuint(nb, ==, ((guint64)n) * block_size); - - j_benchmark_timer_start(run); - - while (j_benchmark_iterate(run)) - { - for (guint i = 0; i < n; i++) - { - // decide read or write - if (rw_pattern[i] == 0) - { - j_object_read(object, dummy, block_size, pattern[i] * block_size, &nb, batch); - } - else - { - j_object_write(object, dummy, block_size, pattern[i] * block_size, &nb, batch); - } - - if (!use_batch) - { - ret = j_batch_execute(batch); - g_assert_true(ret); - g_assert_cmpuint(nb, ==, block_size); - } - } - - if (use_batch) - { - guint64 expected = n; - ret = j_batch_execute(batch); - expected *= block_size; - g_assert_true(ret); - g_assert_cmpuint(nb, ==, expected); - } - } - - j_benchmark_timer_stop(run); - - j_object_delete(object, batch); - ret = j_batch_execute(batch); - g_assert_true(ret); - - run->operations = n; - run->bytes = n * block_size; -} - -/* -Fills a buffer of a given length with incrementing values starting at 0. -*/ -static void -generate_pattern_seq(guint* buf, guint len) -{ - guint i; - for (i = 0; i < len; i++) - { - buf[i] = i; - } -} - -static void -shuffle(guint* buf, guint len) -{ - // set seed - srand(42); - - // shuffle - for (guint i = 0; i < len; i++) - { - guint j = rand() % (i + 1); - - guint temp = buf[j]; - buf[j] = buf[i]; - buf[i] = temp; - } -} - -static void -generate_rw_pattern(guint* buf, guint n, gdouble rw_ratio) -{ - guint read_ops = 0; - if (rw_ratio < 0.0001) - { - read_ops = n; - } - else if (rw_ratio > 0.9999) - { - read_ops = 0; - } - else - { - read_ops = (int)(rw_ratio * n); - } - - for (guint i = 0; i < n; i++) - { - if (i < read_ops) - { - buf[i] = READ_FLAG; - } - else - { - buf[i] = WRITE_FLAG; - } - } - - shuffle(buf, n); -} - -/* -Fills a buffer of a given length with the values in the range [0, len) shuffled using a set seed. -*/ -static void -generate_pattern_rand(guint* buf, guint len) -{ - // insert sequence - generate_pattern_seq(buf, len); - - shuffle(buf, len); -} - -/* -Calculates the nth harmonic number -`H_N = 𝚺(k=1, N) 1/k`. -*/ -static double -calc_harmonic(guint n) -{ - double sum = 0.0; - for (guint i = 1; i <= n; i++) - { - sum += (1.0 / i); - } - - return sum; -} - -/* -Fills a buffer of a given length with values in the range of [0, len] -using a distribution following Zipf's law. - -This is realized by adding the same value multiple times -based on the frequency derived from the value's rank. - -The range [0, len) serves as a basis with the range being considered to already be ordered. - -The resulting range is then shuffled. -*/ -static void -generate_pattern_zipf(guint* buf, guint len) -{ - guint j = 0; - const double HARMONIC = calc_harmonic(len); - - // generate template of randomly ordered indices - g_autofree guint* template = g_malloc0(len * sizeof(guint)); - generate_pattern_rand(template, len); - - // apply distribution - for (guint i = 0; i < len; i++) - { - double f = (1 / HARMONIC) * (1.0 / (i + 1)); - - // always round up to get atleast 1 occurence - guint n_occurences = (f * len) + 1; - - for (guint k = j; k < min(len, j + n_occurences); k++) - { - buf[k] = template[i]; - } - - j += n_occurences; - - if (j >= len) - { - break; - } - } - - // shuffle distribution - shuffle(buf, len); -} - -static void -benchmark_object_read_write(BenchmarkRun* run, gboolean use_batch, void (*generator)(guint*, guint)) -{ - guint n = (use_batch) ? 10 * N : N; - guint block_size = BLOCK_SIZES[index]; - gfloat ratio = RW_RATIOS[rw_index]; - g_autofree guint* pattern = g_malloc0(n * sizeof(guint)); - g_autofree guint* rw_pattern = g_malloc0(n * sizeof(guint)); - rw_index = (rw_index + 1) % NUM_RW_RATIOS; - - if (rw_index == 0) - { - index = (index + 1) % NUM_SIZES; - } - - generate_rw_pattern(rw_pattern, n, ratio); - (*generator)(pattern, n); - - _benchmark_object_read_write(run, use_batch, pattern, rw_pattern, block_size, n); -} - -static void -benchmark_object_read(BenchmarkRun* run, gboolean use_batch, void (*generator)(guint*, guint)) -{ - guint n = (use_batch) ? 10 * N : N; - guint block_size = BLOCK_SIZES[index]; - g_autofree guint* pattern = g_malloc0(n * sizeof(guint)); - g_autofree guint* rw_pattern = g_malloc0(n * sizeof(guint)); - - index = (index + 1) % NUM_SIZES; - - generate_rw_pattern(rw_pattern, n, 0.0); - (*generator)(pattern, n); - - _benchmark_object_read_write(run, use_batch, pattern, rw_pattern, block_size, n); -} - -static void -benchmark_object_write(BenchmarkRun* run, gboolean use_batch, void (*generator)(guint*, guint)) -{ - guint n = (use_batch) ? 10 * N : N; - guint block_size = BLOCK_SIZES[index]; - g_autofree guint* pattern = g_malloc0(n * sizeof(guint)); - g_autofree guint* rw_pattern = g_malloc0(n * sizeof(guint)); - - index = (index + 1) % NUM_SIZES; - - generate_rw_pattern(rw_pattern, n, 1.0); - (*generator)(pattern, n); - - _benchmark_object_read_write(run, use_batch, pattern, rw_pattern, block_size, n); -} - -// RW - -static void -benchmark_object_write_seq(BenchmarkRun* run) -{ - benchmark_object_write(run, FALSE, generate_pattern_seq); -} - -static void -benchmark_object_read_seq(BenchmarkRun* run) -{ - benchmark_object_read(run, FALSE, generate_pattern_seq); -} - -static void -benchmark_object_rw_batch_seq(BenchmarkRun* run) -{ - benchmark_object_read_write(run, TRUE, generate_pattern_seq); -} - -static void -benchmark_object_read_rand_batch(BenchmarkRun* run) -{ - benchmark_object_read(run, TRUE, generate_pattern_rand); -} - -static void -benchmark_object_read_zipf_batch(BenchmarkRun* run) -{ - benchmark_object_read(run, TRUE, generate_pattern_zipf); -} - -static void -benchmark_object_write_rand_batch(BenchmarkRun* run) -{ - benchmark_object_write(run, TRUE, generate_pattern_rand); -} - -static void -benchmark_object_write_zipf_batch(BenchmarkRun* run) -{ - benchmark_object_write(run, TRUE, generate_pattern_zipf); -} - -static void -benchmark_object_write_rand(BenchmarkRun* run) -{ - benchmark_object_write(run, FALSE, generate_pattern_rand); -} - -static void -benchmark_object_write_zipf(BenchmarkRun* run) -{ - benchmark_object_write(run, FALSE, generate_pattern_zipf); -} - -static void -add_write_benches(const gchar* path, BenchmarkFunc benchmark) -{ - for (guint i = 0; i < NUM_SIZES; i++) - { - g_autofree gchar* buf = g_malloc0(64 * sizeof(gchar)); - - if (BLOCK_SIZES[i] >= 1024 * 1024) - { - g_snprintf(buf, 64ul, "%s <%.2fw> %dMiB", path, (double)1, BLOCK_SIZES[i] / (1024 * 1024)); - } - else - { - g_snprintf(buf, 64ul, "%s <%.2fw> %dKiB", path, (double)1, BLOCK_SIZES[i] / 1024); - } - - j_benchmark_add(buf, benchmark); - } -} - -static void -add_read_benches(const gchar* path, BenchmarkFunc benchmark) -{ - for (guint i = 0; i < NUM_SIZES; i++) - { - g_autofree gchar* buf = g_malloc0(64 * sizeof(gchar)); - if (BLOCK_SIZES[i] >= 1024 * 1024) - { - g_snprintf(buf, 64ul, "%s <%.2fw> %dMiB", path, (double)0, BLOCK_SIZES[i] / (1024 * 1024)); - } - else - { - g_snprintf(buf, 64ul, "%s <%.2fw> %dKiB", path, (double)0, BLOCK_SIZES[i] / 1024); - } - - j_benchmark_add(buf, benchmark); - } -} - -static void -add_rw_benches(const gchar* path, BenchmarkFunc benchmark) -{ - for (guint i = 0; i < NUM_SIZES; i++) - { - for (guint j = 0; j < NUM_RW_RATIOS; j++) - { - g_autofree gchar* buf = g_malloc0(64 * sizeof(gchar)); - if (BLOCK_SIZES[i] >= 1024 * 1024) - { - g_snprintf(buf, 64ul, "%s <%.2fw> %dMiB", path, (double)RW_RATIOS[j], BLOCK_SIZES[i] / (1024 * 1024)); - } - else - { - g_snprintf(buf, 64ul, "%s <%.2fw> %dKiB", path, (double)RW_RATIOS[j], BLOCK_SIZES[i] / 1024); - } - - j_benchmark_add(buf, benchmark); - } - } -} - -void -benchmark_object_cluster(void) -{ - // SINGLE - add_read_benches("/object/object/cluster/seq", benchmark_object_read_seq); - add_write_benches("/object/object/cluster/seq", benchmark_object_write_seq); - add_write_benches("/object/object/custer/rand", benchmark_object_write_rand); - add_write_benches("/object/object/cluster/zipf", benchmark_object_write_zipf); - - // BATCH - - // MIXED - add_rw_benches("/object/object/cluster/seq-batch", benchmark_object_rw_batch_seq); - - // READ - add_read_benches("/object/object/cluster/rand-batch", benchmark_object_read_rand_batch); - add_read_benches("/object/object/cluster/zipf-batch", benchmark_object_read_zipf_batch); - - // WRITE - add_write_benches("/object/object/cluster/rand-batch", benchmark_object_write_rand_batch); - add_write_benches("/object/object/cluster/zipf-batch", benchmark_object_write_zipf_batch); -} From 3e298c4d00180f013b9e06c40d4c007b3bb6aecc Mon Sep 17 00:00:00 2001 From: Konrad Ueltzen Date: Tue, 4 Jun 2024 11:12:59 +0200 Subject: [PATCH 9/9] Replace r/w --- benchmark/benchmark.c | 1 - benchmark/benchmark.h | 2 - benchmark/object/object.c | 147 -------------------------------------- meson.build | 3 +- 4 files changed, 1 insertion(+), 152 deletions(-) diff --git a/benchmark/benchmark.c b/benchmark/benchmark.c index fb3d0a74f..1c1be724f 100644 --- a/benchmark/benchmark.c +++ b/benchmark/benchmark.c @@ -359,7 +359,6 @@ main(int argc, char** argv) benchmark_object(); // additional read-write benches benchmark_object_rw(); - benchmark_object_cluster(); // DB client benchmark_db_entry(); diff --git a/benchmark/benchmark.h b/benchmark/benchmark.h index 7a4a47459..5a74be43c 100644 --- a/benchmark/benchmark.h +++ b/benchmark/benchmark.h @@ -30,7 +30,6 @@ struct BenchmarkRun guint iterations; guint64 operations; guint64 bytes; - void* additional_data; }; typedef struct BenchmarkRun BenchmarkRun; @@ -58,7 +57,6 @@ void benchmark_kv(void); void benchmark_distributed_object(void); void benchmark_object(void); void benchmark_object_rw(void); -void benchmark_object_cluster(void); void benchmark_db_entry(void); void benchmark_db_iterator(void); diff --git a/benchmark/object/object.c b/benchmark/object/object.c index 8dd8df960..8af6dfc50 100644 --- a/benchmark/object/object.c +++ b/benchmark/object/object.c @@ -228,149 +228,6 @@ benchmark_object_status_batch(BenchmarkRun* run) _benchmark_object_status(run, TRUE); } -static void -_benchmark_object_read(BenchmarkRun* run, gboolean use_batch, guint block_size) -{ - guint const n = (use_batch) ? 10000 : 1000; - - g_autoptr(JObject) object = NULL; - g_autoptr(JBatch) batch = NULL; - g_autoptr(JSemantics) semantics = NULL; - g_autofree gchar* dummy = NULL; - guint64 nb = 0; - gboolean ret; - - dummy = g_malloc0(block_size); - - semantics = j_benchmark_get_semantics(); - batch = j_batch_new(semantics); - - object = j_object_new("benchmark", "benchmark"); - j_object_create(object, batch); - - for (guint i = 0; i < n; i++) - { - j_object_write(object, dummy, block_size, i * block_size, &nb, batch); - } - - ret = j_batch_execute(batch); - g_assert_true(ret); - g_assert_cmpuint(nb, ==, n * block_size); - - j_benchmark_timer_start(run); - - while (j_benchmark_iterate(run)) - { - for (guint i = 0; i < n; i++) - { - j_object_read(object, dummy, block_size, i * block_size, &nb, batch); - - if (!use_batch) - { - ret = j_batch_execute(batch); - g_assert_true(ret); - g_assert_cmpuint(nb, ==, block_size); - } - } - - if (use_batch) - { - ret = j_batch_execute(batch); - g_assert_true(ret); - g_assert_cmpuint(nb, ==, n * block_size); - } - } - - j_benchmark_timer_stop(run); - - j_object_delete(object, batch); - ret = j_batch_execute(batch); - g_assert_true(ret); - - run->operations = n; - run->bytes = n * block_size; -} - -static void -benchmark_object_read(BenchmarkRun* run) -{ - _benchmark_object_read(run, FALSE, 4 * 1024); -} - -static void -benchmark_object_read_batch(BenchmarkRun* run) -{ - _benchmark_object_read(run, TRUE, 4 * 1024); -} - -static void -_benchmark_object_write(BenchmarkRun* run, gboolean use_batch, guint block_size) -{ - guint const n = (use_batch) ? 10000 : 1000; - - g_autoptr(JObject) object = NULL; - g_autoptr(JBatch) batch = NULL; - g_autoptr(JSemantics) semantics = NULL; - g_autofree gchar* dummy = NULL; - guint64 nb = 0; - gboolean ret; - - dummy = g_malloc0(block_size); - - semantics = j_benchmark_get_semantics(); - batch = j_batch_new(semantics); - - object = j_object_new("benchmark", "benchmark"); - j_object_create(object, batch); - ret = j_batch_execute(batch); - g_assert_true(ret); - - j_benchmark_timer_start(run); - - while (j_benchmark_iterate(run)) - { - for (guint i = 0; i < n; i++) - { - j_object_write(object, dummy, block_size, i * block_size, &nb, batch); - - if (!use_batch) - { - ret = j_batch_execute(batch); - g_assert_true(ret); - g_assert_cmpuint(nb, ==, block_size); - } - } - - if (use_batch) - { - ret = j_batch_execute(batch); - g_assert_true(ret); - g_assert_cmpuint(nb, ==, n * block_size); - } - } - - j_benchmark_timer_stop(run); - - j_object_delete(object, batch); - ret = j_batch_execute(batch); - g_assert_true(ret); - - run->operations = n; - run->bytes = n * block_size; -} - -static void -benchmark_object_write(BenchmarkRun* run) -{ - _benchmark_object_write(run, FALSE, 4 * 1024); -} - -static void -benchmark_object_write_batch(BenchmarkRun* run) -{ - _benchmark_object_write(run, TRUE, 4 * 1024); -} - static void _benchmark_object_unordered_create_delete(BenchmarkRun* run, gboolean use_batch) { @@ -438,10 +295,6 @@ benchmark_object(void) j_benchmark_add("/object/object/status", benchmark_object_status); j_benchmark_add("/object/object/status-batch", benchmark_object_status_batch); /// \todo get - j_benchmark_add("/object/object/read", benchmark_object_read); - j_benchmark_add("/object/object/read-batch", benchmark_object_read_batch); - j_benchmark_add("/object/object/write", benchmark_object_write); - j_benchmark_add("/object/object/write-batch", benchmark_object_write_batch); j_benchmark_add("/object/object/unordered-create-delete", benchmark_object_unordered_create_delete); j_benchmark_add("/object/object/unordered-create-delete-batch", benchmark_object_unordered_create_delete_batch); } diff --git a/meson.build b/meson.build index b550199ba..2ad42e386 100644 --- a/meson.build +++ b/meson.build @@ -585,8 +585,7 @@ julea_benchmark_srcs = files([ 'benchmark/message.c', 'benchmark/object/distributed-object.c', 'benchmark/object/object.c', - 'benchmark/object/bench-object-rw.c', - 'benchmark/object/cluster-benches.c' + 'benchmark/object/bench-object-rw.c' ]) executable('julea-benchmark', julea_benchmark_srcs,