diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/README | 27 | ||||
-rw-r--r-- | examples/credel.c | 484 | ||||
-rw-r--r-- | examples/gaussians.c | 759 | ||||
-rw-r--r-- | examples/nbcompat.c | 17 | ||||
-rw-r--r-- | examples/nbcompat.h | 36 |
5 files changed, 1323 insertions, 0 deletions
diff --git a/examples/README b/examples/README index e0a3364..4ab596d 100644 --- a/examples/README +++ b/examples/README @@ -15,3 +15,30 @@ installed. Compile scripts from other locations can be used by setting an environment variable prefix to the path of the directory containing the bin directory with the compile scripts h5cc, h5fc, etc. For example, export prefix=/usr/local/hdf5 to use h5cc, h5fc, etc. in /usr/local/hdf5/bin. + +*************************************************************************************** +Instruction for compiling and running the two VFD SWMR demo programs in this directory: + +credel.c: +========= +To compile: + h5cc -o credel credel.c nbcompat.c + +To run: + ./credel -v (on one window) + h5ls --vfd=swmr --poll=100 -r -d ./credel.h5 (on another window) + +vfd_swmr_gaussians.c +==================== +To compile: + h5cc -o gaussians ./gaussians.c ./nbcompat.c -lcurses +To link as writer: + ln -s gaussians wgaussians +To link as reader: + ln -s gaussians rgaussians + +To run standalone: + ./gaussians +To run as writer and reader: + ./wgaussians (on one window) + ./rgaussians (on another window) diff --git a/examples/credel.c b/examples/credel.c new file mode 100644 index 0000000..218baeb --- /dev/null +++ b/examples/credel.c @@ -0,0 +1,484 @@ +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <libgen.h> /* basename(3) */ +#include <signal.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> /* struct timespec, nanosleep */ +#include <unistd.h> /* getopt, PATH_MAX, ... */ + +#include "hdf5.h" +#include "nbcompat.h" + +#define DATAROWS 1 +#define DATACOLS 10 +static const hsize_t dims[2] = {DATAROWS, DATACOLS}; +static const hsize_t chunk_dims[2] = {1, 1}; +static volatile sig_atomic_t unbroken = 1; + +typedef struct { + uint64_t created, deleted; +} stats_pair_t; + +typedef struct { + stats_pair_t datasets, groups; + uint64_t iterations; +} stats_t; + +typedef struct { + hid_t dataset[4], dataspace, dapl, dcpl, file, group[2]; + char output_file[PATH_MAX]; + char progname[PATH_MAX]; + struct timespec update_interval; + int verbose; + bool oneshot; + bool print_stats; + bool use_vfd_swmr; + uint64_t iterations_limit; + stats_t stats; +} state_t; + +#define ALL_HID_INITIALIZER (state_t){ \ + .dataspace = H5I_INVALID_HID \ + , .file = H5I_INVALID_HID \ + , .verbose = 0 \ + , .oneshot = false \ + , .use_vfd_swmr = true \ + , .print_stats = false \ + , .iterations_limit = UINT64_MAX \ + , .output_file = "" \ + , .update_interval = (struct timespec){ \ + .tv_sec = 0 \ + , .tv_nsec = 1000000000UL / 10 /* 1/10 second */} \ + , .stats = {{0, 0}, {0, 0}}} + +static void state_init(state_t *, int, char **); + +static void +write_dataset(state_t *s, int didx) +{ + const int ndatasets = __arraycount(s->dataset); + hid_t ds; + int32_t data[DATAROWS][DATACOLS]; + herr_t status; + unsigned int i, j; + + for (i = 0; i < __arraycount(data); i++) { + for (j = 0; j < __arraycount(data[i]); j++) { + int k = (didx + j + i) % __arraycount(data[i]); + data[i][j] = (0 <= k && k < 3) ? 1 : 0; + if (s->verbose > 1) + fprintf(stderr, " %" PRId32, data[i][j]); + } + if (s->verbose > 1) + fprintf(stderr, "\n"); + } + + ds = s->dataset[didx % ndatasets]; + status = H5Dwrite(ds, H5T_NATIVE_INT32, H5S_ALL, H5S_ALL, + H5P_DEFAULT, data); + + if (status < 0) + errx(EXIT_FAILURE, "H5Dwrite failed"); + + if (H5Dflush(ds) < 0) + errx(EXIT_FAILURE, "H5Dflush failed"); +} + +static void +usage(const char *progname) +{ + fprintf(stderr, "usage: %s [-u milliseconds]\n" + "\n" + "-o: oneshot mode, perform one iteration, wait for a signal, " + "then quit.\n" + "-s: print statistics at end\n" + "-S: disable VFD SWMR mode\n" + "-u ms: milliseconds interval between updates to %s.h5\n" + "-v: verbose mode, mention creation/deletion; -vv: print data\n" + "\n", + progname, progname); + exit(EXIT_FAILURE); +} + +static void +print_stats(stats_t *s) +{ + printf("%10" PRIu64 " groups created\n", s->groups.created); + printf("%10" PRIu64 " groups deleted\n", s->groups.deleted); + printf("%10" PRIu64 " datasets created\n", s->datasets.created); + printf("%10" PRIu64 " datasets deleted\n", s->datasets.deleted); + printf("%10" PRIu64 " iterations\n", s->iterations); +} + +static void +state_init(state_t *s, int argc, char **argv) +{ + int ch, i, rc; + char tfile[PATH_MAX]; + char *end; + unsigned long millis; + uintmax_t niters; + + *s = ALL_HID_INITIALIZER; + strlcpy(tfile, argv[0], sizeof(tfile)); + strlcpy(s->progname, basename(tfile), sizeof(s->progname)); + while ((ch = getopt(argc, argv, "n:ou:sSv")) != -1) { + switch (ch) { + case 'n': + niters = strtoumax(optarg, &end, 0); + if (niters == UINTMAX_MAX && errno == ERANGE) { + err(EXIT_FAILURE, "option -%c argument \"%s\"", + ch, optarg); + } else if (*end != '\0') { + errx(EXIT_FAILURE, + "garbage after -%c argument \"%s\"", ch, + optarg); + } + s->iterations_limit = niters; + break; + case 'o': + s->oneshot = true; + break; + case 's': + s->print_stats = true; + break; + case 'S': + s->use_vfd_swmr = false; + break; + case 'u': + errno = 0; + millis = strtoul(optarg, &end, 0); + if (millis == ULONG_MAX && errno == ERANGE) { + err(EXIT_FAILURE, "option -%c argument \"%s\"", + ch, optarg); + } else if (*end != '\0') { + errx(EXIT_FAILURE, + "garbage after -%c argument \"%s\"", ch, + optarg); + } + s->update_interval.tv_sec = millis / 1000UL; + s->update_interval.tv_nsec = (millis * 1000000UL) % 1000000000UL; + warnx("%lu milliseconds between updates", millis); + break; + case 'v': + s->verbose++; + break; + case '?': + default: + usage(s->progname); + } + } + argc -= optind; + argv += optind; + + for (i = 0; i < __arraycount(s->dataset); i++) + s->dataset[i] = H5I_INVALID_HID; + + for (i = 0; i < __arraycount(s->group); i++) + s->group[i] = H5I_INVALID_HID; + + rc = snprintf(s->output_file, sizeof(s->output_file), "%s.h5", s->progname); + if (rc == -1 || rc >= sizeof(s->output_file)) + errx(EXIT_FAILURE, "output filename was truncated"); +} + +static void +delete_group(state_t *s, const int64_t gidx) +{ + hid_t g; + const int ngroups = __arraycount(s->group); + char gname[32]; + + assert(0 <= gidx); + + snprintf(gname, sizeof(gname), "/group-%" PRId64, gidx); + g = s->group[gidx % ngroups]; + + if (H5Ldelete(s->file, gname, H5P_DEFAULT) < 0) { + errx(EXIT_FAILURE, "%s: H5Ldelete(, \"%s\", ) failed", + __func__, gname); + } + + if (H5Gclose(g) < 0) + errx(EXIT_FAILURE, "H5Gclose failed"); + + if (s->verbose > 0) + fprintf(stderr, "Deleted group %s\n", gname); + + s->group[gidx % ngroups] = H5I_INVALID_HID; + s->stats.groups.deleted++; +} + +static void +create_group(state_t *s, const int64_t gidx) +{ + const int ngroups = __arraycount(s->group); + hid_t g; + char gname[32]; + + assert(0 <= gidx); + assert(s->group[gidx % ngroups] == H5I_INVALID_HID); + + snprintf(gname, sizeof(gname), "/group-%" PRId64, gidx); + g = H5Gcreate(s->file, gname, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + if (g < 0) + errx(EXIT_FAILURE, "H5Gcreate failed"); + + if (s->verbose > 0) + fprintf(stderr, "Created group %s\n", gname); + s->group[gidx % ngroups] = g; + s->stats.groups.created++; +} + +static void +delete_dataset(state_t *s, const int64_t didx) +{ + const int ndatasets = __arraycount(s->dataset); + char dname[32]; + const int64_t gidx = didx / 2; + + assert(0 <= gidx && 0 <= didx); + + snprintf(dname, sizeof(dname), "/group-%" PRId64 "/dataset-%" PRId64, + gidx, didx); + if (H5Ldelete(s->file, dname, H5P_DEFAULT) < 0) { + errx(EXIT_FAILURE, "%s: H5Ldelete(, \"%s\", ) failed", + __func__, dname); + } + + if (s->verbose > 0) + fprintf(stderr, "Deleted dataset %s\n", dname); +#if 1 + const hid_t ds = s->dataset[didx % ndatasets]; + if (H5Dclose(ds) < 0) + errx(EXIT_FAILURE, "H5Dclose failed"); +#endif + s->dataset[didx % ndatasets] = H5I_INVALID_HID; + s->stats.datasets.deleted++; +} + +static void +create_dataset(state_t *s, const int64_t didx) +{ + const int ndatasets = __arraycount(s->dataset); + char dname[32]; + const int64_t gidx = didx / 2; + hid_t ds; + + assert(0 <= gidx && 0 <= didx); + assert(s->dataset[didx % ndatasets] == H5I_INVALID_HID); + + s->dataspace = H5Screate_simple(__arraycount(dims), dims, NULL); + + if (s->dataspace < 0) + errx(EXIT_FAILURE, "H5Screate_simple failed"); + + snprintf(dname, sizeof(dname), "/group-%" PRId64 "/dataset-%" PRId64, + gidx, didx); + ds = H5Dcreate2(s->file, dname, + H5T_STD_I32BE, s->dataspace, + H5P_DEFAULT, s->dcpl, s->dapl); + + if (H5Sclose(s->dataspace) < 0) + errx(EXIT_FAILURE, "H5Sclose failed"); + + s->dataspace = H5I_INVALID_HID; + + if (ds < 0) + errx(EXIT_FAILURE, "H5Dcreate(, \"%s\", ) failed", dname); + + if (s->verbose > 0) + fprintf(stderr, "Created dataset %s\n", dname); + s->dataset[didx % ndatasets] = ds; + s->stats.datasets.created++; +} + +static void +create_and_write_dataset(state_t *s, const int64_t didx) +{ +#if 0 + const int64_t gidx = didx / 2; + const int ngroups = __arraycount(s->group); + const hid_t g = s->group[gidx % ngroups]; + + if (H5Odisable_mdc_flushes(g) < 0) + err(EXIT_FAILURE, "H5Odisable_mdc_flushes failed"); +#endif + + create_dataset(s, didx); + write_dataset(s, didx); + +#if 0 + if (H5Oenable_mdc_flushes(g) < 0) + err(EXIT_FAILURE, "H5Oenable_mdc_flushes failed"); +#endif +} + +static void +handle_signal(int signo) +{ + char msg[] = "Handling signal\n"; + write(STDERR_FILENO, msg, sizeof(msg) - 1); + unbroken = 0; +} + +static void +disestablish_handler(const struct sigaction *osa) +{ + if (sigaction(SIGINT, osa, NULL) == -1) + err(EXIT_FAILURE, "%s: sigaction", __func__); +} + +static void +establish_handler(struct sigaction *osa) +{ + struct sigaction sa; + + memset(&sa, '\0', sizeof(sa)); + sa.sa_handler = handle_signal; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGINT, &sa, osa) == -1) + err(EXIT_FAILURE, "%s: sigaction", __func__); +} + +int +main(int argc, char **argv) +{ + hid_t fapl, fcpl; + struct sigaction osa; + state_t storage; + state_t *s = &storage; + int64_t i; + H5F_vfd_swmr_config_t config; + + memset(&config, '\0', sizeof(config)); + + state_init(s, argc, argv); + + fapl = H5Pcreate(H5P_FILE_ACCESS); + if (fapl < 0) { + errx(EXIT_FAILURE, "%s.%d H5Pcreate failed", + __func__, __LINE__); + } + + fcpl = H5Pcreate(H5P_FILE_CREATE); + if (fcpl < 0) { + errx(EXIT_FAILURE, "%s.%d H5Pcreate failed", + __func__, __LINE__); + } + + config.version = H5F__CURR_VFD_SWMR_CONFIG_VERSION; + config.tick_len = 4; + config.max_lag = 5; +#if 0 /* raw-data flushing is not implemented */ + config.flush_raw_data = true; +#endif + config.writer = true; + config.md_pages_reserved = 128; + strlcpy(config.md_file_path, "./my_md_file", + sizeof(config.md_file_path)); + + /* Enable page buffering */ + if (H5Pset_page_buffer_size(fapl, 4096, 100, 0) < 0) + errx(EXIT_FAILURE, "H5Pset_page_buffer_size failed"); + + /* Enable VFD SWMR configuration */ + if (s->use_vfd_swmr && H5Pset_vfd_swmr_config(fapl, &config) < 0) + errx(EXIT_FAILURE, "H5Pset_vfd_swmr_config failed"); + + /* Set file space strategy to paged aggregation in fcpl. + * Page buffering *requires* this strategy. + * + * I set the free-space threshold to 1GB so that deleted + * datasets are not recycled. + */ + if (H5Pset_file_space_strategy(fcpl, + H5F_FSPACE_STRATEGY_PAGE, false, 1024 * 1024 * 1024) < 0) + errx(EXIT_FAILURE, "H5Pset_file_space_strategy failed"); + + s->file = H5Fcreate(s->output_file, H5F_ACC_TRUNC, fcpl, fapl); + + H5Pclose(fapl); + + if (s->file < 0) + errx(EXIT_FAILURE, "H5Fcreate failed"); + + if ((s->dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) { + errx(EXIT_FAILURE, "%s.%d: H5Pcreate failed", + __func__, __LINE__); + } + if ((s->dapl = H5Pcreate(H5P_DATASET_ACCESS)) < 0) { + errx(EXIT_FAILURE, "%s.%d: H5Pcreate failed", + __func__, __LINE__); + } + if (H5Pset_chunk(s->dcpl, 2, chunk_dims) < 0) + errx(EXIT_FAILURE, "H5Pset_chunk failed"); + if (H5Pset_chunk_cache(s->dapl, H5D_CHUNK_CACHE_NSLOTS_DEFAULT, 0, + H5D_CHUNK_CACHE_W0_DEFAULT) < 0) + errx(EXIT_FAILURE, "H5Pset_chunk_cache failed"); + + establish_handler(&osa); + + for (i = 0; i < 4; i++) { + s->stats.iterations++; + if (i % 2 == 0) + create_group(s, i / 2); + create_and_write_dataset(s, i); + } + + for (i = 5; unbroken; i += 2) { + delete_dataset(s, i - 5); + delete_dataset(s, i - 4); + delete_group(s, (i - 4) / 2); + create_group(s, i / 2); + create_and_write_dataset(s, i - 1); + create_and_write_dataset(s, i); + if (s->oneshot || ++s->stats.iterations >= s->iterations_limit) + break; + nanosleep(&s->update_interval, NULL); + } + + if (s->oneshot) { + sigset_t mask; + sigemptyset(&mask); + H5Fvfd_swmr_end_tick(s->file); + (void)sigsuspend(&mask); + } +#if 0 + fprintf(stderr, "Interrupted. Cleaning up.\n"); + + int j; + for (--i, j = 0; j < 4; j++, --i) { + if (i % 2 == 1) { + delete_dataset(s, i - 1); + delete_dataset(s, i); + delete_group(s, i / 2); + } + } + + for (j = 0; j < 4; j++) { + assert(s->dataset[j] == H5I_INVALID_HID); + assert(s->group[j / 2] == H5I_INVALID_HID); + } +#endif + + if (s->print_stats) + print_stats(&s->stats); + + if (H5Fclose(s->file) < 0) + errx(EXIT_FAILURE, "H5Fclose failed"); + + if (H5Pclose(s->dapl) < 0) + errx(EXIT_FAILURE, "H5Pclose failed"); + + if (H5Pclose(s->dcpl) < 0) + errx(EXIT_FAILURE, "H5Pclose failed"); + + disestablish_handler(&osa); + + return EXIT_SUCCESS; +} diff --git a/examples/gaussians.c b/examples/gaussians.c new file mode 100644 index 0000000..0ceeb8b --- /dev/null +++ b/examples/gaussians.c @@ -0,0 +1,759 @@ +#include <assert.h> +#include <curses.h> +#include <err.h> +#include <errno.h> +#include <libgen.h> /* basename(3) */ +#include <math.h> /* expf(3) */ +#include <locale.h> /* setlocale(3) */ +#include <signal.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> /* struct timespec, nanosleep(2), time(3), + * clock_gettime(2) + */ +#include <unistd.h> /* getopt, PATH_MAX, ... */ + +#include <sys/param.h> /* for MIN(a, b) */ + +#include "hdf5.h" +#include "nbcompat.h" + +#define SWMR_TICK_LEN 4 /* in 100 ms */ + +typedef enum { + STANDALONE = 0 + , READ = 1 + , WRITE = 2 +} personality_t; + +typedef enum { + TOP = 0 + , BOTTOM + , LEFT + , RIGHT + , NSIDES +} side_t; + +typedef struct { + bool side[NSIDES]; +} inside_t; + +typedef struct { + float x, y; +} vec_t; + +#define RANK 3 +#define ROWS 20 +#define COLS 40 +static const hsize_t original_dims[RANK] = {0, ROWS, COLS}; +static const hsize_t max_dims[RANK] = {H5S_UNLIMITED, ROWS, COLS}; +static const hsize_t frame_dims[RANK] = {1, ROWS, COLS}; +static const hsize_t *chunk_dims = frame_dims; +static volatile sig_atomic_t unbroken = 1; + +typedef struct { + /* main-loop statistics */ + uint64_t max_elapsed_ns, min_elapsed_ns, total_elapsed_ns; + uint64_t total_loops; + hid_t dataset, memspace, dcpl, file, group; + char output_file[PATH_MAX]; + char progname[PATH_MAX]; + struct timespec update_interval; + bool fuzz; + bool constantrate; + unsigned int partstep; +} state_t; + +#define ALL_HID_INITIALIZER (state_t){ \ + .total_elapsed_ns = 0 \ + , .total_loops = 0 \ + , .min_elapsed_ns = UINT64_MAX \ + , .max_elapsed_ns = 0 \ + , .memspace = H5I_INVALID_HID \ + , .file = H5I_INVALID_HID \ + , .constantrate = false \ + , .partstep = 0 \ + , .output_file = "" \ + , .update_interval = (struct timespec){ \ + .tv_sec = 0 \ + , .tv_nsec = 1000000000UL / 30 /* 1/30 second */}} + +static void state_init(state_t *, int, char **); + +static void +usage(const char *progname) +{ + fprintf(stderr, "usage: %s [-u milliseconds]\n" + "\n" + "-c: increase the frame number continously (reader mode)\n" + "-f: add \"fuzz\" (linear noise) to the data (writer mode)\n" + "-u ms: milliseconds interval between updates to %s.h5\n" + "\n", + progname, progname); + exit(EXIT_FAILURE); +} + +static void +state_init(state_t *s, int argc, char **argv) +{ + int ch; + char tfile[PATH_MAX]; + char *end; + unsigned long millis; + + *s = ALL_HID_INITIALIZER; + strlcpy(tfile, argv[0], sizeof(tfile)); + strlcpy(s->progname, basename(tfile), sizeof(s->progname)); + + while ((ch = getopt(argc, argv, "cfu:")) != -1) { + switch (ch) { + case 'c': + s->constantrate = true; + break; + case 'f': + s->fuzz = true; + break; + case 'u': + errno = 0; + millis = strtoul(optarg, &end, 0); + if (millis == ULONG_MAX && errno == ERANGE) { + err(EXIT_FAILURE, + "option -p argument \"%s\"", optarg); + } else if (*end != '\0') { + errx(EXIT_FAILURE, + "garbage after -p argument \"%s\"", optarg); + } + s->update_interval.tv_sec = millis / 1000UL; + s->update_interval.tv_nsec = + (millis * 1000000UL) % 1000000000UL; + warnx("%lu milliseconds between updates", millis); + break; + case '?': + default: + usage(s->progname); + } + } + argc -= optind; + argv += optind; + + s->dataset = H5I_INVALID_HID; + s->group = H5I_INVALID_HID; + + s->memspace = H5Screate_simple(RANK, frame_dims, NULL); + + if (s->memspace < 0) { + errx(EXIT_FAILURE, "%s.%d: H5Screate_simple failed", + __func__, __LINE__); + } + + const int nprinted = snprintf(s->output_file, sizeof(s->output_file), + "%s.h5", s->progname); + if (nprinted < 0 || nprinted >= sizeof(s->output_file)) { + errx(EXIT_FAILURE, "%s.%d: output filename truncated.", + __func__, __LINE__); + } +} + +static void +matrix_read(state_t *s, int *framenop, float mat[ROWS][COLS]) +{ + hid_t ds, filespace; + herr_t status; + hsize_t dims[RANK]; + + ds = s->dataset; + + if (H5Drefresh(ds) < 0) + errx(EXIT_FAILURE, "H5Drefresh failed"); + + filespace = H5Dget_space(ds); + + if (H5Sget_simple_extent_dims(filespace, dims, NULL) < 0) + errx(EXIT_FAILURE, "H5Sget_simple_extent_dims failed"); + + if (dims[1] != original_dims[1] || dims[2] != original_dims[2]) { + errx(EXIT_FAILURE, "Unexpected dimensions N x %ju x %ju", + (uintmax_t)dims[1], (uintmax_t)dims[2]); + } + + const uint64_t update_ms = timespec2ns(&s->update_interval) / 1000000; + const uint32_t tick_ms = SWMR_TICK_LEN * 100; + const uint64_t updates_per_tick = (tick_ms + update_ms - 1) / update_ms; + const hssize_t hang_back = 2 * updates_per_tick; + const int frameno = *framenop; + + const int lead = frameno + hang_back - dims[0]; + if (s->constantrate) { + *framenop = frameno + 1; + } else if (lead > hang_back * 2) { + if (++s->partstep % 3 == 0) + *framenop = frameno + 1; + } else if (lead > 0) { + if (++s->partstep % 2 == 0) + *framenop = frameno + 1; + } else if (lead == 0) { + *framenop = frameno + 1; + } else if (lead < -hang_back * 2) { + /* We're way behind, so jump close to the front. */ + *framenop = dims[0] - hang_back; + } else /* lead < 0 */ { + *framenop = frameno + 1; + if (++s->partstep % 2 == 0) + *framenop = frameno + 2; + } + +#if 0 + if (!s->constantrate && (lead < -2 || 2 < lead)) { + int gain = 31250 / 4; + const struct timespec prior_integral = s->update_integral; + struct timespec current_interval; + if (lead > 0) + gain *= 2; + struct timespec adjustment = (struct timespec){.tv_sec = 0, + .tv_nsec = gain * MAX(MIN(4, lead), -4)}; + /* XXX clamp it XXX */ + timespecadd(&s->update_integral, + &adjustment, &s->update_integral); + timespecadd(&s->update_integral, + &s->update_interval, ¤t_interval); + if (timespeccmp(¤t_interval, &s->min_interval, <=)) + s->update_integral = prior_integral; + } +#endif + + if (frameno >= dims[0]) { + int i, j; + for (i = 0; i < ROWS; i++) { + for (j = 0; j < COLS; j++) + mat[i][j] = ((i + j) % 2 == 0) ? 0. : 1.; + } + return; + } + + hsize_t offset[RANK] = {frameno, 0, 0}; + + if (H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL, + frame_dims, NULL) < 0) + errx(EXIT_FAILURE, "H5Sselect_hyperslab failed"); + + status = H5Dread(ds, H5T_NATIVE_FLOAT, s->memspace, filespace, + H5P_DEFAULT, mat); + + if (status < 0) + errx(EXIT_FAILURE, "H5Dread failed"); + + if (H5Sclose(filespace) < 0) + errx(EXIT_FAILURE, "H5Sclose failed"); +} + +static void +matrix_write(state_t *s, int frameno, float mat[ROWS][COLS]) +{ + hid_t ds, filespace; + herr_t status; + hsize_t size[RANK] = {frameno + 1, ROWS, COLS}; + hsize_t offset[RANK] = {frameno, 0, 0}; + + ds = s->dataset; + + if (H5Dset_extent(ds, size) < 0) + errx(EXIT_FAILURE, "H5Dset_extent failed"); + + filespace = H5Dget_space(ds); + + if (H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL, + frame_dims, NULL) < 0) + errx(EXIT_FAILURE, "H5Sselect_hyperslab failed"); + + status = H5Dwrite(ds, H5T_NATIVE_FLOAT, s->memspace, filespace, + H5P_DEFAULT, mat); + + if (status < 0) + errx(EXIT_FAILURE, "H5Dwrite failed"); + + if (H5Sclose(filespace) < 0) + errx(EXIT_FAILURE, "H5Sclose failed"); + + if (H5Dflush(ds) < 0) + errx(EXIT_FAILURE, "H5Dflush failed"); +} + +static void +open_group(state_t *s) +{ + hid_t g; + const char *gname = "/group-0"; + + assert(s->group == H5I_INVALID_HID); + + g = H5Gopen(s->file, gname, H5P_DEFAULT); + + if (g < 0) + errx(EXIT_FAILURE, "H5Gcreate failed"); + + fprintf(stderr, "Opened group %s\n", gname); + s->group = g; +} + +static void +create_group(state_t *s) +{ + hid_t g; + const char *gname = "/group-0"; + + assert(s->group == H5I_INVALID_HID); + + g = H5Gcreate(s->file, gname, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + if (g < 0) + errx(EXIT_FAILURE, "H5Gcreate failed"); + + s->group = g; +} + +static void +open_dataset(state_t *s) +{ + const char *dname = "/group-0/dataset-0"; + hid_t ds; + hid_t filespace; + hid_t ty; + hsize_t dims[RANK], maxdims[RANK]; + + assert(s->dataset == H5I_INVALID_HID); + + ds = H5Dopen(s->file, dname, H5P_DEFAULT); + + if (ds < 0) + errx(EXIT_FAILURE, "H5Dopen(, \"%s\", ) failed", dname); + + if ((ty = H5Dget_type(ds)) < 0) + errx(EXIT_FAILURE, "H5Dget_type failed"); + + if (H5Tequal(ty, H5T_IEEE_F32BE) <= 0) + errx(EXIT_FAILURE, "Unexpected data type"); + + if ((filespace = H5Dget_space(ds)) < 0) + errx(EXIT_FAILURE, "H5Dget_space failed"); + + if (H5Sget_simple_extent_ndims(filespace) != RANK) + errx(EXIT_FAILURE, "Unexpected rank"); + + if (H5Sget_simple_extent_dims(filespace, dims, maxdims) < 0) + errx(EXIT_FAILURE, "H5Sget_simple_extent_dims failed"); + + if (dims[1] != original_dims[1] || dims[2] != original_dims[2]) { + errx(EXIT_FAILURE, "Unexpected dimensions ? x %ju x %ju", + (uintmax_t)dims[1], (uintmax_t)dims[2]); + } + + if (maxdims[1] != original_dims[1] || maxdims[2] != original_dims[2]) { + errx(EXIT_FAILURE, + "Unexpected maximum dimensions ? x %ju x %ju", + (uintmax_t)dims[1], (uintmax_t)dims[2]); + } + + fprintf(stderr, "Opened dataset %s\n", dname); + s->dataset = ds; +} + +static void +create_dataset(state_t *s) +{ + const char *dname = "/group-0/dataset-0"; + hid_t ds; + hid_t filespace; + + assert(s->dataset == H5I_INVALID_HID); + + filespace = H5Screate_simple(__arraycount(original_dims), original_dims, + max_dims); + + if (filespace < 0) { + errx(EXIT_FAILURE, "%s.%d: H5Screate_simple failed", + __func__, __LINE__); + } + + if ((s->dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) { + errx(EXIT_FAILURE, "%s.%d: H5Pcreate failed", + __func__, __LINE__); + } + + if (H5Pset_chunk(s->dcpl, RANK, chunk_dims) < 0) + errx(EXIT_FAILURE, "H5Pset_chunk failed"); + + ds = H5Dcreate2(s->file, dname, H5T_IEEE_F32BE, filespace, + H5P_DEFAULT, s->dcpl, H5P_DEFAULT); + + if (H5Sclose(filespace) < 0) + errx(EXIT_FAILURE, "H5Sclose failed"); + + filespace = H5I_INVALID_HID; + + if (ds < 0) + errx(EXIT_FAILURE, "H5Dcreate(, \"%s\", ) failed", dname); + + s->dataset = ds; +} + +static void +handle_signal(int signo) +{ + unbroken = 0; +} + +static void +disestablish_handler(const struct sigaction *osa) +{ + if (sigaction(SIGINT, osa, NULL) == -1) + err(EXIT_FAILURE, "%s: sigaction", __func__); +} + +static void +establish_handler(struct sigaction *osa) +{ + struct sigaction sa; + + memset(&sa, '\0', sizeof(sa)); + sa.sa_handler = handle_signal; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGINT, &sa, osa) == -1) + err(EXIT_FAILURE, "%s: sigaction", __func__); +} + +static void +step(vec_t *center, vec_t *direction, float steplen, bool *recursed) +{ + static const float top = 0., bottom = (float)COLS, + left = 0., right = (float)ROWS; + struct { + bool top, bottom, left, right; + } bounce = {false, false, false, false}; + float xback, yback; + vec_t before = *center; + vec_t after = (vec_t){.x = before.x + direction->x * steplen, + .y = before.y + direction->y * steplen}; + + if (before.x < right && after.x >= right) { + xback = (right - before.x) / (after.x - before.x); + bounce.right = true; + } else if (before.x > left && after.x <= left) { + xback = (before.x - left) / (before.x - after.x); + bounce.left = true; + } else + xback = 0.; + + if (before.y < bottom && after.y >= bottom) { + yback = (bottom - before.y) / (after.y - before.y); + bounce.bottom = true; + } else if (before.y > top && after.y <= top) { + yback = (before.y - top) / (before.y - after.y); + bounce.top = true; + } else + yback = 0.; + + /* I shorten the step length until a corner crossing becomes + * a side crossing. + */ + if ((bounce.top && bounce.right) || + (bounce.right && bounce.bottom) || + (bounce.bottom && bounce.left) || + (bounce.left && bounce.top)) { + + float newsteplen = steplen * 2 / 3; + if (recursed != NULL) + *recursed = true; + step(center, direction, newsteplen, NULL); + step(center, direction, steplen - newsteplen, NULL); + } + if (bounce.right || bounce.left) { + after.x = before.x + direction->x * (2 * xback - 1) * steplen; + direction->x = -direction->x; + } + if (bounce.top || bounce.bottom) { + after.y = before.y + direction->y * (2 * yback - 1) * steplen; + direction->y = -direction->y; + } + *center = after; +} + +static float +gaussian(float x, float y, float r) +{ + return expf(-(x * x + y * y) / (r * r)); +} + +int +stepno(float v) +{ + if (v < 1. / 8.) + return 0; + if (v < 3. / 8.) + return 1; + if (v < 7 / 8.) + return 2; + + return 3; +} + +static void +draw_border(WINDOW *w) +{ + wborder(w, 0, 0, 0, 0, 0, 0, 0, 0); +} + +static void +matrix_draw(WINDOW *w, float mat[ROWS][COLS]) +{ + int ch, i, j; + static char steps[] = " .oO"; + + wclear(w); + draw_border(w); + for (i = 0; i < ROWS; i++) { + wmove(w, 1 + i, 1); + for (j = 0; j < COLS; j++) { + ch = steps[stepno(mat[i][j])]; + waddch(w, ch); + } + } + + wnoutrefresh(w); +} + +static void +matrix_compute(vec_t *center, size_t ncenters, float mat[ROWS][COLS]) +{ + int i, j, k; + float radius = 4; + + for (i = 0; i < ROWS; i++) { + for (j = 0; j < COLS; j++) { + mat[i][j] = 0.; + for (k = 0; k < ncenters; k++) { + mat[i][j] += gaussian(i - center[k].x, + j - center[k].y, radius); + } + } + } +} + +static void +move_centers(vec_t *center, vec_t *direction, size_t ncenters) +{ + const float steplen = .01; + int k; + bool recursed[2] = {false, false}; + + for (k = 0; k < ncenters; k++) { + recursed[k] = false; + step(¢er[k], &direction[k], steplen, &recursed[k]); + } +} + +static void +matrix_open(state_t *s, bool rw) +{ + const char *func; + hid_t fapl, fcpl; + H5F_vfd_swmr_config_t config; + + fapl = H5Pcreate(H5P_FILE_ACCESS); + if (fapl < 0) { + errx(EXIT_FAILURE, "%s.%d H5Pcreate failed", + __func__, __LINE__); + } + + fcpl = H5Pcreate(H5P_FILE_CREATE); + if (fcpl < 0) { + errx(EXIT_FAILURE, "%s.%d H5Pcreate failed", + __func__, __LINE__); + } + + memset(&config, '\0', sizeof(config)); + + config.version = H5F__CURR_VFD_SWMR_CONFIG_VERSION; + config.tick_len = SWMR_TICK_LEN; + config.max_lag = 5; + config.writer = rw; + config.md_pages_reserved = 128; + +#if 0 /* raw-data flushing is not implemented; default open-tries is ok */ + config.flush_raw_data = true; + config.md_open_tries = 1; +#endif + + strlcpy(config.md_file_path, "./my_md_file", sizeof(config.md_file_path)); + + /* Enable page buffering */ + if (H5Pset_page_buffer_size(fapl, 4096, 100, 0) < 0) + errx(EXIT_FAILURE, "H5Pset_page_buffer_size failed"); + + /* Enable VFD SWMR configuration */ + if (H5Pset_vfd_swmr_config(fapl, &config) < 0) + errx(EXIT_FAILURE, "H5Pset_vfd_swmr_config failed"); + + /* Set file space strategy to paged aggregation in fcpl. + * Page buffering *requires* this strategy. + * + * I set the free-space threshold to 1GB so that deleted + * datasets are not recycled. + */ + if (H5Pset_file_space_strategy(fcpl, + H5F_FSPACE_STRATEGY_PAGE, false, 1024 * 1024 * 1024) < 0) + errx(EXIT_FAILURE, "H5Pset_file_space_strategy failed"); + + if (rw) { + s->file = H5Fcreate("gaussians.h5", H5F_ACC_TRUNC, fcpl, fapl); + func = "H5Fcreate"; + } else { + s->file = H5Fopen("gaussians.h5", H5F_ACC_RDONLY, fapl); + func = "H5Fopen"; + } + + H5Pclose(fapl); + + if (s->file < 0) + errx(EXIT_FAILURE, "%s failed", func); +} + +static void +fuzz(float mat[ROWS][COLS]) +{ + int i, j; + + for (i = 0; i < ROWS; i++) { + for (j = 0; j < COLS; j++) { + mat[i][j] += (float)random() / RAND_MAX * (9. / 64.); + } + } +} + +int +main(int argc, char **argv) +{ + char buf[32]; + float mat[ROWS][COLS]; + int frameno; + vec_t center[2] = {{.x = .5, .y = .5}, + {.x = ROWS - .5, .y = COLS - .5}}; + vec_t direction[2] = {{.x = 3, .y = 7}, {.x = 43, .y = 41}}; + struct sigaction osa; + WINDOW *topw = NULL, *w = NULL; + personality_t personality; + state_t s; + + srandom((unsigned int)time(NULL)); + + setlocale(LC_ALL, ""); + + state_init(&s, argc, argv); + + switch (s.progname[0]) { + case 'r': + personality = READ; + break; + case 'w': + personality = WRITE; + break; + default: + personality = STANDALONE; + break; + } + establish_handler(&osa); + + switch (personality) { + case WRITE: + matrix_open(&s, true); + create_group(&s); + create_dataset(&s); + break; + case READ: + matrix_open(&s, false); + open_group(&s); + open_dataset(&s); + break; + default: + break; + } + + if ((topw = initscr()) == NULL) + errx(EXIT_FAILURE, "initscr failed"); + else if ((w = subwin(topw, ROWS + 2, COLS + 2, 0, 0)) == NULL) + errx(EXIT_FAILURE, "subwin failed"); + + for (frameno = 0; unbroken; ) { + struct timespec elapsed, start, stop; + uint64_t elapsed_ns; + clock_gettime(CLOCK_MONOTONIC, &start); + + switch (personality) { + case READ: + matrix_read(&s, &frameno, mat); + break; + case WRITE: + case STANDALONE: + matrix_compute(center, __arraycount(center), mat); + if (s.fuzz) + fuzz(mat); + break; + } + switch (personality) { + case READ: + case STANDALONE: + matrix_draw(w, mat); +#if 0 + wmove(topw, ROWS + 3, 0); + waddstr(topw, "\"Don't cross the streams.\""); +#endif + break; + case WRITE: + matrix_write(&s, frameno, mat); + break; + } + + snprintf(buf, sizeof(buf), "Frame %d.", frameno); + wmove(topw, ROWS + 2, 0); + waddstr(topw, buf); + snprintf(buf, sizeof(buf), "Rate %lld/s.", + 1000000000ULL / timespec2ns(&s.update_interval)); + wmove(topw, ROWS + 2, COLS + 2 - strlen(buf)); + waddstr(topw, buf); + wnoutrefresh(topw); + doupdate(); + + nanosleep(&s.update_interval, NULL); + + switch (personality) { + case STANDALONE: + case WRITE: + move_centers(center, direction, __arraycount(center)); + frameno++; + break; + case READ: + break; + } + clock_gettime(CLOCK_MONOTONIC, &stop); + + timespecsub(&stop, &start, &elapsed); + elapsed_ns = timespec2ns(&elapsed); + + if (elapsed_ns < s.min_elapsed_ns) + s.min_elapsed_ns = elapsed_ns; + if (elapsed_ns > s.max_elapsed_ns) + s.max_elapsed_ns = elapsed_ns; + s.total_elapsed_ns += elapsed_ns; + s.total_loops++; + } + endwin(); + fprintf(stderr, "Iteration stats:\n"); + fprintf(stderr, "min. elapsed %" PRIu64 " ms\n", + s.min_elapsed_ns / 1000000); + fprintf(stderr, "max. elapsed %" PRIu64 " ms\n", + s.max_elapsed_ns / 1000000); + fprintf(stderr, "avg. elapsed %.3f ms\n", + (double)s.total_elapsed_ns / s.total_loops / 1000000); + disestablish_handler(&osa); + return EXIT_SUCCESS; +} diff --git a/examples/nbcompat.c b/examples/nbcompat.c new file mode 100644 index 0000000..03e477f --- /dev/null +++ b/examples/nbcompat.c @@ -0,0 +1,17 @@ +#include "nbcompat.h" + +size_t +strlcpy(char *dst, const char *src, size_t size) +{ + char *d; + const char *s; + + for (d = dst, s = src; (s - src) < size; d++, s++) { + *d = *s; + if (*s == '\0') + return s - src; + } + + dst[size - 1] = '\0'; + return size; +} diff --git a/examples/nbcompat.h b/examples/nbcompat.h new file mode 100644 index 0000000..98f8c7d --- /dev/null +++ b/examples/nbcompat.h @@ -0,0 +1,36 @@ +#ifndef NB_COMPAT_H +#define NB_COMPAT_H + +#include <unistd.h> /* for size_t */ + +#ifndef __arraycount +#define __arraycount(__a) (sizeof(__a) / sizeof((__a)[0])) +#endif + +size_t strlcpy(char *, const char *, size_t); + +#define timespeccmp(tsp, usp, cmp) \ + (((tsp)->tv_sec == (usp)->tv_sec) ? \ + ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ + ((tsp)->tv_sec cmp (usp)->tv_sec)) +#define timespecadd(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ + if ((vsp)->tv_nsec >= 1000000000L) { \ + (vsp)->tv_sec++; \ + (vsp)->tv_nsec -= 1000000000L; \ + } \ + } while (/* CONSTCOND */ 0) +#define timespecsub(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ + if ((vsp)->tv_nsec < 0) { \ + (vsp)->tv_sec--; \ + (vsp)->tv_nsec += 1000000000L; \ + } \ + } while (/* CONSTCOND */ 0) +#define timespec2ns(x) (((uint64_t)(x)->tv_sec) * 1000000000L + (x)->tv_nsec) + +#endif /* NB_COMPAT_H */ |