commit 95a8cd9eaa5e383b3f788fe1e1153d0e0d071997
parent a3496b80792ed74cd68c21576ffd6642b60652e0
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Wed, 20 Jan 2021 11:45:44 +0100
Finalise a first implementation of the cache
Diffstat:
8 files changed, 446 insertions(+), 1 deletion(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -55,6 +55,7 @@ set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
set(ATRSTM_FILES_SRC
atrstm.c
atrstm_build_octrees.c
+ atrstm_cache.c
atrstm_log.c
atrstm_optprops.c
atrstm_partition.c
@@ -62,6 +63,7 @@ set(ATRSTM_FILES_SRC
set(ATRSTM_FILES_INC
atrstm_build_octrees.h
atrstm_c.h
+ atrstm_cache.h
atrstm_log.h
atrstm_optprops.h
atrstm_partition.h)
diff --git a/src/atrstm.c b/src/atrstm.c
@@ -18,6 +18,7 @@
#include "atrstm.h"
#include "atrstm_build_octrees.h"
#include "atrstm_c.h"
+#include "atrstm_cache.h"
#include "atrstm_log.h"
#include <astoria/atrri.h>
@@ -65,6 +66,7 @@ release_atrstm(ref_T* ref)
if(atrstm->suvm) SUVM(device_ref_put(atrstm->suvm));
if(atrstm->volume) SUVM(volume_ref_put(atrstm->volume));
if(atrstm->svx) SVX(device_ref_put(atrstm->svx));
+ if(atrstm->cache) cache_ref_put(atrstm->cache);
if(atrstm->logger == &atrstm->logger__) logger_release(&atrstm->logger__);
str_release(&atrstm->name);
ASSERT(MEM_ALLOCATED_SIZE(&atrstm->svx_allocator) == 0);
@@ -182,6 +184,12 @@ atrstm_create
(atrstm->logger, &atrstm->svx_allocator, atrstm->verbose, &atrstm->svx);
if(res != RES_OK) goto error;
+ /* Create the cache if necessary */
+ if(args->cache_filename) {
+ res = cache_create(atrstm, args->cache_filename, &atrstm->cache);
+ if(res != RES_OK) goto error;
+ }
+
/* Build the octree */
build_octree_args.grid_max_definition[0] = args->grid_max_definition[0];
build_octree_args.grid_max_definition[1] = args->grid_max_definition[1];
diff --git a/src/atrstm.h b/src/atrstm.h
@@ -69,6 +69,7 @@ struct atrstm_args {
const char* atrck_filename; /* Filename of the Correlated K */
const char* atrtp_filename; /* Filename of the Thermodynamic properties */
const char* atrri_filename; /* Filename of the refraction index LUT */
+ const char* cache_filename; /* Cache to use. NULL <=> no cache */
const char* name; /* Name of the medium */
enum atrstm_spectral_type spectral_type; /* Longwave/shortwave */
@@ -91,6 +92,7 @@ struct atrstm_args {
NULL, /* AtrCK filename */ \
NULL, /* AtrTP filename */ \
NULL, /* AtrRI filename */ \
+ NULL, /* Cache filename */ \
"semi transparent medium", /* Name */ \
ATRSTM_SPECTRAL_SW, /* Spectral type */ \
{500,500}, /* Spectral integration range */ \
diff --git a/src/atrstm_build_octrees.c b/src/atrstm_build_octrees.c
@@ -600,6 +600,15 @@ build_octrees(struct atrstm* atrstm, const struct build_octrees_args* args)
"Evaluating and partitionning the field of optical properties.\n");
time_current(&t0);
+#if 0
+ {
+ FILE* fp = fopen("optprops.txt", "w");
+ CHK(fp);
+ CHK(dump_optprops(atrstm, &refract_id, fp) == RES_OK);
+ fclose(fp);
+ }
+#endif
+
omp_set_nested(1); /* Enable nested threads for voxelize_volumetric_mesh */
#pragma omp parallel sections num_threads(2)
{
diff --git a/src/atrstm_c.h b/src/atrstm_c.h
@@ -26,6 +26,7 @@
/* Forward declaration of external data types */
struct atrri;
struct atrtp;
+struct cache;
struct suvm;
struct atrstm {
@@ -49,6 +50,8 @@ struct atrstm {
enum atrstm_spectral_type spectral_type;
double wlen_range[2]; /* Spectral range in nm */
+ struct cache* cache; /* Cache of the SVX data structures */
+
struct mem_allocator* allocator;
struct mem_allocator svx_allocator;
struct logger* logger;
diff --git a/src/atrstm_cache.c b/src/atrstm_cache.c
@@ -0,0 +1,373 @@
+/* Copyright (C) 2020 CNRS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "atrstm_c.h"
+#include "atrstm_cache.h"
+#include "atrstm_log.h"
+
+#include <astoria/atrri.h>
+#include <astoria/atrtp.h>
+
+#include <rsys/cstr.h>
+#include <rsys/hash.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/ref_count.h>
+
+#include <star/suvm.h>
+
+#include <errno.h>
+#include <fcntl.h> /* open */
+#include <sys/stat.h> /* S_IRUSR & S_IWUSR */
+#include <unistd.h> /* close */
+#include <string.h>
+
+struct cache {
+ FILE* stream;
+ struct atrstm* atrstm;
+ ref_T ref;
+};
+
+struct hash {
+ hash256_T therm_props;
+ hash256_T refract_ids;
+ hash256_T volume;
+};
+
+/* Current version the cache memory layout. One should increment it and perform
+ * a version management onto serialized data when the cache data structure is
+ * updated. */
+static const int CACHE_VERSION = 0;
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static res_T
+hash_compute(struct atrstm* atrstm, struct hash* hash)
+{
+ struct atrtp_desc atrtp_desc = ATRTP_DESC_NULL;
+ struct atrri_desc atrri_desc = ATRRI_DESC_NULL;
+ res_T res = RES_OK;
+ ASSERT(atrstm && hash);
+
+ res = atrtp_get_desc(atrstm->atrtp, &atrtp_desc);
+ if(res != RES_OK) goto error;
+ res = atrri_get_desc(atrstm->atrri, &atrri_desc);
+ if(res != RES_OK) goto error;
+
+ hash_sha256
+ (atrtp_desc.properties,
+ atrtp_desc.nnodes*sizeof(double[ATRTP_COUNT__]),
+ hash->therm_props);
+
+ hash_sha256
+ (atrri_desc.indices,
+ atrri_desc.nindices*sizeof(struct atrri_refractive_index),
+ hash->refract_ids);
+
+ res = suvm_volume_compute_hash
+ (atrstm->volume, SUVM_POSITIONS|SUVM_INDICES, hash->volume);
+ if(res != RES_OK) goto error;
+
+exit:
+ return res;
+error:
+ log_err(atrstm, "Error computing the AtrSTM hash -- %s.\n",
+ res_to_cstr(res));
+ goto exit;
+}
+
+static res_T
+hash_write(const struct hash* hash, FILE* fp)
+{
+ res_T res = RES_OK;
+ ASSERT(hash && fp);
+
+ #define WRITE(Var, N) { \
+ if(fwrite((Var), sizeof(*(Var)), (N), fp) != (N)) { \
+ res = RES_IO_ERR; \
+ goto error; \
+ } \
+ } (void)0
+ WRITE(hash->therm_props, 64);
+ WRITE(hash->refract_ids, 64);
+ WRITE(hash->volume, 64);
+ #undef WRITE
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+hash_read(struct hash* hash, FILE* fp)
+{
+ res_T res = RES_OK;
+ ASSERT(hash && fp);
+
+ #define READ(Var, N) { \
+ if(fread((Var), sizeof(*(Var)), (N), fp) != (N)) { \
+ if(feof(fp)) { \
+ res = RES_BAD_ARG; \
+ } else if(ferror(fp)) { \
+ res = RES_IO_ERR; \
+ } else { \
+ res = RES_UNKNOWN_ERR; \
+ } \
+ goto error; \
+ } \
+ } (void)0
+ READ(hash->therm_props, 64);
+ READ(hash->refract_ids, 64);
+ READ(hash->volume, 64);
+ #undef READ
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/* Setup the cache header, i.e. data that uniquely identify the cache regarding
+ * the input data */
+static res_T
+write_cache_header(struct cache* cache, const char* filename)
+{
+ struct hash hash;
+ res_T res = RES_OK;
+ ASSERT(cache);
+
+ #define WRITE(Var, N) { \
+ if(fwrite((Var), sizeof(*(Var)), (N), cache->stream) != (N)) { \
+ log_err(cache->atrstm, \
+ "%s: could not write the cache header.\n", filename); \
+ res = RES_IO_ERR; \
+ goto error; \
+ } \
+ } (void)0
+ WRITE(&CACHE_VERSION, 1);
+ WRITE(&cache->atrstm->gyration_radius_prefactor, 1);
+ WRITE(&cache->atrstm->fractal_dimension, 1);
+ WRITE(&cache->atrstm->spectral_type, 1);
+ WRITE(cache->atrstm->wlen_range, 2);
+ #undef WRITE
+
+ res = hash_compute(cache->atrstm, &hash);
+ if(res != RES_OK) goto error;
+ res = hash_write(&hash, cache->stream);
+ if(res != RES_OK) goto error;
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+read_cache_header(struct cache* cache, const char* filename)
+{
+ struct hash cached_hash;
+ struct hash current_hash;
+ double gyration_radius_prefactor = 0;
+ double fractal_dimension = 0;
+ enum atrstm_spectral_type spectral_type = ATRSTM_SPECTRAL_TYPES_COUNT__;
+ double wlen_range[2] = {0, 0};
+ int cache_version = 0;
+ res_T res = RES_OK;
+ ASSERT(cache);
+
+ /* Read the cache header */
+ #define READ(Var, N) { \
+ if(fread((Var), sizeof(*(Var)), (N), cache->stream) != (N)) { \
+ if(feof(cache->stream)) { \
+ res = RES_BAD_ARG; \
+ } else if(ferror(cache->stream)) { \
+ res = RES_IO_ERR; \
+ } else { \
+ res = RES_UNKNOWN_ERR; \
+ } \
+ log_err(cache->atrstm, "%s: could not read the cache header -- %s.\n", \
+ filename, res_to_cstr(res)); \
+ goto error; \
+ } \
+ } (void)0
+
+ READ(&cache_version, 1);
+ if(cache_version != CACHE_VERSION) {
+ log_err(cache->atrstm,
+ "%s: invalid cache in version %d. Expecting a cache in version %d.\n",
+ filename, cache_version, CACHE_VERSION);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ #define CHK_VAR(CachedVal, CurrentVal, Name, Fmt) { \
+ if((CachedVal) != (CurrentVal)) { \
+ log_err(cache->atrstm, \
+ "%s: invalid cache regarding the "Name". " \
+ "Cached value: "Fmt", Current Value: "Fmt".\n", \
+ filename, (CachedVal), (CurrentVal)); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ } (void)0
+
+ READ(&gyration_radius_prefactor, 1);
+ CHK_VAR(gyration_radius_prefactor, cache->atrstm->gyration_radius_prefactor,
+ "gyration radius prefactor", "%g");
+
+ READ(&fractal_dimension, 1);
+ CHK_VAR(fractal_dimension, cache->atrstm->fractal_dimension,
+ "fractal dimension", "%g");
+
+ READ(&spectral_type, 1);
+ CHK_VAR(spectral_type, cache->atrstm->spectral_type,
+ "spectral type", "%i");
+
+ READ(wlen_range, 2);
+ CHK_VAR(wlen_range[0], cache->atrstm->wlen_range[0],
+ "spectral lower bound", "%g");
+ CHK_VAR(wlen_range[1], cache->atrstm->wlen_range[1],
+ "spectral upper bound", "%g");
+
+ #undef CHK_VAR
+
+ res = hash_read(&cached_hash, cache->stream);
+ if(res != RES_OK) goto error;
+ res = hash_compute(cache->atrstm, ¤t_hash);
+ if(res != RES_OK) goto error;
+
+ #define CHK_HASH(CachedHash, CurrentHash, Name) { \
+ if(!hash256_eq((CachedHash), (CurrentHash))) { \
+ log_err(cache->atrstm, \
+ "%s: invalid cache regarding the submitted "Name".\n", \
+ filename); \
+ res = RES_BAD_ARG; \
+ goto error; \
+ } \
+ } (void)0
+ CHK_HASH(cached_hash.therm_props, current_hash.therm_props,
+ "thermodynamic properties");
+ CHK_HASH(cached_hash.refract_ids, current_hash.refract_ids,
+ "refractive indices");
+ CHK_HASH(cached_hash.volume, current_hash.volume,
+ "volumetric mesh");
+ #undef CHK_HASH
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static void
+release_cache(ref_T* ref)
+{
+ struct cache* cache = NULL;
+ struct atrstm* atrstm = NULL;
+ ASSERT(ref);
+ cache = CONTAINER_OF(ref, struct cache, ref);
+ atrstm = cache->atrstm;
+ if(cache->stream) CHK(fclose(cache->stream) == 0);
+ MEM_RM(atrstm->allocator, cache);
+ ATRSTM(ref_put(atrstm));
+}
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+res_T
+cache_create
+ (struct atrstm* atrstm,
+ const char* filename,
+ struct cache** out_cache)
+{
+ struct cache* cache = NULL;
+ struct stat cache_stat;
+ int fd = -1;
+ int err = 0;
+ res_T res = RES_OK;
+ ASSERT(atrstm && filename && out_cache);
+
+ cache = MEM_CALLOC(atrstm->allocator, 1, sizeof(*cache));
+ if(!cache) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ ref_init(&cache->ref);
+ ATRSTM(ref_get(atrstm));
+ cache->atrstm = atrstm;
+
+ fd = open(filename, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
+ if(fd < 0) {
+ log_err(atrstm, "Could not open the cache file `%s' -- %s.\n",
+ filename, strerror(errno));
+ res = RES_IO_ERR;
+ goto error;
+ }
+
+ cache->stream = fdopen(fd, "w+");
+ if(!cache->stream) {
+ log_err(atrstm, "Could not open the cache file `%s' -- %s.\n",
+ filename, strerror(errno));
+ res = RES_IO_ERR;
+ goto error;
+ }
+ fd = -1;
+
+ err = stat(filename, &cache_stat);
+ if(err < 0) {
+ log_err(atrstm, "Could not stat the cache file `%s' -- %s.\n",
+ filename, strerror(errno));
+ res = RES_IO_ERR;
+ goto error;
+ }
+
+ if(cache_stat.st_size == 0) { /* Empty cache */
+ res = write_cache_header(cache, filename);
+ if(res != RES_OK) goto error;
+ } else { /* Cache already exists */
+ res = read_cache_header(cache, filename);
+ if(res != RES_OK) goto error;
+ }
+
+exit:
+ *out_cache = cache;
+ return res;
+error:
+ if(cache) { cache_ref_put(cache); cache = NULL; }
+ if(fd >= 0) CHK(close(fd) == 0);
+ goto exit;
+}
+
+void
+cache_ref_get(struct cache* cache)
+{
+ ASSERT(cache);
+ ref_get(&cache->ref);
+}
+
+void
+cache_ref_put(struct cache* cache)
+{
+ ASSERT(cache);
+ ref_put(&cache->ref, release_cache);
+}
+
+FILE*
+cache_get_stream(struct cache* cache)
+{
+ ASSERT(cache);
+ return cache->stream;
+}
diff --git a/src/atrstm_cache.h b/src/atrstm_cache.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2020 CNRS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef ATRSTM_CACHE_H
+#define ATRSTM_CACHE_H
+
+#include <rsys/rsys.h>
+#include <stdio.h>
+
+/* Cache of the acceleration data structures used to partitionned the optical
+ * properties of the semi transparent medium */
+struct cache;
+
+extern LOCAL_SYM res_T
+cache_create
+ (struct atrstm* atrstm,
+ const char* cache_name,
+ struct cache** cache);
+
+extern LOCAL_SYM void
+cache_ref_get
+ (struct cache* cache);
+
+extern LOCAL_SYM void
+cache_ref_put
+ (struct cache* cache);
+
+extern LOCAL_SYM FILE*
+cache_get_stream
+ (struct cache* cache);
+
+#endif /* ATRSTM_CACHE_H */
diff --git a/src/test_atrstm.c b/src/test_atrstm.c
@@ -50,6 +50,9 @@ print_help(const char* cmd)
" -n NAME name of the medium. Default is \"%s\".\n",
ATRSTM_ARGS_DEFAULT.name);
printf(
+" -O CACHE name of the cache file to use/fill. By default\n"
+" no cache is used\n");
+ printf(
" -p THERMOPROPS path toward the thermodynamic properties.\n");
printf(
" -r REFRACT_ID path toward the per wavelength refractive\n"
@@ -127,7 +130,7 @@ args_init(struct atrstm_args* args, int argc, char** argv)
*args = ATRSTM_ARGS_DEFAULT;
- while((opt = getopt(argc, argv, "f:g:hm:Nn:p:T:t:r:vV:w:")) != -1) {
+ while((opt = getopt(argc, argv, "f:g:hm:Nn:O:p:T:t:r:vV:w:")) != -1) {
switch(opt) {
case 'f':
res = cstr_to_double(optarg, &args->fractal_dimension);
@@ -146,6 +149,7 @@ args_init(struct atrstm_args* args, int argc, char** argv)
case 'm': args->sth_filename = optarg; break;
case 'N': args->precompute_normals = 1; break;
case 'n': args->name = optarg; break;
+ case 'O': args->cache_filename = optarg; break;
case 'p': args->atrtp_filename = optarg; break;
case 'r': args->atrri_filename = optarg; break;
case 'T':