32#if HAVE_PTHREAD_SETAFFINITY_NP
48#include "checkasm/checkasm.h"
57 #if HAVE_PTHREAD_SETAFFINITY_NP
60 #include <pthread_np.h>
70 #include <sys/prctl.h>
135 return cpu ?
cpu->suffix :
"c";
146 return sqrt(
exp(lvar) - 1.0);
187 json_var(json,
"regressionSlope", unit,
222 if ((cpu_mask & info->flag) != info->flag)
242 switch (
cfg.format) {
247 printf(
"name%csuffix%c%ss%cstddev%cnanoseconds\n", sep, sep,
254 printf(
"<!doctype html>\n"
257 " <meta charset=\"utf-8\"/>\n"
258 " <title>checkasm report</title>\n"
259 " <script type=\"module\">\n"
266 " <script type=\"application/json\" id=\"report-data\">\n",
277 if (
cfg.test_pattern)
279 if (
cfg.function_pattern)
284 if (
cfg.cpu_affinity_set)
292 const int available = (
cfg.cpu & info->flag) == info->flag;
295 checkasm_json(json,
"available", available ?
"true" :
"false");
304 char perf_scale_unit[32];
318 "time (nanoseconds)");
322 printf(
" nop:%*.1f +/- %-7.1f %11.1f ns +/- %-6.1f\n",
337 switch (
cfg.format) {
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);
356 " <meta name=\"viewport\" content=\"width=device-width, "
357 "initial-scale=1\">\n"
383 int json_func_pushed = 0;
396 switch (
cfg.format) {
399 if (!json_func_pushed) {
405 json_func_pushed = 1;
410 json_var(json,
"rawTime",
"nsec", raw_time);
412 json_var(json,
"adjustedTime",
"nsec", time);
413 if (v !=
ref &&
ref->cycles.nb_measurements)
424 const int pad = 12 +
state.max_function_name_length
431 if (v !=
ref &&
ref->cycles.nb_measurements) {
446 }
while ((v = v->
next));
448 if (json_func_pushed) {
458 struct IterState iter = { .json.file = stdout };
483 return stats.next_count;
499 checkasm_simd_warmup();
525 const char *wild = strchr(pattern,
'*');
527 const size_t len = wild - pattern;
528 if (strncmp(str, pattern,
len))
530 while (*++wild ==
'*')
539 return strcmp(str, pattern);
562 current.cpu_flags &= ~info->flag;
565 if (
cpu &&
current.cpu_flags == prev_cpu_flags)
573 if (
cfg.set_cpu_flags)
603 if (
cfg.bench && !
state.skip_tests) {
619 if (!
current.cpu_name_printed) {
627#if HAVE_PTHREAD_SETAFFINITY_NP && defined(CPU_SET)
629 if (pthread_getaffinity_np(pthread_self(),
sizeof(
mask), &
mask))
633 for (
int c = 0;
c < CPU_SETSIZE;
c++) {
634 if (CPU_ISSET(
c, &
mask)) {
638 if (pthread_setaffinity_np(pthread_self(),
sizeof(
set), &
set)) {
645 pthread_setaffinity_np(pthread_self(),
sizeof(
mask), &
mask);
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");
662 affinity_err = !spdcs(
process, (ULONG[]) { (ULONG) affinity + 256 }, 1);
666 if (affinity <
sizeof(DWORD_PTR) * 8)
667 affinity_err = !SetProcessAffinityMask(
process, (DWORD_PTR) 1 << affinity);
671#elif HAVE_PTHREAD_SETAFFINITY_NP && defined(CPU_SET)
674 CPU_SET(affinity, &
set);
675 affinity_err = pthread_setaffinity_np(pthread_self(),
sizeof(
set), &
set);
679 LOG(
"checkasm: --affinity is not supported on your system\n");
684 LOG(
"checkasm: invalid cpu affinity (%u)\n", affinity);
687 LOG(
"checkasm: running on cpu %u\n", affinity);
697 if ((
cfg->cpu & info->flag) == info->flag)
717 while ((v = v->
next))
747 state.skip_tests = 1;
762 fprintf(
f,
" - CPU: ");
763 vfprintf(
f, fmt, ap);
779 LOG(
" - Timing resolution: %.4f +/- %.3f ns/%s (%.0f +/- %.1f "
780 "MHz) (provisional)\n",
784 LOG(
" - No-op overhead: %.2f +/- %.3f %ss per call (provisional)\n",
788 LOG(
" - Bench duration: %d µs per function (%" PRIu64
" %ss)\n",
cfg.bench_usec,
791 LOG(
" - Random seed: %u\n",
cfg.seed);
796 if (
state.skip_tests)
802 len +=
snprintf(status,
sizeof(status),
"checkasm: iter=%d/%d cpu=%s test=%s",
823 const int num_checked_asm =
current.num_checked -
current.num_funcs;
826 LOG(
"of %d tests failed", num_checked_asm);
827 }
else if (num_checked_asm) {
830 LOG(
"%d tests passed", num_checked_asm);
831 }
else if (interrupted) {
832 LOG(
"no tests performed");
839 LOG(
", iteration %d of %d, seed %u\n",
state.test_iter + 1,
cfg.repeat,
cfg.seed);
861 LOG(
"checkasm: built without HTML support\n");
871#if HAVE_PRCTL && defined(PR_SET_UNALIGN)
872 prctl(PR_SET_UNALIGN, PR_UNALIGN_SIGBUS);
874 if (
cfg.cpu_affinity_set)
878 if (!
cfg.seed && !
cfg.seed_set)
883 cfg.bench_usec = 1000;
898 if (low_estimate <= 0.0) {
900 "checkasm: cycle counter seems to be non-functional "
901 "(invalid timer scale: %.4f %ss/nsec)\n",
906 state.target_cycles = (uint64_t) (1e3 *
cfg.bench_usec / low_estimate);
944 if (!
version || name_length <= 0 || (
size_t) name_length >=
sizeof(name_buf)
966 fp->report_idx =
current.report_idx;
982 }
while ((v = v->
next));
990 name_length += (int) strlen(v->
suffix) + 1;
992 name_length +=
current.cpu_suffix_length;
995 if (name_length >
state.max_function_name_length)
996 state.max_function_name_length = name_length;
1004 if (
state.skip_tests)
1009 if (
f->report_idx <
current.report_idx) {
1010 f->report_idx =
current.report_idx;
1023 checkasm_simd_warmup();
1039 va_start(
arg, id_fmt);
1040 assert(!
current.func_variant);
1055 vfprintf(stderr, msg,
arg);
1056 fputs(
")\n", stderr);
1089#if CHECKASM_HAVE_LONGJMP
1102 char report_name[256];
1105 int length = (int) strlen(
current.test_name);
1113 if (length >
state.max_report_name_length)
1114 state.max_report_name_length = length;
1116 const int new_checked =
current.num_checked -
current.prev_checked;
1118 int pad_length = (int)
state.max_report_name_length + 3;
1119 assert(!
state.skip_tests);
1123 current.num_failed =
current.prev_failed + (new_checked - fails);
1127 if (
state.test_iter == 0)
1133 pad_length -=
LOG(
" - %s.%s",
current.test_name, report_name);
1135 pad_length -=
LOG(
" - %s",
current.test_name);
1137 LOG(
"%*c",
imax(pad_length, 0) + 2,
'[');
1141 else if (!
current.should_fail)
1168 "Usage: %s [options...] <random seed>\n"
1169 " <random seed> Use fixed value to seed the PRNG\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"
1175 " --function=<pattern> -f Test only the functions matching "
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 "
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 "
1195 val = strtoul(str, &end,
base);
1196 if (errno || end == str || *end)
1198#if !defined(__SIZEOF_LONG__) || !defined(__SIZEOF_INT__) || __SIZEOF_LONG__ > __SIZEOF_INT__
1202 if (
val > (
unsigned) -1)
1212 if (!strncmp(argv[1],
"--help", 6) || !strcmp(argv[1],
"-h")) {
1215 }
else if (!strcmp(argv[1],
"--list-cpu-flags")
1216 || !strcmp(argv[1],
"--list-cpuflags")) {
1219 }
else if (!strcmp(argv[1],
"--list-tests")) {
1222 }
else if (!strcmp(argv[1],
"--list-functions")) {
1225 }
else if (!strcmp(argv[1],
"--bench") || !strcmp(argv[1],
"-b")) {
1227 }
else if (!strncmp(argv[1],
"--bench=", 8)) {
1229 config->function_pattern = argv[1] + 8;
1230 }
else if (!strcmp(argv[1],
"--csv")) {
1232 }
else if (!strcmp(argv[1],
"--tsv")) {
1234 }
else if (!strcmp(argv[1],
"--json")) {
1236 }
else if (!strcmp(argv[1],
"--html")) {
1240 LOG(
"checkasm: built without HTML support\n");
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);
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] :
"";
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] :
"";
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);
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);
1279 }
else if (!strcmp(argv[1],
"--repeat")) {
1280 config->repeat = UINT_MAX;
1282 config->seed_set = 1;
1283 if (!
parseu(&config->seed, argv[1], 10)) {
1284 LOG(
"checkasm: unknown option (%s)\n", argv[1]);
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
static double val(void *priv, double ch)
static const char *const format[]
static void BS_FUNC skip(BSCTX *bc, unsigned int n)
Skip n bits in the buffer.
#define i(width, name, range_min, range_max)
__device__ int printf(const char *,...)
double fmax(double, double)
static void print_usage(void)
uint64_t CheckasmCpu
Opaque type representing a set of CPU feature flags.
uintptr_t CheckasmKey
Opaque type used to identify function implementations.
CheckasmFormat
Output format for benchmark results.
@ CHECKASM_FORMAT_PRETTY
Pretty-printed (colored) text output (default)
@ CHECKASM_FORMAT_TSV
Tab-separated values with optional header.
@ CHECKASM_FORMAT_HTML
Interactive HTML report for web viewing.
@ CHECKASM_FORMAT_CSV
Comma-separated values with optional header.
@ CHECKASM_FORMAT_JSON
JSON structured output with all measurement data.
CHECKASM_API int checkasm_main(CheckasmConfig *config, int argc, const char *argv[])
Main entry point for checkasm test programs.
CheckasmMeasurement perf_scale
CheckasmMeasurement nop_cycles
static const char * ver_suffix(const CheckasmFuncVersion *ver)
static int print_summary(int interrupted)
static int fail_internal(const char *const msg, va_list arg)
int max_report_name_length
static void run_all_tests(void)
static int parseu(unsigned *const dst, const char *const str, const int base)
int checkasm_fail_func(const char *const msg,...)
static void json_var(CheckasmJson *json, const char *key, const char *unit, const CheckasmVar var)
static CheckasmStats stats
static void print_bench_footer(struct IterState *const iter)
static void json_measurement(CheckasmJson *json, const char *key, const char *unit, const CheckasmMeasurement measurement)
void checkasm_fail_abort(const char *const msg,...)
static void update_statusline(void)
static int test_enabled(const CheckasmTest *test)
CheckasmCpu checkasm_get_cpu_flags(void)
Get the current active set of CPU flags.
int checkasm_run(const CheckasmConfig *config)
Run all tests and benchmarks matching the specified patterns.
static const char * cpu_suffix(const CheckasmCpuInfo *cpu)
int checkasm_should_fail(CheckasmCpu cpu_flags)
Mark a block of tests as expected to fail.
const CheckasmCpuInfo * checkasm_get_cpu_info(void)
Get the CPU flag currently being tested.
static char separator(CheckasmFormat format)
static COLD void print_info(void)
void checkasm_list_tests(const CheckasmConfig *config)
Print available tests.
static void check_cpu_flag(const CheckasmCpuInfo *cpu)
static void print_bench_iter(const CheckasmFunc *const f, struct IterState *const iter)
static int wildstrcmp(const char *str, const char *pattern)
void checkasm_set_func_variant(const char *id_fmt,...)
void checkasm_report(const char *const name,...)
static void cpu_mask_json(CheckasmJson *json, const CheckasmConfig cfg, const CheckasmCpu cpu_mask)
static struct @346255127015250356166251341105367306144006377143 state
void checkasm_list_cpu_flags(const CheckasmConfig *cfg)
Print available CPU flags to stdout.
const CheckasmCpuInfo * cpu
static void print_bench_header(struct IterState *const iter)
static struct @111144215057303131116103221376075045141373005341 current
static void handle_interrupt(void)
void checkasm_list_functions(const CheckasmConfig *config)
Print available functions within tests.
static void print_functions(const CheckasmFunc *const f)
CheckasmKey checkasm_check_key(const CheckasmKey version, const char *const name,...)
static int set_cpu_affinity(const unsigned affinity)
static void print_benchmarks(void)
static CheckasmConfig cfg
static void cpu_info_json(void *priv, const char *fmt,...)
static void print_cpu_name(void)
CheckasmFuncVersion * func_ver
static void cpu_fprintf(void *priv, const char *fmt,...)
static double relative_error(double lvar)
int max_function_name_length
int checkasm_run_on_all_cores(void(*func)(void))
void checkasm_func_tree_uninit(CheckasmFuncTree *tree)
CheckasmFunc * checkasm_func_get(CheckasmFuncTree *tree, const char *const name)
int checkasm_bench_func(void)
Check if current function should be benchmarked.
void checkasm_bench_finish(void)
Finalize and store benchmark results.
void checkasm_bench_update(const int iterations, const uint64_t cycles)
Update benchmark statistics with timing results.
int checkasm_bench_runs(void)
Get number of iterations for current benchmark run.
static const char checkasm_html_body[]
static const char checkasm_css[]
static const char checkasm_js[]
static const char checkasm_chart_js[]
int(* func)(AVBPrint *dst, const char *in, const char *arg)
static atomic_int cpu_flags
#define checkasm_load_context(ctx)
#define checkasm_save_context(ctx)
static const uint16_t mask[17]
CheckasmVar checkasm_stats_estimate(const CheckasmStats *const stats)
CheckasmVar checkasm_var_div(CheckasmVar a, CheckasmVar b)
CheckasmVar checkasm_var_mul(CheckasmVar a, CheckasmVar b)
CheckasmVar checkasm_var_sub(CheckasmVar a, CheckasmVar b)
static void checkasm_stats_reset(CheckasmStats *const stats)
static double checkasm_median(const CheckasmVar x)
#define CHECKASM_STATS_SAMPLES
static CheckasmVar checkasm_measurement_result(const CheckasmMeasurement measurement)
static double checkasm_stddev(const CheckasmVar x)
static double checkasm_sample(const CheckasmVar x, const double q)
static CheckasmVar checkasm_var_const(double x)
static void checkasm_measurement_update(CheckasmMeasurement *measurement, const CheckasmStats stats)
static double checkasm_mean(const CheckasmVar x)
static void checkasm_measurement_init(CheckasmMeasurement *measurement)
static double checkasm_mode(const CheckasmVar x)
static void checkasm_stats_add(CheckasmStats *const stats, const CheckasmSample s)
static void checkasm_stats_count_grow(CheckasmStats *const stats, uint64_t cycles, uint64_t target_cycles)
Configuration structure for the checkasm test suite.
Describes a CPU feature flag/capability.
CheckasmMeasurement cycles
const CheckasmCpuInfo * cpu
struct CheckasmFuncVersion * next
CheckasmSample samples[CHECKASM_STATS_SAMPLES]
Describes a single test function.
Test writing API for checkasm.
COLD void checkasm_init_cpu(void)
COLD void checkasm_cpu_info(void(*info_cb)(void *priv, const char *fmt,...), void *priv, const CheckasmConfig *config)
static char * checkasm_strdup(const char *str)
static void * checkasm_mallocz(const size_t size)
int checkasm_perf_init(void)
void checkasm_srand(unsigned seed)
void checkasm_json(CheckasmJson *json, const char *key, const char *fmt,...) CHECKASM_PRINTF(3
void checkasm_set_signal_handlers(void)
checkasm_jmp_buf checkasm_context
static int imax(const int a, const int b)
const char * checkasm_get_last_signal_desc(void)
void checkasm_json_pop(CheckasmJson *json, char type)
void checkasm_statusline(const char *status)
unsigned checkasm_seed(void)
void checkasm_json_push(CheckasmJson *json, const char *const key, char type)
void checkasm_setup_fprintf(void)
int static int checkasm_fprintf(FILE *const f, int color, const char *fmt,...)
char * checkasm_vasprintf(const char *fmt, va_list arg)
CheckasmPerf checkasm_perf
void void checkasm_json_str(CheckasmJson *json, const char *key, const char *str)
void checkasm_measure_nop_cycles(CheckasmMeasurement *meas, uint64_t target_cycles)
volatile sig_atomic_t checkasm_interrupted
void checkasm_measure_perf_scale(CheckasmMeasurement *meas)
static int ref[MAX_W *MAX_W]
static void set(uint8_t *a[], int ch, int index, int ch_count, enum AVSampleFormat f, double v)
static void process(NormalizeContext *s, AVFrame *in, AVFrame *out)