Catalog: parallelize all-sky tile prefetch
This commit is contained in:
1 parent
d9f2c83b9f
commit
457c0728b4
10 files changed
+371
-55
No files matched your search
+173
-25
@@ -6,6 +6,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <omp.h>
|
||||
|
||||
#define PI 3.14159265358979323846
|
||||
|
||||
enum {
|
||||
@@ -108,26 +110,40 @@ static size_t tile_index(int ra_index, int dec_index)
|
||||
return (size_t)dec_index * CATALOG_ALL_SKY_RA_TILES + ra_index;
|
||||
}
|
||||
|
||||
static int load_tile_file(const StarCatalog *catalog, int ra_index,
|
||||
int dec_index, Star **stars, size_t *count)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int written;
|
||||
if (catalog == NULL || stars == NULL || count == NULL)
|
||||
return -1;
|
||||
*stars = NULL;
|
||||
*count = 0;
|
||||
written = snprintf(path, sizeof path, "%s/tile_ra%03d_dec%03d.csv",
|
||||
catalog->all_sky_root, ra_index, dec_index);
|
||||
if (written < 0 || (size_t)written >= sizeof path)
|
||||
return -1;
|
||||
StarCatalog temporary = {0};
|
||||
if (catalog_load_csv(&temporary, path))
|
||||
return -1;
|
||||
*stars = temporary.stars;
|
||||
*count = temporary.count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_tile(StarCatalog *catalog, int ra_index, int dec_index)
|
||||
{
|
||||
CatalogTile *tile = &catalog->tiles[tile_index(ra_index, dec_index)];
|
||||
char path[PATH_MAX];
|
||||
int written;
|
||||
Star *stars;
|
||||
size_t count;
|
||||
if (tile->state != 0)
|
||||
return tile->state == 1 ? 0 : -1;
|
||||
written = snprintf(path, sizeof path, "%s/tile_ra%03d_dec%03d.csv",
|
||||
catalog->all_sky_root, ra_index, dec_index);
|
||||
if (written < 0 || (size_t)written >= sizeof path) {
|
||||
if (load_tile_file(catalog, ra_index, dec_index, &stars, &count)) {
|
||||
tile->state = -1;
|
||||
return -1;
|
||||
}
|
||||
StarCatalog temporary = {0};
|
||||
if (catalog_load_csv(&temporary, path)) {
|
||||
tile->state = -1;
|
||||
return -1;
|
||||
}
|
||||
tile->stars = temporary.stars;
|
||||
tile->count = temporary.count;
|
||||
tile->stars = stars;
|
||||
tile->count = count;
|
||||
tile->state = 1;
|
||||
catalog->count += tile->count;
|
||||
return 0;
|
||||
@@ -255,15 +271,15 @@ static int tile_is_fully_contained(const double direction[3][3], int ra_index,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int catalog_visit_source_triangle(StarCatalog *catalog,
|
||||
const double direction[3][3],
|
||||
int load_missing, CatalogTileVisitor visitor,
|
||||
void *context)
|
||||
typedef int (*CatalogTileIndexVisitor)(int ra_index, int dec_index,
|
||||
void *context);
|
||||
|
||||
static int visit_source_triangle_tile_indices(
|
||||
const double direction[3][3], CatalogTileIndexVisitor visitor,
|
||||
void *context)
|
||||
{
|
||||
if (catalog == NULL || direction == NULL || visitor == NULL)
|
||||
if (direction == NULL || visitor == NULL)
|
||||
return -1;
|
||||
if (catalog->kind == STAR_CATALOG_MEMORY)
|
||||
return visitor(catalog->stars, catalog->count, 0, context);
|
||||
double longitude[3], latitude[3], unwrapped[3];
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
lon_lat_from_direction(direction[i], &longitude[i], &latitude[i]);
|
||||
@@ -311,17 +327,149 @@ int catalog_visit_source_triangle(StarCatalog *catalog,
|
||||
for (int raw_ra = all_ra ? 0 : ra_first;
|
||||
raw_ra <= (all_ra ? 359 : ra_last); ++raw_ra) {
|
||||
const int ra = (raw_ra % 360 + 360) % 360;
|
||||
CatalogTile *tile = &catalog->tiles[tile_index(ra, dec)];
|
||||
if (load_missing && load_tile(catalog, ra, dec))
|
||||
continue; /* Downloader has not finished this tile yet. */
|
||||
if (tile->state != 1 ||
|
||||
visitor(tile->stars, tile->count,
|
||||
tile_is_fully_contained(direction, ra, dec), context))
|
||||
if (visitor(ra, dec, context))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
StarCatalog *catalog;
|
||||
const double (*direction)[3];
|
||||
int load_missing;
|
||||
CatalogTileVisitor visitor;
|
||||
void *context;
|
||||
} CatalogVisitContext;
|
||||
|
||||
static int visit_catalog_tile(int ra_index, int dec_index, void *opaque)
|
||||
{
|
||||
CatalogVisitContext *context = opaque;
|
||||
CatalogTile *tile =
|
||||
&context->catalog->tiles[tile_index(ra_index, dec_index)];
|
||||
if (context->load_missing && load_tile(context->catalog, ra_index, dec_index))
|
||||
return 0; /* Downloader has not finished this tile yet. */
|
||||
if (tile->state != 1)
|
||||
return 0;
|
||||
return context->visitor(
|
||||
tile->stars, tile->count,
|
||||
tile_is_fully_contained(context->direction, ra_index, dec_index),
|
||||
context->context);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
unsigned char *requested;
|
||||
} CatalogMarkContext;
|
||||
|
||||
static int mark_catalog_tile(int ra_index, int dec_index, void *opaque)
|
||||
{
|
||||
CatalogMarkContext *context = opaque;
|
||||
context->requested[tile_index(ra_index, dec_index)] = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int catalog_mark_source_triangle_tiles(
|
||||
const double direction[3][3],
|
||||
unsigned char requested[CATALOG_ALL_SKY_TILE_COUNT])
|
||||
{
|
||||
CatalogMarkContext context = {.requested = requested};
|
||||
if (requested == NULL)
|
||||
return -1;
|
||||
return visit_source_triangle_tile_indices(direction, mark_catalog_tile,
|
||||
&context);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
size_t tile_id;
|
||||
Star *stars;
|
||||
size_t count;
|
||||
int loaded;
|
||||
} CatalogPendingTile;
|
||||
|
||||
int catalog_prefetch_marked_tiles(
|
||||
StarCatalog *catalog,
|
||||
const unsigned char requested[CATALOG_ALL_SKY_TILE_COUNT],
|
||||
int worker_count, CatalogPrefetchStats *stats)
|
||||
{
|
||||
CatalogPendingTile *pending = NULL;
|
||||
size_t pending_count = 0;
|
||||
double load_start;
|
||||
int result = -1;
|
||||
if (stats != NULL)
|
||||
*stats = (CatalogPrefetchStats){0};
|
||||
if (catalog == NULL || requested == NULL || worker_count <= 0)
|
||||
return -1;
|
||||
if (catalog->kind != STAR_CATALOG_ALL_SKY)
|
||||
return 0;
|
||||
for (size_t tile_id = 0; tile_id < CATALOG_ALL_SKY_TILE_COUNT; ++tile_id) {
|
||||
if (!requested[tile_id])
|
||||
continue;
|
||||
if (stats != NULL)
|
||||
++stats->requested_tiles;
|
||||
if (catalog->tiles[tile_id].state == 0)
|
||||
++pending_count;
|
||||
}
|
||||
if (pending_count == 0)
|
||||
return 0;
|
||||
pending = calloc(pending_count, sizeof *pending);
|
||||
if (pending == NULL)
|
||||
return -1;
|
||||
size_t pending_index = 0;
|
||||
for (size_t tile_id = 0; tile_id < CATALOG_ALL_SKY_TILE_COUNT; ++tile_id)
|
||||
if (requested[tile_id] && catalog->tiles[tile_id].state == 0)
|
||||
pending[pending_index++].tile_id = tile_id;
|
||||
if (worker_count > (int)pending_count)
|
||||
worker_count = (int)pending_count;
|
||||
load_start = omp_get_wtime();
|
||||
#pragma omp parallel for num_threads(worker_count) schedule(static)
|
||||
for (size_t i = 0; i < pending_count; ++i) {
|
||||
const int ra_index =
|
||||
(int)(pending[i].tile_id % CATALOG_ALL_SKY_RA_TILES);
|
||||
const int dec_index =
|
||||
(int)(pending[i].tile_id / CATALOG_ALL_SKY_RA_TILES);
|
||||
pending[i].loaded = !load_tile_file(
|
||||
catalog, ra_index, dec_index, &pending[i].stars, &pending[i].count);
|
||||
}
|
||||
if (stats != NULL)
|
||||
stats->load_seconds = omp_get_wtime() - load_start;
|
||||
/* Only this serial commit mutates the shared catalog cache. */
|
||||
for (size_t i = 0; i < pending_count; ++i) {
|
||||
CatalogTile *tile = &catalog->tiles[pending[i].tile_id];
|
||||
if (pending[i].loaded) {
|
||||
tile->stars = pending[i].stars;
|
||||
tile->count = pending[i].count;
|
||||
tile->state = 1;
|
||||
catalog->count += tile->count;
|
||||
if (stats != NULL) {
|
||||
++stats->newly_loaded_tiles;
|
||||
stats->newly_loaded_stars += tile->count;
|
||||
}
|
||||
} else {
|
||||
tile->state = -1;
|
||||
if (stats != NULL)
|
||||
++stats->unavailable_tiles;
|
||||
}
|
||||
}
|
||||
result = 0;
|
||||
free(pending);
|
||||
return result;
|
||||
}
|
||||
|
||||
int catalog_visit_source_triangle(StarCatalog *catalog,
|
||||
const double direction[3][3],
|
||||
int load_missing, CatalogTileVisitor visitor,
|
||||
void *context)
|
||||
{
|
||||
if (catalog == NULL || direction == NULL || visitor == NULL)
|
||||
return -1;
|
||||
if (catalog->kind == STAR_CATALOG_MEMORY)
|
||||
return visitor(catalog->stars, catalog->count, 0, context);
|
||||
CatalogVisitContext visit_context = {
|
||||
.catalog = catalog, .direction = direction, .load_missing = load_missing,
|
||||
.visitor = visitor, .context = context};
|
||||
return visit_source_triangle_tile_indices(direction, visit_catalog_tile,
|
||||
&visit_context);
|
||||
}
|
||||
|
||||
void catalog_destroy(StarCatalog *catalog)
|
||||
{
|
||||
if (catalog->tiles != NULL)
|
||||
|
||||
+26
-1
@@ -9,7 +9,12 @@ typedef struct {
|
||||
double amplitude;
|
||||
} Star;
|
||||
|
||||
enum { CATALOG_ALL_SKY_RA_TILES = 360, CATALOG_ALL_SKY_DEC_TILES = 180 };
|
||||
enum {
|
||||
CATALOG_ALL_SKY_RA_TILES = 360,
|
||||
CATALOG_ALL_SKY_DEC_TILES = 180,
|
||||
CATALOG_ALL_SKY_TILE_COUNT =
|
||||
CATALOG_ALL_SKY_RA_TILES * CATALOG_ALL_SKY_DEC_TILES
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Star *stars;
|
||||
@@ -33,6 +38,14 @@ typedef struct {
|
||||
typedef int (*CatalogTileVisitor)(const Star *stars, size_t count,
|
||||
int fully_contained, void *context);
|
||||
|
||||
typedef struct {
|
||||
size_t requested_tiles;
|
||||
size_t newly_loaded_tiles;
|
||||
size_t unavailable_tiles;
|
||||
size_t newly_loaded_stars;
|
||||
double load_seconds;
|
||||
} CatalogPrefetchStats;
|
||||
|
||||
/*
|
||||
* Synthetic lensing fixture: stars lie on the union of 10-degree longitude
|
||||
* and latitude lines, sampled every 2 degrees. The eight longitude/hemisphere
|
||||
@@ -44,6 +57,18 @@ int catalog_load_csv(StarCatalog *catalog, const char *path);
|
||||
/* The directory contains tile_raRRR_decDDD.csv plus optional .done markers.
|
||||
* Tiles are loaded only after a source triangle intersects them. */
|
||||
int catalog_load_all_sky(StarCatalog *catalog, const char *directory);
|
||||
/* Add every one-degree tile which can intersect a source triangle to the
|
||||
* caller-owned CATALOG_ALL_SKY_TILE_COUNT-byte request bitmap. */
|
||||
int catalog_mark_source_triangle_tiles(
|
||||
const double direction[3][3],
|
||||
unsigned char requested[CATALOG_ALL_SKY_TILE_COUNT]);
|
||||
/* Load each requested, previously unseen tile once. File reads use at most
|
||||
* worker_count OpenMP workers, but cache mutation happens serially afterward,
|
||||
* leaving the cache immutable for later splatting. */
|
||||
int catalog_prefetch_marked_tiles(
|
||||
StarCatalog *catalog,
|
||||
const unsigned char requested[CATALOG_ALL_SKY_TILE_COUNT],
|
||||
int worker_count, CatalogPrefetchStats *stats);
|
||||
/* Visit only 1-degree all-sky tiles that can intersect this source triangle.
|
||||
* Call with load_missing=1 before parallel rendering, then 0 inside workers. */
|
||||
int catalog_visit_source_triangle(StarCatalog *catalog,
|
||||
|
||||
+13
-17
@@ -259,19 +259,13 @@ static size_t splat_catalog_triangles(const FrameLensMesh *mesh,
|
||||
return images;
|
||||
}
|
||||
|
||||
static int prefetch_catalog_tile(const Star *stars, size_t count,
|
||||
int fully_contained, void *context) {
|
||||
(void)stars;
|
||||
(void)count;
|
||||
(void)fully_contained;
|
||||
(void)context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prefetch_catalog_for_mesh(const FrameLensMesh *mesh,
|
||||
StarCatalog *catalog) {
|
||||
StarCatalog *catalog,
|
||||
int worker_count,
|
||||
CatalogPrefetchStats *stats) {
|
||||
if (catalog->kind != STAR_CATALOG_ALL_SKY)
|
||||
return;
|
||||
unsigned char requested[CATALOG_ALL_SKY_TILE_COUNT] = {0};
|
||||
for (size_t t = 0; t < mesh->triangle_count; ++t) {
|
||||
const LensVertex *vertex[3];
|
||||
if (!usable_triangle(mesh, &mesh->triangles[t], vertex))
|
||||
@@ -280,22 +274,24 @@ static void prefetch_catalog_for_mesh(const FrameLensMesh *mesh,
|
||||
{vertex[0]->n_infinity[0], vertex[0]->n_infinity[1], vertex[0]->n_infinity[2]},
|
||||
{vertex[1]->n_infinity[0], vertex[1]->n_infinity[1], vertex[1]->n_infinity[2]},
|
||||
{vertex[2]->n_infinity[0], vertex[2]->n_infinity[1], vertex[2]->n_infinity[2]}};
|
||||
(void)catalog_visit_source_triangle(catalog, direction, 1,
|
||||
prefetch_catalog_tile, NULL);
|
||||
(void)catalog_mark_source_triangle_tiles(direction, requested);
|
||||
}
|
||||
(void)catalog_prefetch_marked_tiles(catalog, requested, worker_count, stats);
|
||||
}
|
||||
|
||||
size_t frame_splat_catalog(const FrameLensMesh *mesh,
|
||||
StarCatalog *catalog, double *hdr, int width,
|
||||
int height, double exposure,
|
||||
const PointSpreadFunction *psf) {
|
||||
const PointSpreadFunction *psf,
|
||||
int catalog_load_workers,
|
||||
CatalogPrefetchStats *prefetch_stats) {
|
||||
if (mesh == NULL || catalog == NULL || hdr == NULL || exposure <= 0.0 ||
|
||||
psf == NULL || width <= 0 || height <= 0)
|
||||
psf == NULL || width <= 0 || height <= 0 || catalog_load_workers <= 0)
|
||||
return 0;
|
||||
|
||||
/* Tile I/O is deliberately serial and complete before OpenMP workers start.
|
||||
* The parallel splat pass then reads an immutable tile cache. */
|
||||
prefetch_catalog_for_mesh(mesh, catalog);
|
||||
/* A bounded parallel read phase completes before splatting. Its serial cache
|
||||
* commit leaves immutable tile data for the OpenMP splat workers. */
|
||||
prefetch_catalog_for_mesh(mesh, catalog, catalog_load_workers, prefetch_stats);
|
||||
|
||||
const size_t pixel_count = (size_t)width * height * 3;
|
||||
if (pixel_count > SIZE_MAX / sizeof(double) ||
|
||||
|
||||
+3
-1
@@ -38,7 +38,9 @@ int frame_lens_mesh_trace(FrameLensMesh *mesh, const SpacetimeSource *spacetime,
|
||||
size_t frame_splat_catalog(const FrameLensMesh *mesh,
|
||||
StarCatalog *catalog, double *hdr, int width,
|
||||
int height, double exposure,
|
||||
const PointSpreadFunction *psf);
|
||||
const PointSpreadFunction *psf,
|
||||
int catalog_load_workers,
|
||||
CatalogPrefetchStats *prefetch_stats);
|
||||
void frame_draw_mesh(const FrameLensMesh *mesh, double *hdr, int width,
|
||||
int height, double gray, double opacity);
|
||||
void frame_lens_mesh_destroy(FrameLensMesh *mesh);
|
||||
|
||||
+30
-5
@@ -31,6 +31,7 @@ typedef struct {
|
||||
double movie_start_time, movie_duration, movie_fps;
|
||||
double slab_duration;
|
||||
double minkowski_proper_acceleration;
|
||||
int catalog_load_workers;
|
||||
} Settings;
|
||||
|
||||
static int parse_int(const char *text, int *value) {
|
||||
@@ -108,7 +109,8 @@ static int parse_args(int argc, char **argv, Settings *s,
|
||||
.movie_duration = 2.0,
|
||||
.movie_fps = 30.0,
|
||||
.slab_duration = 64.0,
|
||||
.minkowski_proper_acceleration = 1.52};
|
||||
.minkowski_proper_acceleration = 1.52,
|
||||
.catalog_load_workers = 4};
|
||||
*write_path = NULL;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (!strcmp(argv[i], "--catalog") && i + 1 < argc)
|
||||
@@ -158,6 +160,8 @@ static int parse_args(int argc, char **argv, Settings *s,
|
||||
} else if (!strcmp(argv[i], "--proper-acceleration") && i + 1 < argc &&
|
||||
!parse_nonnegative(argv[++i],
|
||||
&s->minkowski_proper_acceleration)) {
|
||||
} else if (!strcmp(argv[i], "--catalog-load-workers") && i + 1 < argc &&
|
||||
!parse_int(argv[++i], &s->catalog_load_workers)) {
|
||||
} else if (!strcmp(argv[i], "--write-minkowski-accel-track") &&
|
||||
i + 1 < argc)
|
||||
s->write_minkowski_accel_track_path = argv[++i];
|
||||
@@ -204,14 +208,24 @@ static int render_observer_frame(const Settings *s, StarCatalog *catalog,
|
||||
free(hdr);
|
||||
return -1;
|
||||
}
|
||||
size_t images = frame_splat_catalog(&mesh, catalog, hdr, s->width, s->height,
|
||||
s->exposure, &s->psf);
|
||||
CatalogPrefetchStats prefetch = {0};
|
||||
size_t images = frame_splat_catalog(
|
||||
&mesh, catalog, hdr, s->width, s->height, s->exposure, &s->psf,
|
||||
s->catalog_load_workers, &prefetch);
|
||||
if (s->draw_mesh)
|
||||
frame_draw_mesh(&mesh, hdr, s->width, s->height, 0.5, 0.5);
|
||||
int result = write_tonemapped_image(output_path, hdr, s->width, s->height);
|
||||
fprintf(stderr, "Rendered %zu images from %zu catalog stars to %s (%s)\n",
|
||||
images, catalog->count, output_path,
|
||||
result == 0 ? "ok" : "write failed");
|
||||
if (catalog->kind == STAR_CATALOG_ALL_SKY)
|
||||
fprintf(stderr,
|
||||
"Catalog prefetch: %zu requested, %zu newly loaded (%zu stars), "
|
||||
"%zu unavailable in %.3f s; %d loader workers\n",
|
||||
prefetch.requested_tiles, prefetch.newly_loaded_tiles,
|
||||
prefetch.newly_loaded_stars, prefetch.unavailable_tiles,
|
||||
prefetch.load_seconds,
|
||||
s->catalog_load_workers);
|
||||
frame_lens_mesh_destroy(&mesh);
|
||||
free(hdr);
|
||||
return result;
|
||||
@@ -289,14 +303,24 @@ static int render_movie(const Settings *s, StarCatalog *catalog,
|
||||
free(hdr);
|
||||
goto done;
|
||||
}
|
||||
const size_t images = frame_splat_catalog(&movie.frames[i].mesh, catalog, hdr,
|
||||
s->width, s->height, s->exposure, &s->psf);
|
||||
CatalogPrefetchStats prefetch = {0};
|
||||
const size_t images = frame_splat_catalog(
|
||||
&movie.frames[i].mesh, catalog, hdr, s->width, s->height, s->exposure,
|
||||
&s->psf, s->catalog_load_workers, &prefetch);
|
||||
if (s->draw_mesh)
|
||||
frame_draw_mesh(&movie.frames[i].mesh, hdr, s->width, s->height, 0.5, 0.5);
|
||||
const int write_result = write_tonemapped_image(output_path, hdr, s->width, s->height);
|
||||
free(hdr);
|
||||
fprintf(stderr, "Rendered %zu images from %zu catalog stars to %s (%s)\n",
|
||||
images, catalog->count, output_path, write_result == 0 ? "ok" : "write failed");
|
||||
if (catalog->kind == STAR_CATALOG_ALL_SKY)
|
||||
fprintf(stderr,
|
||||
"Catalog prefetch: %zu requested, %zu newly loaded (%zu stars), "
|
||||
"%zu unavailable in %.3f s; %d loader workers\n",
|
||||
prefetch.requested_tiles, prefetch.newly_loaded_tiles,
|
||||
prefetch.newly_loaded_stars, prefetch.unavailable_tiles,
|
||||
prefetch.load_seconds,
|
||||
s->catalog_load_workers);
|
||||
if (write_result)
|
||||
goto done;
|
||||
}
|
||||
@@ -331,6 +355,7 @@ int main(int argc, char **argv) {
|
||||
"[--exposure E] [--observer-inward-speed V] "
|
||||
"[--psf-fwhm-pixels N] [--psf-moffat-beta N] "
|
||||
"[--coarse-cell-pixels N] [--draw-mesh] [--write-catalog PATH] "
|
||||
"[--catalog-load-workers N] "
|
||||
"[--observer-track PATH --frames-dir DIR --frames-prefix NAME "
|
||||
"--start-time T --duration T --fps N] "
|
||||
"[--proper-acceleration A --write-minkowski-accel-track PATH]\n",
|
||||
|
||||
Reference in new issue
Block a user