FFmpeg
checkasm.c
Go to the documentation of this file.
1 /*
2  * Copyright © 2025, Niklas Haas
3  * Copyright © 2018, VideoLAN and dav1d authors
4  * Copyright © 2018, Two Orioles, LLC
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  * this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright notice,
14  * this list of conditions and the following disclaimer in the documentation
15  * and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include "checkasm_config.h"
31 
32 #if HAVE_PTHREAD_SETAFFINITY_NP
33  /* _GNU_SOURCE is required for pthread_setaffinity_np and CPU_SET on glibc. */
34  #ifndef _GNU_SOURCE
35  #define _GNU_SOURCE
36  #endif
37 #endif
38 
39 #include <assert.h>
40 #include <errno.h>
41 #include <inttypes.h>
42 #include <limits.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #include "checkasm/checkasm.h"
49 #include "checkasm/test.h"
50 #include "cpu.h"
51 #include "function.h"
52 #include "html_data.h"
53 #include "internal.h"
54 #include "stats.h"
55 
56 #ifndef _WIN32
57  #if HAVE_PTHREAD_SETAFFINITY_NP
58  #include <pthread.h>
59  #if HAVE_PTHREAD_NP_H
60  #include <pthread_np.h>
61  #endif
62  #endif
63 #endif
64 
65 #ifdef _WIN32
66  #include <windows.h>
67 #endif
68 
69 #if HAVE_PRCTL
70  #include <sys/prctl.h>
71 #endif
72 
73 /* Internal state */
75 static CheckasmStats stats; /* temporary buffer for function measurements */
76 
77 /* Current function/test state, reset after each test run */
78 static struct {
80 
81  /* (Re)set by check_cpu_flag() */
86  const char *test_name;
89 
90  /* (Re)set per function (check_func, bench_finish) */
93  char *func_variant;
94  uint64_t cycles;
95 
96  /* Overall stats for this test run */
97  int num_funcs; /* known functions */
98  int num_checked; /* checked versions */
99  int num_failed; /* failed versions */
100  int num_benched; /* benched versions */
101  int prev_checked, prev_failed; /* reset by report() */
102  int saved_checked, saved_failed; /* for restoring after a crash */
103  double var_sum, var_max;
104 } current;
105 
106 /* Global state for the entire checkasm_run() call */
107 static struct {
108  /* Miscellaneous global state (cosmetic) */
111  unsigned test_iter;
112 
113  /* Timing code measurements (aggregated over multiple trials) */
116 
117  /* Runtime constants */
118  uint64_t target_cycles;
120 } state;
121 
123 {
124  return current.cpu_flags;
125 }
126 
128 {
129  return current.cpu;
130 }
131 
132 /* Get the suffix of the specified cpu flag */
133 static const char *cpu_suffix(const CheckasmCpuInfo *cpu)
134 {
135  return cpu ? cpu->suffix : "c";
136 }
137 
138 static const char *ver_suffix(const CheckasmFuncVersion *ver)
139 {
140  return ver->suffix ? ver->suffix : cpu_suffix(ver->cpu);
141 }
142 
143 /* Returns the coefficient of variation (CV) */
144 static double relative_error(double lvar)
145 {
146  return sqrt(exp(lvar) - 1.0);
147 }
148 
149 static inline char separator(CheckasmFormat format)
150 {
151  switch (format) {
152  case CHECKASM_FORMAT_CSV: return ',';
153  case CHECKASM_FORMAT_TSV: return '\t';
154  default: return 0;
155  }
156 }
157 
158 static void json_var(CheckasmJson *json, const char *key, const char *unit,
159  const CheckasmVar var)
160 {
161  if (key)
162  checkasm_json_push(json, key, '{');
163  if (unit)
164  checkasm_json_str(json, "unit", unit);
165  checkasm_json(json, "mode", "%g", checkasm_mode(var));
166  checkasm_json(json, "median", "%g", checkasm_median(var));
167  checkasm_json(json, "mean", "%g", checkasm_mean(var));
168  checkasm_json(json, "lowerCI", "%g", checkasm_sample(var, -1.96));
169  checkasm_json(json, "upperCI", "%g", checkasm_sample(var, 1.96));
170  checkasm_json(json, "stdDev", "%g", checkasm_stddev(var));
171  checkasm_json(json, "logMean", "%g", var.lmean);
172  checkasm_json(json, "logVar", "%g", var.lvar);
173  if (key)
174  checkasm_json_pop(json, '}');
175 }
176 
177 static void json_measurement(CheckasmJson *json, const char *key, const char *unit,
178  const CheckasmMeasurement measurement)
179 {
180  const CheckasmVar result = checkasm_measurement_result(measurement);
181  if (key)
182  checkasm_json_push(json, key, '{');
183  json_var(json, NULL, unit, result);
184  checkasm_json(json, "numMeasurements", "%d", measurement.nb_measurements);
185 
186  if (measurement.stats.nb_samples) {
187  json_var(json, "regressionSlope", unit,
188  checkasm_stats_estimate(&measurement.stats));
189  checkasm_json_push(json, "rawData", '[');
190  for (int i = 0; i < measurement.stats.nb_samples; i++) {
191  const CheckasmSample s = measurement.stats.samples[i];
192  checkasm_json(json, NULL, "{ \"iters\": %d, \"cycles\": %" PRIu64 " }",
193  s.count, s.sum);
194  }
195  checkasm_json_pop(json, ']');
196  }
197 
198  if (key)
199  checkasm_json_pop(json, '}');
200 }
201 
202 static void cpu_info_json(void *priv, const char *fmt, ...)
203 {
204  CheckasmJson *json = priv;
205  char buf[128];
206  va_list ap;
207  va_start(ap, fmt);
208  vsnprintf(buf, sizeof(buf), fmt, ap);
209  va_end(ap);
210 
211  checkasm_json_str(json, NULL, buf);
212 }
213 
214 static void cpu_mask_json(CheckasmJson *json, const CheckasmConfig cfg,
215  const CheckasmCpu cpu_mask)
216 {
217  if (!cpu_mask)
218  return;
219 
220  checkasm_json_push(json, "mask", '[');
221  for (const CheckasmCpuInfo *info = cfg.cpu_flags; info->flag; info++) {
222  if ((cpu_mask & info->flag) != info->flag)
223  continue;
224  checkasm_json_str(json, NULL, info->suffix);
225  }
226  checkasm_json_pop(json, ']');
227 }
228 
229 struct IterState {
230  const char *test;
231  const char *report;
233 };
234 
235 static void print_bench_header(struct IterState *const iter)
236 {
240  CheckasmJson *const json = &iter->json;
241 
242  switch (cfg.format) {
243  case CHECKASM_FORMAT_TSV:
244  case CHECKASM_FORMAT_CSV:
245  if (cfg.verbose) {
246  const char sep = separator(cfg.format);
247  printf("name%csuffix%c%ss%cstddev%cnanoseconds\n", sep, sep,
248  checkasm_perf.unit, sep, sep);
249  printf("nop%c%c%.4f%c%.5f%c%.4f\n", sep, sep, checkasm_mode(nop_cycles), sep,
250  checkasm_stddev(nop_cycles), sep, checkasm_mode(nop_time));
251  }
252  break;
254  printf("<!doctype html>\n"
255  "<html>\n"
256  "<head>\n"
257  " <meta charset=\"utf-8\"/>\n"
258  " <title>checkasm report</title>\n"
259  " <script type=\"module\">\n"
260  " %s"
261  " %s"
262  " </script>\n"
263  " <style>\n"
264  " %s"
265  " </style>\n"
266  " <script type=\"application/json\" id=\"report-data\">\n",
268  FALLTHROUGH;
270  checkasm_json_push(json, NULL, '{');
271  checkasm_json_str(json, "checkasmVersion", CHECKASM_VERSION);
272  checkasm_json(json, "numChecked", "%d", current.num_checked);
273  checkasm_json(json, "numFailed", "%d", current.num_failed);
274  checkasm_json(json, "targetCycles", "%" PRIu64, state.target_cycles);
275  checkasm_json(json, "numBenchmarks", "%d", current.num_benched);
276  checkasm_json_push(json, "config", '{');
277  if (cfg.test_pattern)
278  checkasm_json_str(json, "testPattern", cfg.test_pattern);
279  if (cfg.function_pattern)
280  checkasm_json_str(json, "functionPattern", cfg.function_pattern);
281  checkasm_json(json, "benchUsec", "%u", cfg.bench_usec);
282  checkasm_json(json, "seed", "%u", cfg.seed);
283  checkasm_json(json, "repeat", "%u", cfg.repeat);
284  if (cfg.cpu_affinity_set)
285  checkasm_json(json, "cpuAffinity", "%u", cfg.cpu_affinity);
286  checkasm_json_pop(json, '}'); /* close config */
287  checkasm_json_push(json, "cpuInfo", '[');
289  checkasm_json_pop(json, ']');
290  checkasm_json_push(json, "cpuFlags", '{');
291  for (const CheckasmCpuInfo *info = cfg.cpu_flags; info->flag; info++) {
292  const int available = (cfg.cpu & info->flag) == info->flag;
293  checkasm_json_push(json, info->suffix, '{');
294  checkasm_json_str(json, "name", info->name);
295  checkasm_json(json, "available", available ? "true" : "false");
296  cpu_mask_json(json, cfg, info->mask);
297  checkasm_json_pop(json, '}');
298  }
299  checkasm_json_pop(json, '}'); /* close cpuFlags */
300  checkasm_json_push(json, "tests", '[');
301  for (const CheckasmTest *test = cfg.tests; test->func; test++)
302  checkasm_json_str(json, NULL, test->name);
303  checkasm_json_pop(json, ']'); /* close tests */
304  char perf_scale_unit[32];
305  snprintf(perf_scale_unit, sizeof(perf_scale_unit), "nsec/%s", checkasm_perf.unit);
306  json_measurement(json, "nopCycles", checkasm_perf.unit, state.nop_cycles);
307  json_measurement(json, "timerScale", perf_scale_unit, state.perf_scale);
308  json_var(json, "nopTime", checkasm_perf.unit, nop_time);
309  checkasm_json(json, "numFunctions", "%d", current.num_funcs);
310  checkasm_json_push(json, "functions", '{');
311  break;
313  checkasm_fprintf(stdout, COLOR_YELLOW, "Benchmark results:\n");
314  checkasm_fprintf(stdout, COLOR_GREEN, " name%*ss",
315  5 + state.max_function_name_length, checkasm_perf.unit);
316  if (cfg.verbose) {
317  checkasm_fprintf(stdout, COLOR_GREEN, " +/- stddev %*s", 26,
318  "time (nanoseconds)");
319  }
320  checkasm_fprintf(stdout, COLOR_GREEN, " (vs ref)\n");
321  if (cfg.verbose) {
322  printf(" nop:%*.1f +/- %-7.1f %11.1f ns +/- %-6.1f\n",
323  6 + state.max_function_name_length, checkasm_mode(nop_cycles),
325  checkasm_stddev(nop_time));
326  }
327  break;
328  }
329 }
330 
331 static void print_bench_footer(struct IterState *const iter)
332 {
333  const double err_rel = relative_error(current.var_sum / current.num_benched);
334  const double err_max = relative_error(current.var_max);
335  CheckasmJson *const json = &iter->json;
336 
337  switch (cfg.format) {
338  case CHECKASM_FORMAT_TSV:
339  case CHECKASM_FORMAT_CSV: break;
341  if (cfg.verbose) {
342  printf(" - average timing error: %.3f%% across %d benchmarks "
343  "(maximum %.3f%%)\n",
344  100.0 * err_rel, current.num_benched, 100.0 * err_max);
345  }
346  break;
349  checkasm_json_pop(json, '}'); /* close functions */
350  checkasm_json(json, "averageError", "%g", err_rel);
351  checkasm_json(json, "maximumError", "%g", err_max);
352  checkasm_json_pop(json, '}'); /* close root */
353 
355  printf(" </script>\n"
356  " <meta name=\"viewport\" content=\"width=device-width, "
357  "initial-scale=1\">\n"
358  "</head>\n"
359  "%s"
360  "</html>\n",
362  }
363  break;
364  }
365 }
366 
367 static void print_bench_iter(const CheckasmFunc *const f, struct IterState *const iter)
368 {
369  CheckasmJson *const json = &iter->json;
370  const char sep = separator(cfg.format);
371  if (!f)
372  return;
373 
374  print_bench_iter(f->child[0], iter);
375 
376  const CheckasmFuncVersion *ref = &f->versions;
377  const CheckasmFuncVersion *v = ref;
380 
381  /* Defer pushing the function header until we know that we have at least one
382  * benchmark to report */
383  int json_func_pushed = 0;
384 
385  do {
386  if (v->cycles.nb_measurements) {
388  const CheckasmVar raw_ref = checkasm_measurement_result(ref->cycles);
389 
391  const CheckasmVar cycles_ref = checkasm_var_sub(raw_ref, nop_cycles);
392  const CheckasmVar ratio = checkasm_var_div(cycles_ref, cycles);
393  const CheckasmVar raw_time = checkasm_var_mul(raw, perf_scale);
395 
396  switch (cfg.format) {
399  if (!json_func_pushed) {
400  checkasm_json_push(json, f->name, '{');
401  checkasm_json_str(json, "testName", f->test_name);
402  if (f->report_name)
403  checkasm_json_str(json, "reportName", f->report_name);
404  checkasm_json_push(json, "versions", '{');
405  json_func_pushed = 1;
406  }
407 
408  checkasm_json_push(json, ver_suffix(v), '{');
409  json_measurement(json, "rawCycles", checkasm_perf.unit, v->cycles);
410  json_var(json, "rawTime", "nsec", raw_time);
411  json_var(json, "adjustedCycles", checkasm_perf.unit, cycles);
412  json_var(json, "adjustedTime", "nsec", time);
413  if (v != ref && ref->cycles.nb_measurements)
414  json_var(json, "ratio", NULL, checkasm_var_div(cycles_ref, cycles));
415  checkasm_json_pop(json, '}'); /* close version */
416  break;
417  case CHECKASM_FORMAT_TSV:
418  case CHECKASM_FORMAT_CSV:
419  printf("%s%c%s%c%.4f%c%.5f%c%.4f\n", f->name, sep, ver_suffix(v),
420  sep, checkasm_mode(cycles), sep, checkasm_stddev(cycles), sep,
421  checkasm_mode(time));
422  break;
424  const int pad = 12 + state.max_function_name_length
425  - printf(" %s_%s:", f->name, ver_suffix(v));
426  printf("%*.1f", imax(pad, 0), checkasm_mode(cycles));
427  if (cfg.verbose) {
428  printf(" +/- %-7.1f %11.1f ns +/- %-6.1f", checkasm_stddev(cycles),
429  checkasm_mode(time), checkasm_stddev(time));
430  }
431  if (v != ref && ref->cycles.nb_measurements) {
432  const double ratio_lo = checkasm_sample(ratio, -1.0);
433  const double ratio_hi = checkasm_sample(ratio, 1.0);
434  const int color = ratio_lo >= 10.0 ? COLOR_GREEN
435  : ratio_hi >= 1.1 && ratio_lo >= 1.0 ? COLOR_DEFAULT
436  : ratio_hi >= 1.0 ? COLOR_YELLOW
437  : COLOR_RED;
438  printf(" (");
439  checkasm_fprintf(stdout, color, "%5.2fx", checkasm_mode(ratio));
440  printf(")");
441  }
442  printf("\n");
443  break;
444  }
445  }
446  } while ((v = v->next));
447 
448  if (json_func_pushed) {
449  checkasm_json_pop(json, '}'); /* close versions */
450  checkasm_json_pop(json, '}'); /* close function */
451  }
452 
453  print_bench_iter(f->child[1], iter);
454 }
455 
456 static void print_benchmarks(void)
457 {
458  struct IterState iter = { .json.file = stdout };
459  print_bench_header(&iter);
460  print_bench_iter(current.tree.root, &iter);
461  print_bench_footer(&iter);
462  assert(iter.json.level == 0);
463 }
464 
465 /* Decide whether or not the current function needs to be benchmarked */
467 {
468  return !current.num_failed && cfg.bench && !checkasm_interrupted;
469 }
470 
472 {
474  return 0;
475 
476  /* This limit should be impossible to hit in practice */
478  return 0;
479 
480  /* Try and gather at least 30 samples for statistical validity, even if
481  * it means exceeding the time budget */
482  if (current.cycles < state.target_cycles || stats.nb_samples < 30)
483  return stats.next_count;
484  else
485  return 0;
486 }
487 
488 /* Update benchmark results of the current function */
489 void checkasm_bench_update(const int iterations, const uint64_t cycles)
490 {
491  checkasm_stats_add(&stats, (CheckasmSample) { cycles, iterations });
492  checkasm_stats_count_grow(&stats, cycles, state.target_cycles);
493  current.cycles += cycles;
494 
495  /* Emit this periodically while benchmarking, to avoid the SIMD
496  * units turning on and off during long bench runs of non-SIMD
497  * functions */
498 #if ARCH_X86
499  checkasm_simd_warmup();
500 #endif
501 }
502 
504 {
505  CheckasmFuncVersion *const v = current.func_ver;
506  if (v && current.cycles) {
508 
509  /* Accumulate multiple bench_new() calls */
511 
512  /* Keep track of min/max/avg (log) variance */
513  current.var_sum += cycles.lvar;
514  current.var_max = fmax(current.var_max, cycles.lvar);
515  current.num_benched++;
516  }
517 
519  current.cycles = 0;
520 }
521 
522 /* Compares a string with a wildcard pattern. */
523 static int wildstrcmp(const char *str, const char *pattern)
524 {
525  const char *wild = strchr(pattern, '*');
526  if (wild) {
527  const size_t len = wild - pattern;
528  if (strncmp(str, pattern, len))
529  return 1;
530  while (*++wild == '*')
531  ;
532  if (!*wild)
533  return 0;
534  str += len;
535  while (*str && wildstrcmp(str, wild))
536  str++;
537  return !*str;
538  }
539  return strcmp(str, pattern);
540 }
541 
542 static void handle_interrupt(void);
543 static void update_statusline(void);
544 
545 static int test_enabled(const CheckasmTest *test)
546 {
548 }
549 
550 /* Perform tests and benchmarks for the specified
551  * cpu flag if supported by the host */
552 static void check_cpu_flag(const CheckasmCpuInfo *cpu)
553 {
554  const CheckasmCpu prev_cpu_flags = current.cpu_flags;
555  if (cpu) {
556  current.cpu_flags &= ~cpu->mask;
557  current.cpu_flags |= cpu->flag & cfg.cpu;
558  } else {
559  /* Also include any CPU flags not related to the CPU flags list */
560  current.cpu_flags = cfg.cpu;
561  for (const CheckasmCpuInfo *info = cfg.cpu_flags; info->flag; info++)
562  current.cpu_flags &= ~info->flag;
563  }
564 
565  if (cpu && current.cpu_flags == prev_cpu_flags)
566  return;
567 
568  current.func = NULL;
569  current.report_idx = 1;
570  current.cpu = cpu;
571  current.cpu_name_printed = 0;
572  current.cpu_suffix_length = (int) strlen(cpu_suffix(cpu)) + 1;
573  if (cfg.set_cpu_flags)
574  cfg.set_cpu_flags(current.cpu_flags);
575 
576  for (const CheckasmTest *test = cfg.tests; test->func; test++) {
577  if (!test_enabled(test))
578  continue;
579  current.test_name = test->name;
581 
583  const char *signal = checkasm_get_last_signal_desc();
585  checkasm_fail_func("%s", signal);
586 
587  /* We want to associate this (and any prior) failures with the
588  * correct report group, so remember the failure state until we
589  * reach the same position in the test() function again */
590  current.func_ver->state = CHECKASM_FUNC_CRASHED;
591  current.saved_checked = current.num_checked - current.prev_checked;
592  current.saved_failed = current.num_failed - current.prev_failed;
593  current.num_failed = current.prev_failed;
594  current.num_checked = current.prev_checked;
595  current.func = NULL;
596  }
597 
599  current.should_fail = 0; // reset between tests
600  test->func();
601  checkasm_report(NULL); // catch any un-reported functions
602 
603  if (cfg.bench && !state.skip_tests) {
604  /* Measure NOP and perf scale after each test+CPU flag configuration */
606  checkasm_measure_nop_cycles(&state.nop_cycles, state.target_cycles);
608  checkasm_measure_perf_scale(&state.perf_scale);
609  }
610 
611  free(current.func_variant);
612  current.func_variant = NULL;
613  }
614 }
615 
616 /* Print the name of the current CPU flag, but only do it once */
617 static void print_cpu_name(void)
618 {
619  if (!current.cpu_name_printed) {
620  LOG_COLOR(COLOR_YELLOW, "%s:\n", current.cpu ? current.cpu->name : "C");
621  current.cpu_name_printed = 1;
622  }
623 }
624 
625 int checkasm_run_on_all_cores(void (*func)(void))
626 {
627 #if HAVE_PTHREAD_SETAFFINITY_NP && defined(CPU_SET)
628  cpu_set_t mask;
629  if (pthread_getaffinity_np(pthread_self(), sizeof(mask), &mask))
630  return 1;
631 
632  int ret = 0;
633  for (int c = 0; c < CPU_SETSIZE; c++) {
634  if (CPU_ISSET(c, &mask)) {
635  cpu_set_t set;
636  CPU_ZERO(&set);
637  CPU_SET(c, &set);
638  if (pthread_setaffinity_np(pthread_self(), sizeof(set), &set)) {
639  ret = 1;
640  break;
641  }
642  func();
643  }
644  }
645  pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask);
646  return ret;
647 #else
648  return 1;
649 #endif
650 }
651 
652 static int set_cpu_affinity(const unsigned affinity)
653 {
654  int affinity_err;
655 
656 #ifdef _WIN32
657  HANDLE process = GetCurrentProcess();
658  #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
659  BOOL(WINAPI * spdcs)(HANDLE, const ULONG *, ULONG) = (void *) GetProcAddress(
660  GetModuleHandleW(L"kernel32.dll"), "SetProcessDefaultCpuSets");
661  if (spdcs)
662  affinity_err = !spdcs(process, (ULONG[]) { (ULONG) affinity + 256 }, 1);
663  else
664  #endif
665  {
666  if (affinity < sizeof(DWORD_PTR) * 8)
667  affinity_err = !SetProcessAffinityMask(process, (DWORD_PTR) 1 << affinity);
668  else
669  affinity_err = 1;
670  }
671 #elif HAVE_PTHREAD_SETAFFINITY_NP && defined(CPU_SET)
672  cpu_set_t set;
673  CPU_ZERO(&set);
674  CPU_SET(affinity, &set);
675  affinity_err = pthread_setaffinity_np(pthread_self(), sizeof(set), &set);
676 #else
677  (void) affinity;
678  (void) affinity_err;
679  LOG("checkasm: --affinity is not supported on your system\n");
680  return 1;
681 #endif
682 
683  if (affinity_err) {
684  LOG("checkasm: invalid cpu affinity (%u)\n", affinity);
685  return 1;
686  } else {
687  LOG("checkasm: running on cpu %u\n", affinity);
688  return 0;
689  }
690 }
691 
693 {
695 
696  for (const CheckasmCpuInfo *info = cfg->cpu_flags; info->flag; info++) {
697  if ((cfg->cpu & info->flag) == info->flag)
698  checkasm_fprintf(stdout, COLOR_GREEN, "%s", info->suffix);
699  else
700  checkasm_fprintf(stdout, COLOR_RED, "~%s", info->suffix);
701  printf(info[1].flag ? ", " : "\n");
702  }
703 }
704 
706 {
707  for (const CheckasmTest *test = config->tests; test->func; test++)
708  printf("%s\n", test->name);
709 }
710 
711 static void print_functions(const CheckasmFunc *const f)
712 {
713  if (f) {
714  print_functions(f->child[0]);
715  const CheckasmFuncVersion *v = &f->versions;
716  printf("%s (%s", f->name, ver_suffix(v));
717  while ((v = v->next))
718  printf(", %s", ver_suffix(v));
719  printf(")\n");
720  print_functions(f->child[1]);
721  }
722 }
723 
724 static void run_all_tests(void)
725 {
726  for (const CheckasmTest *test = cfg.tests; test->func; test++) {
727  if (test->init && test_enabled(test)) {
729  test->init();
730  }
731  }
732 
734  for (const CheckasmCpuInfo *info = cfg.cpu_flags; info->flag; info++)
736 
737  for (const CheckasmTest *test = cfg.tests; test->func; test++) {
738  if (test->uninit && test_enabled(test))
739  test->uninit();
740  }
741 }
742 
744 {
745  memset(&state, 0, sizeof(state));
746  memset(&current, 0, sizeof(current));
747  state.skip_tests = 1;
748  cfg = *config;
749 
750  run_all_tests();
751 
752  print_functions(current.tree.root);
754 }
755 
756 static void cpu_fprintf(void *priv, const char *fmt, ...)
757 {
758  FILE *f = priv;
759 
760  va_list ap;
761  va_start(ap, fmt);
762  fprintf(f, " - CPU: ");
763  vfprintf(f, fmt, ap);
764  fprintf(f, "\n");
765  va_end(ap);
766 }
767 
768 static COLD void print_info(void)
769 {
770  LOG_COLOR(COLOR_YELLOW, "checkasm:\n");
771  checkasm_cpu_info(cpu_fprintf, stderr, &cfg);
772 
773  if (cfg.bench) {
774  LOG(" - Timing source: %s\n", checkasm_perf.name);
775  if (cfg.verbose) {
779  LOG(" - Timing resolution: %.4f +/- %.3f ns/%s (%.0f +/- %.1f "
780  "MHz) (provisional)\n",
783 
784  LOG(" - No-op overhead: %.2f +/- %.3f %ss per call (provisional)\n",
787  }
788  LOG(" - Bench duration: %d µs per function (%" PRIu64 " %ss)\n", cfg.bench_usec,
789  state.target_cycles, checkasm_perf.unit);
790  }
791  LOG(" - Random seed: %u\n", cfg.seed);
792 }
793 
794 static void update_statusline(void)
795 {
796  if (state.skip_tests)
797  return;
798 
799  char status[256];
800  int len = 0;
801 
802  len += snprintf(status, sizeof(status), "checkasm: iter=%d/%d cpu=%s test=%s",
803  state.test_iter + 1, cfg.repeat, cpu_suffix(current.cpu),
804  current.test_name);
805 
806  if (current.func && current.func->report_name) {
807  snprintf(status + len, sizeof(status) - len, " func=%s",
808  current.func->report_name);
809  }
810 
812 }
813 
814 static int print_summary(int interrupted)
815 {
817 
818  LOG("checkasm: ");
819  if (interrupted)
820  LOG_COLOR(COLOR_BLUE, "(interrupted) ");
821 
822  /* Exclude C/ref versions from count reported to user */
823  const int num_checked_asm = current.num_checked - current.num_funcs;
824  if (current.num_failed) {
825  LOG_COLOR(COLOR_RED, "%d ", current.num_failed);
826  LOG("of %d tests failed", num_checked_asm);
827  } else if (num_checked_asm) {
828  if (!interrupted)
829  LOG("all ");
830  LOG("%d tests passed", num_checked_asm);
831  } else if (interrupted) {
832  LOG("no tests performed");
833  } else {
834  /* User did not define any tests */
835  LOG_COLOR(COLOR_YELLOW, "no tests to perform");
836  }
837 
838  if (cfg.repeat > 1)
839  LOG(", iteration %d of %d, seed %u\n", state.test_iter + 1, cfg.repeat, cfg.seed);
840  else
841  LOG("\n");
842 
843  if (current.num_benched && !current.num_failed)
845 
846  return current.num_failed > 0;
847 }
848 
849 static void handle_interrupt(void)
850 {
851  if (checkasm_interrupted) {
852  print_summary(1);
853  exit(128 + checkasm_interrupted);
854  }
855 }
856 
858 {
859 #if !HAVE_HTML_DATA
861  LOG("checkasm: built without HTML support\n");
862  return 1;
863  }
864 #endif
865 
866  memset(&state, 0, sizeof(state));
867  memset(&current, 0, sizeof(current));
868  cfg = *config;
869 
871 #if HAVE_PRCTL && defined(PR_SET_UNALIGN)
872  prctl(PR_SET_UNALIGN, PR_UNALIGN_SIGBUS);
873 #endif
874  if (cfg.cpu_affinity_set)
877 
878  if (!cfg.seed && !cfg.seed_set)
879  cfg.seed = checkasm_seed();
880  if (!cfg.repeat)
881  cfg.repeat = 1;
882  if (!cfg.bench_usec)
883  cfg.bench_usec = 1000;
884 
885  if (cfg.bench) {
886  if (checkasm_perf_init())
887  return 1;
888 
890  checkasm_measurement_init(&state.nop_cycles);
891  checkasm_measurement_init(&state.perf_scale);
892  checkasm_measure_perf_scale(&state.perf_scale);
893 
894  /* Use the low estimate to compute the number of target cycles, to
895  * ensure we reach the required number of cycles with confidence */
897  const double low_estimate = checkasm_sample(perf_scale, -1.0);
898  if (low_estimate <= 0.0) {
899  fprintf(stderr,
900  "checkasm: cycle counter seems to be non-functional "
901  "(invalid timer scale: %.4f %ss/nsec)\n",
903  return 1;
904  }
905 
906  state.target_cycles = (uint64_t) (1e3 * cfg.bench_usec / low_estimate);
907  checkasm_measure_nop_cycles(&state.nop_cycles, state.target_cycles);
908  }
909 
911 
912  print_info();
913 
914  for (state.test_iter = 0; state.test_iter < cfg.repeat; state.test_iter++) {
915  run_all_tests();
916 
917  int res = print_summary(0);
919  if (res)
920  return res;
921 
922  memset(&current, 0, sizeof(current));
923  cfg.seed++;
924  }
925 
926  return 0;
927 }
928 
929 /* Decide whether or not the specified function needs to be tested and
930  * allocate/initialize data structures if needed. Returns a pointer to a
931  * reference function if the function should be tested, otherwise NULL */
932 CheckasmKey checkasm_check_key(const CheckasmKey version, const char *const name, ...)
933 {
934  char name_buf[256];
935  va_list arg;
936 
938  goto skip;
939 
940  va_start(arg, name);
941  int name_length = vsnprintf(name_buf, sizeof(name_buf), name, arg);
942  va_end(arg);
943 
944  if (!version || name_length <= 0 || (size_t) name_length >= sizeof(name_buf)
946  goto skip;
947 
948  CheckasmFunc *const f = checkasm_func_get(&current.tree, name_buf);
949  CheckasmFuncVersion *v = &f->versions;
951 
952  if (v->key) {
953  CheckasmFuncVersion *prev;
954  do {
955  if (v->state == CHECKASM_FUNC_CRASHED) {
956  /* This function threw a signal last time; so restore the
957  * retained test state for the next report() call */
959  current.num_checked += current.saved_checked;
960  current.num_failed += current.saved_failed;
961  current.saved_checked = 0;
962  current.saved_failed = 0;
963  current.func = f;
964  current.func_ver = v;
965  for (CheckasmFunc *fp = f; fp; fp = fp->prev)
966  fp->report_idx = current.report_idx;
967  }
968 
969  /* Skip functions without a working reference */
970  if (!v->cpu && v->state != CHECKASM_FUNC_OK)
971  goto skip;
972 
973  /* Only test functions that haven't already been tested */
974  if (v->key == version)
975  goto skip;
976 
977  /* Exclude failed or variant functions from being used as ref */
978  if (v->state == CHECKASM_FUNC_OK && !v->suffix)
979  ref = v->key;
980 
981  prev = v;
982  } while ((v = v->next));
983 
984  v = prev->next = checkasm_mallocz(sizeof(CheckasmFuncVersion));
985  }
986 
987  if (current.func_variant) {
988  v->suffix = current.func_variant;
989  current.func_variant = NULL;
990  name_length += (int) strlen(v->suffix) + 1;
991  } else {
992  name_length += current.cpu_suffix_length;
993  }
994 
995  if (name_length > state.max_function_name_length)
996  state.max_function_name_length = name_length;
997 
998  v->key = version;
999  v->state = CHECKASM_FUNC_OK;
1000  v->cpu = current.cpu;
1001  if (ref == version)
1002  current.num_funcs++;
1003 
1004  if (state.skip_tests)
1005  goto skip;
1006 
1007  /* Associate this function with each other function that was last used
1008  * as part of the same report group */
1009  if (f->report_idx < current.report_idx) {
1010  f->report_idx = current.report_idx;
1011  f->prev = current.func;
1012  f->test_name = current.test_name;
1013  }
1014 
1015  current.func = f;
1016  current.func_ver = v;
1017  current.num_checked++;
1020 
1021  if (cfg.bench) {
1022 #if ARCH_X86
1023  checkasm_simd_warmup();
1024 #endif
1026  }
1027 
1028  return ref;
1029 
1030 skip:
1031  free(current.func_variant);
1032  current.func_variant = NULL;
1033  return 0;
1034 }
1035 
1036 void checkasm_set_func_variant(const char *id_fmt, ...)
1037 {
1038  va_list arg;
1039  va_start(arg, id_fmt);
1040  assert(!current.func_variant);
1041  current.func_variant = checkasm_vasprintf(id_fmt, arg);
1042  va_end(arg);
1043 }
1044 
1045 /* Indicate that the current test has failed, return whether verbose printing
1046  * is requested. */
1047 static int fail_internal(const char *const msg, va_list arg)
1048 {
1049  CheckasmFuncVersion *const v = current.func_ver;
1050  if (v && v->state == CHECKASM_FUNC_OK) {
1051  if (!current.should_fail) {
1052  print_cpu_name();
1053  LOG_COLOR(COLOR_RED, "FAILURE:");
1054  LOG(" %s_%s (", current.func->name, ver_suffix(v));
1055  vfprintf(stderr, msg, arg);
1056  fputs(")\n", stderr);
1057  }
1058 
1060  current.num_failed++;
1061  }
1062  return cfg.verbose && !current.should_fail;
1063 }
1064 
1065 int checkasm_fail_func(const char *const msg, ...)
1066 {
1067  va_list arg;
1068  va_start(arg, msg);
1069  const int ret = fail_internal(msg, arg);
1070  va_end(arg);
1071  return ret;
1072 }
1073 
1074 void checkasm_fail_abort(const char *const msg, ...)
1075 {
1076  va_list arg;
1077  va_start(arg, msg);
1078  fail_internal(msg, arg);
1079  va_end(arg);
1080 
1082  abort(); // in case we don't have a longjmp implementation
1083 }
1084 
1086 {
1087  current.should_fail = !!(current.cpu_flags & cpu_flags);
1088 
1089 #if CHECKASM_HAVE_LONGJMP
1090  return 1; /* we can catch any crashes */
1091 #else
1092  /* If our signal handler isn't working, we shouldn't run tests that
1093  * are expected to fail, as they may rely on the signal handler. */
1094  return !current.should_fail;
1095 #endif
1096 }
1097 
1098 /* Print the outcome of all tests performed since
1099  * the last time this function was called */
1100 void checkasm_report(const char *const name, ...)
1101 {
1102  char report_name[256];
1103 
1104  /* Calculate the amount of padding required to make the output vertically aligned */
1105  int length = (int) strlen(current.test_name);
1106  if (name) {
1107  va_list arg;
1108  va_start(arg, name);
1109  length += 1 + vsnprintf(report_name, sizeof(report_name), name, arg);
1110  va_end(arg);
1111  }
1112 
1113  if (length > state.max_report_name_length)
1114  state.max_report_name_length = length;
1115 
1116  const int new_checked = current.num_checked - current.prev_checked;
1117  if (new_checked) {
1118  int pad_length = (int) state.max_report_name_length + 3; // strlen(" - ")
1119  assert(!state.skip_tests);
1120 
1121  int fails = current.num_failed - current.prev_failed;
1122  if (current.should_fail)
1123  current.num_failed = current.prev_failed + (new_checked - fails);
1124 
1125  /* Omit 'OK' after the first run, unless failed or verbose */
1126  int want_print = current.num_failed != current.prev_failed || cfg.verbose;
1127  if (state.test_iter == 0)
1128  want_print |= current.should_fail || current.cpu;
1129 
1130  if (want_print) {
1131  print_cpu_name();
1132  if (name) {
1133  pad_length -= LOG(" - %s.%s", current.test_name, report_name);
1134  } else {
1135  pad_length -= LOG(" - %s", current.test_name);
1136  }
1137  LOG("%*c", imax(pad_length, 0) + 2, '[');
1138 
1139  if (current.num_failed == current.prev_failed)
1140  LOG_COLOR(COLOR_GREEN, current.should_fail ? "EXPECTED" : "OK");
1141  else if (!current.should_fail)
1142  LOG_COLOR(COLOR_RED, "FAILED");
1143  else
1144  LOG_COLOR(COLOR_RED, "%d/%d EXPECTED", fails, new_checked);
1145  LOG("]\n");
1146  }
1147 
1148  current.prev_checked = current.num_checked;
1149  current.prev_failed = current.num_failed;
1150  }
1151 
1152  /* Store the report name with each function in this report group */
1153  CheckasmFunc *func = current.func;
1154  while (func) {
1155  if (name && !func->report_name)
1156  func->report_name = checkasm_strdup(report_name);
1157  func = func->prev;
1158  }
1159 
1160  current.func = NULL; /* reset current function for new report */
1161  current.report_idx++;
1162  handle_interrupt();
1163 }
1164 
1165 static void print_usage(const char *const progname)
1166 {
1167  fprintf(stderr,
1168  "Usage: %s [options...] <random seed>\n"
1169  " <random seed> Use fixed value to seed the PRNG\n"
1170  "Options:\n"
1171  " --affinity=<cpu> Run the process on CPU <cpu>\n"
1172  " --bench -b Benchmark the tested functions\n"
1173  " --csv, --tsv, --json, Choose output format for benchmarks\n"
1174  " --html\n"
1175  " --function=<pattern> -f Test only the functions matching "
1176  "<pattern>\n"
1177  " --help -h Print this usage info\n"
1178  " --list-cpu-flags List available cpu flags\n"
1179  " --list-functions List available functions\n"
1180  " --list-tests List available tests\n"
1181  " --duration=<μs> Benchmark duration (per function) in "
1182  "μs\n"
1183  " --repeat[=<N>] Repeat tests N times, on successive seeds\n"
1184  " --test=<pattern> -t Test only <pattern>\n"
1185  " --verbose -v Print verbose timing info and failure "
1186  "data\n",
1187  progname);
1188 }
1189 
1190 static int parseu(unsigned *const dst, const char *const str, const int base)
1191 {
1192  unsigned long val;
1193  char *end;
1194  errno = 0;
1195  val = strtoul(str, &end, base);
1196  if (errno || end == str || *end)
1197  return 0;
1198 #if !defined(__SIZEOF_LONG__) || !defined(__SIZEOF_INT__) || __SIZEOF_LONG__ > __SIZEOF_INT__
1199  /* This condition is split out; it can cause -Wtype-limits warnings on
1200  * 32 bit platforms and on Windows:
1201  * warning: result of comparison 'unsigned long' > 4294967295 is always false [-Wtautological-type-limit-compare] */
1202  if (val > (unsigned) -1)
1203  return 0;
1204 #endif
1205  *dst = (unsigned) val;
1206  return 1;
1207 }
1208 
1209 int checkasm_main(CheckasmConfig *config, int argc, const char *argv[])
1210 {
1211  while (argc > 1) {
1212  if (!strncmp(argv[1], "--help", 6) || !strcmp(argv[1], "-h")) {
1213  print_usage(argv[0]);
1214  return 0;
1215  } else if (!strcmp(argv[1], "--list-cpu-flags")
1216  || !strcmp(argv[1], "--list-cpuflags")) {
1218  return 0;
1219  } else if (!strcmp(argv[1], "--list-tests")) {
1221  return 0;
1222  } else if (!strcmp(argv[1], "--list-functions")) {
1224  return 0;
1225  } else if (!strcmp(argv[1], "--bench") || !strcmp(argv[1], "-b")) {
1226  config->bench = 1;
1227  } else if (!strncmp(argv[1], "--bench=", 8)) {
1228  config->bench = 1;
1229  config->function_pattern = argv[1] + 8;
1230  } else if (!strcmp(argv[1], "--csv")) {
1231  config->format = CHECKASM_FORMAT_CSV;
1232  } else if (!strcmp(argv[1], "--tsv")) {
1233  config->format = CHECKASM_FORMAT_TSV;
1234  } else if (!strcmp(argv[1], "--json")) {
1235  config->format = CHECKASM_FORMAT_JSON;
1236  } else if (!strcmp(argv[1], "--html")) {
1237 #if HAVE_HTML_DATA
1238  config->format = CHECKASM_FORMAT_HTML;
1239 #else
1240  LOG("checkasm: built without HTML support\n");
1241  return 1;
1242 #endif
1243  } else if (!strncmp(argv[1], "--duration=", 11)) {
1244  const char *const s = argv[1] + 11;
1245  if (!parseu(&config->bench_usec, s, 10)) {
1246  LOG("checkasm: invalid duration (%s)\n", s);
1247  print_usage(argv[0]);
1248  return 1;
1249  }
1250  } else if (!strncmp(argv[1], "--test=", 7)) {
1251  config->test_pattern = argv[1] + 7;
1252  } else if (!strcmp(argv[1], "-t")) {
1253  config->test_pattern = argc > 1 ? argv[2] : "";
1254  argc--;
1255  argv++;
1256  } else if (!strncmp(argv[1], "--function=", 11)) {
1257  config->function_pattern = argv[1] + 11;
1258  } else if (!strcmp(argv[1], "-f")) {
1259  config->function_pattern = argc > 1 ? argv[2] : "";
1260  argc--;
1261  argv++;
1262  } else if (!strcmp(argv[1], "--verbose") || !strcmp(argv[1], "-v")) {
1263  config->verbose = 1;
1264  } else if (!strncmp(argv[1], "--affinity=", 11)) {
1265  const char *const s = argv[1] + 11;
1266  config->cpu_affinity_set = 1;
1267  if (!parseu(&config->cpu_affinity, s, 16)) {
1268  LOG("checkasm: invalid cpu affinity (%s)\n", s);
1269  print_usage(argv[0]);
1270  return 1;
1271  }
1272  } else if (!strncmp(argv[1], "--repeat=", 9)) {
1273  const char *const s = argv[1] + 9;
1274  if (!parseu(&config->repeat, s, 10)) {
1275  LOG("checkasm: invalid number of repetitions (%s)\n", s);
1276  print_usage(argv[0]);
1277  return 1;
1278  }
1279  } else if (!strcmp(argv[1], "--repeat")) {
1280  config->repeat = UINT_MAX;
1281  } else {
1282  config->seed_set = 1;
1283  if (!parseu(&config->seed, argv[1], 10)) {
1284  LOG("checkasm: unknown option (%s)\n", argv[1]);
1285  print_usage(argv[0]);
1286  return 1;
1287  }
1288  }
1289 
1290  argc--;
1291  argv++;
1292  }
1293 
1294  return checkasm_run(config);
1295 }
func_variant
char * func_variant
Definition: checkasm.c:93
COLD
#define COLD
Definition: internal.h:45
CheckasmStats::next_count
int next_count
Definition: stats.h:91
num_failed
int num_failed
Definition: checkasm.c:99
process
static void process(NormalizeContext *s, AVFrame *in, AVFrame *out)
Definition: vf_normalize.c:155
checkasm_interrupted
volatile sig_atomic_t checkasm_interrupted
Definition: signal.c:50
name
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default minimum maximum flags name is the option name
Definition: writing_filters.txt:88
printf
__device__ int printf(const char *,...)
CheckasmConfig::cpu_affinity
unsigned cpu_affinity
CPU core ID for process pinning.
Definition: checkasm.h:312
flag
int flag
Definition: cpu.c:40
checkasm_cpu_info
COLD void checkasm_cpu_info(void(*info_cb)(void *priv, const char *fmt,...), void *priv, const CheckasmConfig *config)
Definition: cpu.c:83
CheckasmCpuInfo::mask
CheckasmCpu mask
Bitmask of prior CPU flags to disable again.
Definition: checkasm.h:118
checkasm_config.h
ver_suffix
static const char * ver_suffix(const CheckasmFuncVersion *ver)
Definition: checkasm.c:138
color
Definition: vf_paletteuse.c:513
CheckasmJson::level
int level
Definition: internal.h:135
test_name
const char * test_name
Definition: checkasm.c:86
CheckasmStats::samples
CheckasmSample samples[CHECKASM_STATS_SAMPLES]
Definition: stats.h:89
LOG_COLOR
#define LOG_COLOR(...)
Definition: internal.h:125
CheckasmFunc
Definition: function.h:50
should_fail
int should_fail
Definition: checkasm.c:87
LOG
#define LOG(...)
Definition: internal.h:126
test_iter
unsigned test_iter
Definition: checkasm.c:111
checkasm_context
checkasm_jmp_buf checkasm_context
Definition: signal.c:46
checkasm_js
static const char checkasm_js[]
Definition: html_data.h:63
var_max
double var_max
Definition: checkasm.c:103
IterState::report
const char * report
Definition: checkasm.c:231
checkasm_check_key
CheckasmKey checkasm_check_key(const CheckasmKey version, const char *const name,...)
Definition: checkasm.c:932
mask
int mask
Definition: mediacodecdec_common.c:154
checkasm_perf_init
int checkasm_perf_init(void)
Definition: perf.c:59
CheckasmFuncVersion::suffix
char * suffix
Definition: function.h:44
checkasm_save_context
#define checkasm_save_context(ctx)
Definition: longjmp.h:67
CHECKASM_VERSION
#define CHECKASM_VERSION
Definition: checkasm_config.h:89
CheckasmMeasurement::stats
CheckasmStats stats
Definition: stats.h:125
CheckasmConfig::bench
int bench
Enable benchmarking.
Definition: checkasm.h:247
test
Definition: idctdsp.c:35
saved_checked
int saved_checked
Definition: checkasm.c:102
checkasm_measure_nop_cycles
void checkasm_measure_nop_cycles(CheckasmMeasurement *meas, uint64_t target_cycles)
Definition: perf.c:176
CheckasmFuncVersion::key
CheckasmKey key
Definition: function.h:45
base
uint8_t base
Definition: vp3data.h:128
checkasm_list_cpu_flags
void checkasm_list_cpu_flags(const CheckasmConfig *cfg)
Print available CPU flags to stdout.
Definition: checkasm.c:692
cpu_info_json
static void cpu_info_json(void *priv, const char *fmt,...)
Definition: checkasm.c:202
checkasm_list_tests
void checkasm_list_tests(const CheckasmConfig *config)
Print available tests.
Definition: checkasm.c:705
CheckasmConfig::test_pattern
const char * test_pattern
Pattern for filtering which tests to run.
Definition: checkasm.h:230
CheckasmMeasurement::nb_measurements
int nb_measurements
Definition: stats.h:124
key
const char * key
Definition: ffmpeg_mux_init.c:2985
CheckasmJson::file
FILE * file
Definition: internal.h:134
cpu_suffix_length
int cpu_suffix_length
Definition: checkasm.c:85
tf_sess_config.config
config
Definition: tf_sess_config.py:33
fail_internal
static int fail_internal(const char *const msg, va_list arg)
Definition: checkasm.c:1047
cpu_mask_json
static void cpu_mask_json(CheckasmJson *json, const CheckasmConfig cfg, const CheckasmCpu cpu_mask)
Definition: checkasm.c:214
test_enabled
static int test_enabled(const CheckasmTest *test)
Definition: checkasm.c:545
CHECKASM_STATS_SAMPLES
#define CHECKASM_STATS_SAMPLES
Definition: stats.h:88
report_idx
int report_idx
Definition: checkasm.c:88
checkasm_chart_js
static const char checkasm_chart_js[]
Definition: html_data.h:62
checkasm_set_func_variant
void checkasm_set_func_variant(const char *id_fmt,...)
Definition: checkasm.c:1036
checkasm_perf
CheckasmPerf checkasm_perf
Definition: perf.c:52
handle_interrupt
static void handle_interrupt(void)
Definition: checkasm.c:849
CheckasmFormat
CheckasmFormat
Output format for benchmark results.
Definition: checkasm.h:153
IterState::json
CheckasmJson json
Definition: checkasm.c:232
CHECKASM_FORMAT_JSON
@ CHECKASM_FORMAT_JSON
JSON structured output with all measurement data.
Definition: checkasm.h:157
checkasm_load_context
#define checkasm_load_context(ctx)
Definition: longjmp.h:68
print_bench_iter
static void print_bench_iter(const CheckasmFunc *const f, struct IterState *const iter)
Definition: checkasm.c:367
IterState
Definition: checkasm.c:229
checkasm_statusline
void checkasm_statusline(const char *status)
Definition: utils.c:501
checkasm_run_on_all_cores
int checkasm_run_on_all_cores(void(*func)(void))
Definition: checkasm.c:625
CheckasmConfig::cpu_flags
const CheckasmCpuInfo * cpu_flags
List of CPU flags understood by the implementation.
Definition: checkasm.h:192
checkasm_func_tree_uninit
void checkasm_func_tree_uninit(CheckasmFuncTree *tree)
Definition: function.c:59
CheckasmConfig::tests
const CheckasmTest * tests
Array of test functions to execute.
Definition: checkasm.h:200
imax
static int imax(const int a, const int b)
Definition: internal.h:177
CheckasmPerf::name
const char * name
Name of the timing mechanism (e.g., "clock_gettime")
Definition: test.h:544
val
static double val(void *priv, double ch)
Definition: aeval.c:77
max_function_name_length
int max_function_name_length
Definition: checkasm.c:109
check_cpu_flag
static void check_cpu_flag(const CheckasmCpuInfo *cpu)
Definition: checkasm.c:552
CheckasmCpuInfo
Describes a CPU feature flag/capability.
Definition: checkasm.h:105
num_funcs
int num_funcs
Definition: checkasm.c:97
checkasm_measurement_result
static CheckasmVar checkasm_measurement_result(const CheckasmMeasurement measurement)
Definition: stats.h:147
checkasm_mode
static double checkasm_mode(const CheckasmVar x)
Definition: stats.h:50
state
static struct @604 state
CheckasmConfig::function_pattern
const char * function_pattern
Pattern for filtering which functions within tests to run.
Definition: checkasm.h:239
target_cycles
uint64_t target_cycles
Definition: checkasm.c:118
checkasm_should_fail
int checkasm_should_fail(CheckasmCpu cpu_flags)
Mark a block of tests as expected to fail.
Definition: checkasm.c:1085
CheckasmFuncVersion
Definition: function.h:41
FALLTHROUGH
#define FALLTHROUGH
Definition: internal.h:92
num_benched
int num_benched
Definition: checkasm.c:100
checkasm_json
void checkasm_json(CheckasmJson *json, const char *key, const char *fmt,...) CHECKASM_PRINTF(3
set
static void set(uint8_t *a[], int ch, int index, int ch_count, enum AVSampleFormat f, double v)
Definition: swresample.c:57
checkasm_mallocz
static void * checkasm_mallocz(const size_t size)
Definition: internal.h:197
parseu
static int parseu(unsigned *const dst, const char *const str, const int base)
Definition: checkasm.c:1190
checkasm_list_functions
void checkasm_list_functions(const CheckasmConfig *config)
Print available functions within tests.
Definition: checkasm.c:743
COLOR_YELLOW
#define COLOR_YELLOW
Definition: internal.h:104
cpu_name_printed
int cpu_name_printed
Definition: checkasm.c:83
checkasm_set_signal_handlers
void checkasm_set_signal_handlers(void)
Definition: signal.c:132
checkasm_bench_update
void checkasm_bench_update(const int iterations, const uint64_t cycles)
Update benchmark statistics with timing results.
Definition: checkasm.c:489
CheckasmFuncTree
Definition: function.h:61
info
MIPS optimizations info
Definition: mips.txt:2
checkasm_mean
static double checkasm_mean(const CheckasmVar x)
Definition: stats.h:55
limits.h
CheckasmJson
Definition: internal.h:133
checkasm_var_const
static CheckasmVar checkasm_var_const(double x)
Definition: stats.h:65
CheckasmFunc::prev
struct CheckasmFunc * prev
Definition: function.h:52
stats.h
arg
const char * arg
Definition: jacosubdec.c:65
CheckasmConfig::seed
unsigned seed
Random number generator seed.
Definition: checkasm.h:286
set_cpu_affinity
static int set_cpu_affinity(const unsigned affinity)
Definition: checkasm.c:652
print_bench_footer
static void print_bench_footer(struct IterState *const iter)
Definition: checkasm.c:331
result
and forward the result(frame or status change) to the corresponding input. If nothing is possible
NULL
#define NULL
Definition: coverity.c:32
CheckasmConfig::bench_usec
unsigned bench_usec
Target benchmark duration in microseconds.
Definition: checkasm.h:258
cycles
uint64_t cycles
Definition: checkasm.c:94
run_all_tests
static void run_all_tests(void)
Definition: checkasm.c:724
format
New swscale design to change SwsGraph is what coordinates multiple passes These can include cascaded scaling error diffusion and so on Or we could have separate passes for the vertical and horizontal scaling In between each SwsPass lies a fully allocated image buffer Graph passes may have different levels of e g we can have a single threaded error diffusion pass following a multi threaded scaling pass SwsGraph is internally recreated whenever the image format
Definition: swscale-v2.txt:14
prev_checked
int prev_checked
Definition: checkasm.c:101
CheckasmTest
Describes a single test function.
Definition: checkasm.h:127
current
static struct @603 current
checkasm_measure_perf_scale
void checkasm_measure_perf_scale(CheckasmMeasurement *meas)
Definition: perf.c:209
CHECKASM_FORMAT_PRETTY
@ CHECKASM_FORMAT_PRETTY
Pretty-printed (colored) text output (default)
Definition: checkasm.h:154
exp
int8_t exp
Definition: eval.c:76
test::name
const char * name
Definition: idctdsp.c:36
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
CHECKASM_FORMAT_TSV
@ CHECKASM_FORMAT_TSV
Tab-separated values with optional header.
Definition: checkasm.h:156
checkasm_var_mul
CheckasmVar checkasm_var_mul(CheckasmVar a, CheckasmVar b)
Definition: stats.c:78
print_benchmarks
static void print_benchmarks(void)
Definition: checkasm.c:456
CHECKASM_FUNC_OK
@ CHECKASM_FUNC_OK
Definition: function.h:36
cfg
static CheckasmConfig cfg
Definition: checkasm.c:74
CheckasmCpuInfo::suffix
const char * suffix
Short suffix for function names (e.g., "sse2", "avx2")
Definition: checkasm.h:107
func_ver
CheckasmFuncVersion * func_ver
Definition: checkasm.c:92
CHECKASM_FUNC_FAILED
@ CHECKASM_FUNC_FAILED
Definition: function.h:37
f
f
Definition: af_crystalizer.c:122
func
CheckasmFunc * func
Definition: checkasm.c:91
checkasm_stats_estimate
CheckasmVar checkasm_stats_estimate(const CheckasmStats *const stats)
Definition: stats.c:102
checkasm_html_body
static const char checkasm_html_body[]
Definition: html_data.h:65
checkasm_measurement_init
static void checkasm_measurement_init(CheckasmMeasurement *measurement)
Definition: stats.h:128
CheckasmFunc::report_name
char * report_name
Definition: function.h:55
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:87
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
checkasm_strdup
static char * checkasm_strdup(const char *str)
Definition: internal.h:202
COLOR_GREEN
#define COLOR_GREEN
Definition: internal.h:103
CheckasmConfig
Configuration structure for the checkasm test suite.
Definition: checkasm.h:182
test.h
Test writing API for checkasm.
checkasm_init_cpu
COLD void checkasm_init_cpu(void)
Definition: cpu.c:42
update_statusline
static void update_statusline(void)
Definition: checkasm.c:794
relative_error
static double relative_error(double lvar)
Definition: checkasm.c:144
cpu.h
CheckasmFuncVersion::cpu
const CheckasmCpuInfo * cpu
Definition: function.h:43
stats
static CheckasmStats stats
Definition: checkasm.c:75
checkasm_fail_abort
void checkasm_fail_abort(const char *const msg,...)
Definition: checkasm.c:1074
CHECKASM_FORMAT_CSV
@ CHECKASM_FORMAT_CSV
Comma-separated values with optional header.
Definition: checkasm.h:155
CHECKASM_FORMAT_HTML
@ CHECKASM_FORMAT_HTML
Interactive HTML report for web viewing.
Definition: checkasm.h:158
checkasm_get_cpu_flags
CheckasmCpu checkasm_get_cpu_flags(void)
Get the current active set of CPU flags.
Definition: checkasm.c:122
cpu_flags
CheckasmCpu cpu_flags
Definition: checkasm.c:84
print_functions
static void print_functions(const CheckasmFunc *const f)
Definition: checkasm.c:711
checkasm_var_sub
CheckasmVar checkasm_var_sub(CheckasmVar a, CheckasmVar b)
Definition: stats.c:64
version
version
Definition: libkvazaar.c:313
checkasm_setup_fprintf
void checkasm_setup_fprintf(void)
Definition: utils.c:544
cpu_fprintf
static void cpu_fprintf(void *priv, const char *fmt,...)
Definition: checkasm.c:756
checkasm_bench_func
int checkasm_bench_func(void)
Check if current function should be benchmarked.
Definition: checkasm.c:466
print_cpu_name
static void print_cpu_name(void)
Definition: checkasm.c:617
checkasm_var_div
CheckasmVar checkasm_var_div(CheckasmVar a, CheckasmVar b)
Definition: stats.c:94
perf_scale
CheckasmMeasurement perf_scale
Definition: checkasm.c:115
checkasm_seed
unsigned checkasm_seed(void)
Definition: utils.c:117
wildstrcmp
static int wildstrcmp(const char *str, const char *pattern)
Definition: checkasm.c:523
prev_failed
int prev_failed
Definition: checkasm.c:101
function.h
COLOR_DEFAULT
#define COLOR_DEFAULT
Definition: internal.h:101
checkasm_run
int checkasm_run(const CheckasmConfig *config)
Run all tests and benchmarks matching the specified patterns.
Definition: checkasm.c:857
available
if no frame is available
Definition: filter_design.txt:166
CheckasmMeasurement
Definition: stats.h:122
print_summary
static int print_summary(int interrupted)
Definition: checkasm.c:814
CheckasmFuncVersion::next
struct CheckasmFuncVersion * next
Definition: function.h:42
vsnprintf
#define vsnprintf
Definition: snprintf.h:36
json_measurement
static void json_measurement(CheckasmJson *json, const char *key, const char *unit, const CheckasmMeasurement measurement)
Definition: checkasm.c:177
max_report_name_length
int max_report_name_length
Definition: checkasm.c:110
s
uint8_t s
Definition: llvidencdsp.c:39
CheckasmCpuInfo::flag
CheckasmCpu flag
Bitmask flag value for this CPU feature.
Definition: checkasm.h:108
cpu
const CheckasmCpuInfo * cpu
Definition: checkasm.c:82
checkasm_srand
void checkasm_srand(unsigned seed)
Definition: utils.c:229
checkasm_median
static double checkasm_median(const CheckasmVar x)
Definition: stats.h:45
checkasm_json_pop
void checkasm_json_pop(CheckasmJson *json, char type)
Definition: utils.c:627
print_info
static COLD void print_info(void)
Definition: checkasm.c:768
checkasm_stats_count_grow
static void checkasm_stats_count_grow(CheckasmStats *const stats, uint64_t cycles, uint64_t target_cycles)
Definition: stats.h:108
len
int len
Definition: vorbis_enc_data.h:426
CheckasmPerf::unit
const char * unit
Unit of measurement (e.g., "ns", "cycles")
Definition: test.h:547
checkasm_main
int checkasm_main(CheckasmConfig *config, int argc, const char *argv[])
Main entry point for checkasm test programs.
Definition: checkasm.c:1209
checkasm_stddev
static double checkasm_stddev(const CheckasmVar x)
Definition: stats.h:60
checkasm_json_str
void void checkasm_json_str(CheckasmJson *json, const char *key, const char *str)
Definition: utils.c:586
CheckasmConfig::cpu_affinity_set
int cpu_affinity_set
Enable process pinning via cpu_affinity.
Definition: checkasm.h:305
checkasm_css
static const char checkasm_css[]
Definition: html_data.h:64
html_data.h
CHECKASM_FUNC_CRASHED
@ CHECKASM_FUNC_CRASHED
Definition: function.h:38
ret
ret
Definition: filter_design.txt:187
CheckasmVar::lmean
double lmean
Definition: stats.h:36
checkasm_get_cpu_info
const CheckasmCpuInfo * checkasm_get_cpu_info(void)
Get the CPU flag currently being tested.
Definition: checkasm.c:127
checkasm_bench_runs
int checkasm_bench_runs(void)
Get number of iterations for current benchmark run.
Definition: checkasm.c:471
checkasm_json_push
void checkasm_json_push(CheckasmJson *json, const char *const key, char type)
Definition: utils.c:611
CheckasmKey
uintptr_t CheckasmKey
Opaque type used to identify function implementations.
Definition: checkasm.h:96
fmax
double fmax(double, double)
checkasm_get_last_signal_desc
const char * checkasm_get_last_signal_desc(void)
Definition: signal.c:167
CheckasmConfig::seed_set
int seed_set
Enable using the seed value.
Definition: checkasm.h:277
CheckasmConfig::repeat
unsigned repeat
Number of times to repeat tests.
Definition: checkasm.h:295
separator
static char separator(CheckasmFormat format)
Definition: checkasm.c:149
cpu_suffix
static const char * cpu_suffix(const CheckasmCpuInfo *cpu)
Definition: checkasm.c:133
status
ov_status_e status
Definition: dnn_backend_openvino.c:99
CheckasmVar
Definition: stats.h:35
saved_failed
int saved_failed
Definition: checkasm.c:102
tree
CheckasmFuncTree tree
Definition: checkasm.c:79
CheckasmSample
Definition: stats.h:80
checkasm_stats_reset
static void checkasm_stats_reset(CheckasmStats *const stats)
Definition: stats.h:94
ref
static int ref[MAX_W *MAX_W]
Definition: jpeg2000dwt.c:117
L
#define L(x)
Definition: vpx_arith.h:36
internal.h
CheckasmStats::nb_samples
int nb_samples
Definition: stats.h:90
print_bench_header
static void print_bench_header(struct IterState *const iter)
Definition: checkasm.c:235
CheckasmStats
Definition: stats.h:85
checkasm_fprintf
int static int checkasm_fprintf(FILE *const f, int color, const char *fmt,...)
Definition: internal.h:116
checkasm_report
void checkasm_report(const char *const name,...)
Definition: checkasm.c:1100
checkasm_vasprintf
char * checkasm_vasprintf(const char *fmt, va_list arg)
Definition: utils.c:853
checkasm_bench_finish
void checkasm_bench_finish(void)
Finalize and store benchmark results.
Definition: checkasm.c:503
COLOR_BLUE
#define COLOR_BLUE
Definition: internal.h:105
CheckasmConfig::cpu
CheckasmCpu cpu
Detected CPU flags for the current system.
Definition: checkasm.h:210
num_checked
int num_checked
Definition: checkasm.c:98
IterState::test
const char * test
Definition: checkasm.c:230
checkasm_measurement_update
static void checkasm_measurement_update(CheckasmMeasurement *measurement, const CheckasmStats stats)
Definition: stats.h:135
CheckasmVar::lvar
double lvar
Definition: stats.h:36
checkasm_func_get
CheckasmFunc * checkasm_func_get(CheckasmFuncTree *tree, const char *const name)
Definition: function.c:141
checkasm_sample
static double checkasm_sample(const CheckasmVar x, const double q)
Definition: stats.h:40
CheckasmConfig::format
CheckasmFormat format
Output format for benchmark results.
Definition: checkasm.h:261
checkasm_fail_func
int checkasm_fail_func(const char *const msg,...)
Definition: checkasm.c:1065
json_var
static void json_var(CheckasmJson *json, const char *key, const char *unit, const CheckasmVar var)
Definition: checkasm.c:158
CheckasmCpu
uint64_t CheckasmCpu
Opaque type representing a set of CPU feature flags.
Definition: checkasm.h:88
skip_tests
int skip_tests
Definition: checkasm.c:119
print_usage
static void print_usage(const char *const progname)
Definition: checkasm.c:1165
CheckasmFuncVersion::cycles
CheckasmMeasurement cycles
Definition: function.h:46
checkasm_stats_add
static void checkasm_stats_add(CheckasmStats *const stats, const CheckasmSample s)
Definition: stats.h:100
CheckasmConfig::set_cpu_flags
void(* set_cpu_flags)(CheckasmCpu new_flags)
Callback invoked when active CPU flags change.
Definition: checkasm.h:222
COLOR_RED
#define COLOR_RED
Definition: internal.h:102
snprintf
#define snprintf
Definition: snprintf.h:34
CheckasmConfig::verbose
int verbose
Enable verbose output.
Definition: checkasm.h:270
skip
static void BS_FUNC() skip(BSCTX *bc, unsigned int n)
Skip n bits in the buffer.
Definition: bitstream_template.h:383
nop_cycles
CheckasmMeasurement nop_cycles
Definition: checkasm.c:114
var_sum
double var_sum
Definition: checkasm.c:103
CheckasmFuncVersion::state
CheckasmFuncState state
Definition: function.h:47