From 4bf2de1b400c0766773a80b2ffd48bb564ef6445 Mon Sep 17 00:00:00 2001 From: Yingjie Wang Date: Mon, 14 Sep 2026 19:15:53 -0400 Subject: [PATCH] Feat: Add dummy PSF support workload diagnostics Add an opt-in DUMMY_PSF_SUPPORT_STATS mode to the diagnostic-only dummy consumer. It records per-chunk cache-limited 16px support references, touched tiles, 256-reference tasks, maximum tile list and first-pass counting time, then reports percentiles. No HDR is produced and no references are materialized or uploaded. --- src/dummy_psf.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++-- src/dummy_psf.h | 2 +- 2 files changed, 128 insertions(+), 6 deletions(-) diff --git a/src/dummy_psf.c b/src/dummy_psf.c index 25e58d5..88276cd 100644 --- a/src/dummy_psf.c +++ b/src/dummy_psf.c @@ -7,13 +7,16 @@ #include #include -enum { DUMMY_SELECTOR_TILE = 32 }; +enum { DUMMY_SELECTOR_TILE = 32, DUMMY_SUPPORT_TILE = 16, + DUMMY_TASK_EVENTS = 256 }; typedef struct { size_t worker, events, triangles, occupied_tiles; double event_bbox_diagonal, triangle_bbox_diagonal; double maximum_triangle_jump; int adaptive_tile16; + size_t support_refs, support_tiles, support_tasks, maximum_tile_list; + double support_pass_seconds; } DummyChunkSample; struct DummyPsfSink { @@ -21,6 +24,8 @@ struct DummyPsfSink { size_t count, capacity, event_capacity; size_t total_events; int width, height, tiles_x, tiles_y; + int support_tiles_x, support_tiles_y, support_stats; + double max_radius_pixels; int failed; omp_lock_t lock; }; @@ -28,6 +33,9 @@ struct DummyPsfSink { struct DummyPsfChunk { DummyPsfSink *sink; uint32_t *tile_generation; + PsfCachedEvent *support_events; + uint32_t *support_counts, *support_touched; + size_t support_touched_count; uint32_t generation; size_t worker, events, triangles, occupied_tiles; size_t last_triangle; @@ -66,8 +74,9 @@ static int append_sample(DummyPsfSink *sink, DummyChunkSample sample) { } int dummy_psf_sink_create(DummyPsfSink **out, int width, int height, - size_t event_capacity) { - if (out == NULL || width <= 0 || height <= 0 || event_capacity == 0) + size_t event_capacity, double max_radius_pixels) { + if (out == NULL || width <= 0 || height <= 0 || event_capacity == 0 || + !isfinite(max_radius_pixels) || max_radius_pixels < 0) return -1; DummyPsfSink *sink = calloc(1, sizeof *sink); if (sink == NULL) @@ -77,6 +86,14 @@ int dummy_psf_sink_create(DummyPsfSink **out, int width, int height, sink->tiles_x = (width + DUMMY_SELECTOR_TILE - 1) / DUMMY_SELECTOR_TILE; sink->tiles_y = (height + DUMMY_SELECTOR_TILE - 1) / DUMMY_SELECTOR_TILE; sink->event_capacity = event_capacity; + sink->support_tiles_x = (width + DUMMY_SUPPORT_TILE - 1) / DUMMY_SUPPORT_TILE; + sink->support_tiles_y = (height + DUMMY_SUPPORT_TILE - 1) / DUMMY_SUPPORT_TILE; + sink->max_radius_pixels = max_radius_pixels; + const char *support = getenv("DUMMY_PSF_SUPPORT_STATS"); + if (support && strcmp(support, "0") && strcmp(support, "1")) { + free(sink); return -1; + } + sink->support_stats = support && !strcmp(support, "1"); omp_init_lock(&sink->lock); *out = sink; return 0; @@ -89,13 +106,24 @@ int dummy_psf_chunk_create(DummyPsfChunk **out, DummyPsfSink *sink, DummyPsfChunk *chunk = calloc(1, sizeof *chunk); if (chunk == NULL) return -1; + chunk->sink = sink; const size_t tile_count = (size_t)sink->tiles_x * sink->tiles_y; chunk->tile_generation = calloc(tile_count, sizeof *chunk->tile_generation); if (chunk->tile_generation == NULL) { free(chunk); return -1; } - chunk->sink = sink; + if (sink->support_stats) { + const size_t count = (size_t)sink->support_tiles_x * sink->support_tiles_y; + chunk->support_events = malloc(sink->event_capacity * sizeof *chunk->support_events); + chunk->support_counts = calloc(count, sizeof *chunk->support_counts); + chunk->support_touched = malloc(count * sizeof *chunk->support_touched); + if (!chunk->support_events || !chunk->support_counts || + !chunk->support_touched) { + dummy_psf_chunk_destroy(chunk); + return -1; + } + } chunk->worker = worker_id; chunk->generation = 1; *out = chunk; @@ -127,6 +155,40 @@ int dummy_psf_chunk_flush(DummyPsfChunk *chunk) { const double triangle_diagonal = hypot(chunk->triangle_max_x - chunk->triangle_min_x, chunk->triangle_max_y - chunk->triangle_min_y); + size_t support_refs = 0, support_tasks = 0, maximum_tile_list = 0; + double support_pass_seconds = 0.0; + if (chunk->sink->support_stats) { + const double start = omp_get_wtime(); + for (size_t i = 0; i < chunk->support_touched_count; ++i) + chunk->support_counts[chunk->support_touched[i]] = 0; + chunk->support_touched_count = 0; + const int tiles_x = chunk->sink->support_tiles_x; + for (size_t i = 0; i < chunk->events; ++i) { + const PsfCachedEvent *event = &chunk->support_events[i]; + const int support = (int)fmin(ceil(event->support_radius), + chunk->sink->max_radius_pixels); + const int bx = (int)floor(event->x), by = (int)floor(event->y); + const int x0 = fmax(0, bx - support); + const int x1 = fmin(chunk->sink->width - 1, bx + support); + const int y0 = fmax(0, by - support); + const int y1 = fmin(chunk->sink->height - 1, by + support); + if (x0 > x1 || y0 > y1) continue; + for (int y = y0 / DUMMY_SUPPORT_TILE; y <= y1 / DUMMY_SUPPORT_TILE; ++y) + for (int x = x0 / DUMMY_SUPPORT_TILE; x <= x1 / DUMMY_SUPPORT_TILE; ++x) { + const size_t tile = (size_t)y * tiles_x + x; + if (!chunk->support_counts[tile]) + chunk->support_touched[chunk->support_touched_count++] = tile; + ++chunk->support_counts[tile]; + ++support_refs; + } + } + for (size_t i = 0; i < chunk->support_touched_count; ++i) { + const size_t count = chunk->support_counts[chunk->support_touched[i]]; + if (count > maximum_tile_list) maximum_tile_list = count; + support_tasks += (count + DUMMY_TASK_EVENTS - 1) / DUMMY_TASK_EVENTS; + } + support_pass_seconds = omp_get_wtime() - start; + } const DummyChunkSample sample = { .worker = chunk->worker, .events = chunk->events, @@ -136,7 +198,12 @@ int dummy_psf_chunk_flush(DummyPsfChunk *chunk) { .triangle_bbox_diagonal = triangle_diagonal, .maximum_triangle_jump = chunk->maximum_triangle_jump, .adaptive_tile16 = chunk->events >= 8192 && chunk->occupied_tiles != 0 && - chunk->events / chunk->occupied_tiles >= 32}; + chunk->events / chunk->occupied_tiles >= 32, + .support_refs = support_refs, + .support_tiles = chunk->support_touched_count, + .support_tasks = support_tasks, + .maximum_tile_list = maximum_tile_list, + .support_pass_seconds = support_pass_seconds}; const int result = append_sample(chunk->sink, sample); reset_chunk(chunk); return result; @@ -150,6 +217,8 @@ int dummy_psf_chunk_emit(DummyPsfChunk *chunk, const PsfCachedEvent *event, if (chunk->events == chunk->sink->event_capacity && dummy_psf_chunk_flush(chunk)) return -1; + if (chunk->sink->support_stats) + chunk->support_events[chunk->events] = *event; if (!chunk->have_event) { chunk->event_min_x = chunk->event_max_x = event->x; @@ -204,6 +273,9 @@ void dummy_psf_chunk_destroy(DummyPsfChunk *chunk) { return; (void)dummy_psf_chunk_flush(chunk); free(chunk->tile_generation); + free(chunk->support_events); + free(chunk->support_counts); + free(chunk->support_touched); free(chunk); } @@ -284,6 +356,51 @@ static void report_group(const DummyPsfSink *sink, int full_only, free(events); } +static void report_support_group(const DummyPsfSink *sink, int full_only, + const char *label) { + size_t count = 0, total_refs = 0, total_tasks = 0; + double total_pass = 0.0; + for (size_t i = 0; i < sink->count; ++i) + if (!full_only || sink->samples[i].events == sink->event_capacity) + ++count; + if (!count || count > SIZE_MAX / (5 * sizeof(double))) return; + double *values = malloc(5 * count * sizeof *values); + if (!values) { fputs("Dummy PSF support report allocation failed\n", stderr); return; } + const char *names[] = {"support_16px_references", "support_16px_tiles", + "support_16px_tasks", "maximum_tile_list", + "support_first_pass_ms"}; + size_t next = 0; + for (size_t i = 0; i < sink->count; ++i) { + const DummyChunkSample *sample = &sink->samples[i]; + if (full_only && sample->events != sink->event_capacity) continue; + values[next] = sample->support_refs; + values[count + next] = sample->support_tiles; + values[2 * count + next] = sample->support_tasks; + values[3 * count + next] = sample->maximum_tile_list; + values[4 * count + next] = 1000.0 * sample->support_pass_seconds; + total_refs += sample->support_refs; + total_tasks += sample->support_tasks; + total_pass += sample->support_pass_seconds; + ++next; + } + for (size_t field = 0; field < 5; ++field) { + double *series = values + field * count; + qsort(series, count, sizeof *series, compare_double); + fprintf(stderr, + "Dummy PSF %s %s: min=%.3f p10=%.3f p25=%.3f p50=%.3f " + "p75=%.3f p90=%.3f p99=%.3f max=%.3f\n", + label, names[field], series[0], + percentile(series, count, 0.10), percentile(series, count, 0.25), + percentile(series, count, 0.50), percentile(series, count, 0.75), + percentile(series, count, 0.90), percentile(series, count, 0.99), + series[count - 1]); + } + fprintf(stderr, + "Dummy PSF %s support totals: references=%zu tasks=%zu summed_first_pass=%.3f s\n", + label, total_refs, total_tasks, total_pass); + free(values); +} + void dummy_psf_sink_report(const DummyPsfSink *sink) { if (sink == NULL) return; @@ -298,6 +415,11 @@ void dummy_psf_sink_report(const DummyPsfSink *sink) { sink->event_capacity, DUMMY_SELECTOR_TILE); report_group(sink, 0, "all"); report_group(sink, 1, "full"); + if (sink->support_stats) { + fputs("Dummy PSF support diagnostic: 16px rectangular cache-limited first pass only; no references were materialized or uploaded.\n", stderr); + report_support_group(sink, 0, "all"); + report_support_group(sink, 1, "full"); + } } void dummy_psf_sink_destroy(DummyPsfSink *sink) { diff --git a/src/dummy_psf.h b/src/dummy_psf.h index 8c16729..62f61eb 100644 --- a/src/dummy_psf.h +++ b/src/dummy_psf.h @@ -11,7 +11,7 @@ typedef struct DummyPsfChunk DummyPsfChunk; /* Diagnostic-only consumer for the production PSF event stream. It preserves * per-worker chunk boundaries but never accumulates or writes an HDR image. */ int dummy_psf_sink_create(DummyPsfSink **out, int width, int height, - size_t event_capacity); + size_t event_capacity, double max_radius_pixels); int dummy_psf_chunk_create(DummyPsfChunk **out, DummyPsfSink *sink, size_t worker_id); int dummy_psf_chunk_emit(DummyPsfChunk *chunk, const PsfCachedEvent *event,