43#include <glib/gstdio.h>
44#include <json-glib/json-glib.h>
57static float *
read_f32(
const char *dir,
const char *
name,
const size_t count)
60 snprintf(path,
sizeof(path),
"%s/%s", dir,
name);
61 FILE *
f = g_fopen(path,
"rb");
62 if(!
f) { fprintf(stderr,
"cannot open %s\n", path);
return NULL; }
63 float *buf = (
float *)malloc(
sizeof(
float) * count);
64 if(!buf) { fclose(
f);
return NULL; }
65 const size_t got = fread(buf,
sizeof(
float), count,
f);
69 fprintf(stderr,
"%s: expected %zu floats, got %zu -- stale or mismatched fixture\n", path, count, got);
82 snprintf(path,
sizeof(path),
"%s/fixture-meta.json", dir);
83 JsonParser *parser = json_parser_new();
85 if(json_parser_load_from_file(parser, path, NULL))
87 JsonObject *root = json_node_get_object(json_parser_get_root(parser));
88 if(json_object_has_member(root,
"model_sha256"))
90 const char *want = json_object_get_string_member(root,
"model_sha256");
93 if(g_file_get_contents(model_path, &blob, &len, NULL))
95 gchar *got = g_compute_checksum_for_data(G_CHECKSUM_SHA256, (
const guchar *)blob, len);
96 if(g_strcmp0(got, want))
98 fprintf(stderr,
"FAIL: fixture was generated from a different model\n pins %s\n got %s\n",
107 g_object_unref(parser);
111static double max_abs_diff(
const float *a,
const float *b,
const size_t n,
size_t *where)
114 for(
size_t i = 0;
i <
n;
i++)
116 const double d = fabs((
double)a[
i] - (
double)b[
i]);
117 if(
d > worst) { worst =
d;
if(where) *where =
i; }
126 fprintf(stderr,
"usage: %s <model.anselnn> <fixture-dir> [N]\n", arg[0]);
129 const char *model_path = arg[1];
130 const char *fixture_dir = arg[2];
131 const int n = (argc > 3 && arg[3][0] !=
'-') ? atoi(arg[3]) : 96;
134 float *in = NULL, *expected = NULL, *cpu = NULL, *gpu = NULL, *head = NULL;
137 void *dev_in = NULL, *dev_out = NULL;
144 for(
int i = 1;
i < argc;
i++)
145 if(!strcmp(arg[
i],
"--core")) { core_at =
i;
break; }
147 char *m_arg[] = {
"--library",
":memory:" };
148 const int m_argc = (int)(
sizeof(m_arg) /
sizeof(m_arg[0]));
149 const int passthrough = (core_at < argc) ? (argc - core_at - 1) : 0;
150 char **argv = (
char **)malloc(
sizeof(
char *) * (size_t)(1 + passthrough + m_argc));
153 argv[dt_argc++] = arg[0];
154 for(
int i = 0;
i < passthrough;
i++) argv[dt_argc++] = arg[core_at + 1 +
i];
155 for(
int i = 0;
i < m_argc;
i++) argv[dt_argc++] = m_arg[
i];
160 char err[256] = { 0 };
162 if(!
model) { fprintf(stderr,
"cannot load %s: %s\n", model_path, err);
goto done; }
166 const size_t plane = (size_t)
n *
n;
168 in =
read_f32(fixture_dir,
"fixture-input.f32", plane * in_ch);
169 expected =
read_f32(fixture_dir,
"fixture-expected.f32", plane * out_ch);
170 cpu = (
float *)malloc(
sizeof(
float) * plane * out_ch);
171 gpu = (
float *)malloc(
sizeof(
float) * plane * out_ch);
172 head = (
float *)malloc(
sizeof(
float) * plane * out_ch);
173 if(!in || !expected || !cpu || !gpu || !head)
goto done;
175 printf(
"model %s: in=%d out=%d, fixture %dx%d\n", model_path, in_ch, out_ch,
n,
n);
180 fprintf(stderr,
"CPU stage failed\n");
184 const double cpu_err =
max_abs_diff(cpu, expected, plane * out_ch, &
w1);
185 printf(
" torch vs CPU : max abs err %.3g (at %zu: %.6f vs %.6f)\n",
186 cpu_err,
w1, cpu[
w1], expected[
w1]);
192 printf(
" torch vs OpenCL : SKIPPED (no OpenCL device available)\n");
193 result = (cpu_err > 2e-4) ? 1 : 0;
196 printf(
" using OpenCL device %d\n", devid);
203 if(!dev_in || !dev_out) { fprintf(stderr,
"device allocation failed\n");
dt_opencl_release_device(devid);
goto done; }
213 fprintf(stderr,
"dt_nn_unet_apply_stage_cl failed (%d)\n", rc);
227 for(
size_t i = 0;
i < plane * out_ch;
i++) gpu[
i] = in[
i] - head[
i];
229 size_t w2 = 0,
w3 = 0;
230 const double gpu_err =
max_abs_diff(gpu, expected, plane * out_ch, &
w2);
231 const double dev_err =
max_abs_diff(gpu, cpu, plane * out_ch, &
w3);
232 printf(
" torch vs OpenCL : max abs err %.3g (at %zu: %.6f vs %.6f)\n",
233 gpu_err,
w2, gpu[
w2], expected[
w2]);
234 printf(
" CPU vs OpenCL : max abs err %.3g\n", dev_err);
237 const double tol = 2e-4;
238 const int pass = (cpu_err <= tol) && (gpu_err <= tol);
239 printf(
" %s (tolerance %.0e)\n", pass ?
"PASS" :
"FAIL", tol);
240 result = pass ? 0 : 1;
static int check_model_hash(const char *dir, const char *model_path)
static double max_abs_diff(const float *a, const float *b, const size_t n, size_t *where)
static float * read_f32(const char *dir, const char *name, const size_t count)
int dt_init(int argc, char *argv[], const gboolean init_gui, const gboolean load_data)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
void dt_nn_cl_destroy(dt_nn_cl_t *cl)
int dt_nn_unet_apply_stage_cl(const dt_nn_model_t *m, int stage, dt_nn_cl_t *cl, int devid, cl_mem dev_in, cl_mem dev_out, int width, int height)
void dt_nn_model_free(dt_nn_model_t *m)
int dt_nn_model_in_channels(const dt_nn_model_t *m)
dt_nn_cl_t * dt_nn_cl_create(int program)
int dt_nn_unet_apply_stage(const dt_nn_model_t *m, int stage, const float *in, float *out, int width, int height, int apply_residual)
dt_nn_model_t * dt_nn_model_load(const char *path, char *err, size_t err_len)
int dt_nn_model_out_channels(const dt_nn_model_t *m)
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
int dt_opencl_write_buffer_to_device(const int devid, void *host, void *device, const size_t offset, const size_t size, const int blocking)
int dt_opencl_read_buffer_from_device(const int devid, void *host, void *device, const size_t offset, const size_t size, const int blocking)
int dt_opencl_reserve_device_for_pipe(const int pipetype)
Reserve a device for a pipe run: choose a free one by the pipe's priority list and take its lock....
void dt_opencl_release_device(const int devid)
Release a device reserved by either reserve function.
void dt_opencl_release_mem_object(cl_mem mem)
@ DT_DEV_PIXELPIPE_EXPORT