#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#include <notify.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <assert.h>
extern uint32_t notify_register_plain(const char *name, int *out_token);
#ifdef NO_OP_TESTS
extern uint32_t notify_no_op_str_sync(const char *name, size_t len);
extern uint32_t notify_no_op_str_async(const char *name, size_t len);
extern uint32_t notify_no_op_int_sync(int n);
extern uint32_t notify_no_op_int_async(int n);
#endif
#define MAX_CNT 100
#define MAX_SPL 10000
#define DEFAULT_CNT 50
#if defined(__arm__)
#define DEFAULT_SPL 501
#else
#define DEFAULT_SPL 1001
#endif
uint32_t cnt = DEFAULT_CNT;
uint32_t spl = DEFAULT_SPL;
static long double loop_cost;
static mach_timebase_info_data_t tbi;
static uint64_t dmy[MAX_SPL], reg_plain[MAX_SPL], cancel_plain[MAX_SPL], reg_port[MAX_SPL], cancel_port[MAX_SPL];
static uint64_t post_plain1[MAX_SPL], post_plain2[MAX_SPL], post_plain3[MAX_SPL];
static uint64_t post_port[MAX_SPL], set_state1[MAX_SPL], set_state2[MAX_SPL], get_state[MAX_SPL];
#ifdef NO_OP_TESTS
static uint64_t nss[MAX_SPL], nsa[MAX_SPL], nis[MAX_SPL], nia[MAX_SPL];
#endif
static void __attribute__((noinline))
print_result(uint64_t *s, const char *str)
{
unsigned j;
uint64_t m;
long double dd = 0, mm, sd = 0;
for (j = 0 ; j < spl; j++) {
dd += (long double)s[j]/(long double)cnt;
}
dd = dd/(long double)spl;
for (j = 0 ; j < spl; j++) {
long double tmp = (long double)s[j]/(long double)cnt - dd;
sd += tmp * tmp;
}
sd = sqrtl(sd/(long double)spl);
qsort_b(s, spl, sizeof(uint64_t),
^(const void *a, const void *b){
const uint64_t l = *(const uint64_t*)a;
const uint64_t r = *(const uint64_t*)b;
return l == r ? 0 : (l < r ? -1 : 1);
});
m = s[spl/2];
mm = (long double)m / (long double)cnt;
if (tbi.numer != tbi.denom) {
dd *= tbi.numer;
dd /= tbi.denom;
mm *= tbi.numer;
mm /= tbi.denom;
sd *= tbi.numer;
sd /= tbi.denom;
}
if (str) {
dd -= loop_cost;
mm -= loop_cost;
} else {
loop_cost = mm;
}
dd /= NSEC_PER_USEC;
dd = roundl(dd * 200.0)/200.0;
mm /= NSEC_PER_USEC;
mm = roundl(mm * 200.0)/200.0;
sd /= NSEC_PER_USEC;
sd = roundl(sd * 200.0)/200.0;
if (!str) {
printf("%-28s %-8s %-8s %-8s\n",
"Symbol", "Median", "Average", "StdDev");
}
printf("%-28s%8.3Lf us %8.3Lf us %8.3Lf us\n",
str ? str : "Empty loop:", mm, dd, sd);
}
int
main(int argc, char *argv[])
{
uint32_t r;
kern_return_t kr;
unsigned i, j;
kr = mach_timebase_info(&tbi);
assert(!kr);
int tok;
r = notify_register_check("dummy.test", &tok);
assert(r == 0);
r = notify_cancel(tok);
assert(r == 0);
int t[MAX_CNT];
mach_port_t p[MAX_CNT];
char *n[MAX_CNT];
size_t l[MAX_CNT];
uint64_t s;
for (i = 1; i < argc; i++)
{
if (!strcmp(argv[i], "-c")) cnt = atoi(argv[++i]);
else if (!strcmp(argv[i], "-s")) spl = atoi(argv[++i]) + 1;
}
if (cnt > MAX_CNT) cnt = MAX_CNT;
if (spl > MAX_SPL) spl = MAX_SPL + 1;
for (j = 0 ; j < spl; j++)
{
for (i = 0; i < cnt; i++)
{
r = asprintf(&n[i], "dummy.test.%d", i);
assert(r != -1);
l[i] = strlen(n[i]);
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &p[i]);
assert(kr == 0);
}
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
#if defined(__arm__)
asm volatile("movs %0, #0 @ %1" : "=r" (r) : "m" (n[i]) );
#elif defined(__x86_64)
asm volatile("movl $0, %0 # %1" : "=r" (r) : "m" (n[i]) );
#else
asm volatile("" : : "m" (n[i]) );
r = 0;
#endif
assert(r == 0);
}
dmy[j] = mach_absolute_time() - s;
#ifdef NO_OP_TESTS
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
r = notify_no_op_str_sync(n[i], l[i]);
assert(r == 0);
}
nss[j] = mach_absolute_time() - s;
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
r = notify_no_op_str_async(n[i], l[i]);
assert(r == 0);
}
nsa[j] = mach_absolute_time() - s;
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
r = notify_no_op_int_sync(i);
assert(r == 0);
}
nis[j] = mach_absolute_time() - s;
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
r = notify_no_op_int_async(i);
assert(r == 0);
}
nia[j] = mach_absolute_time() - s;
#endif
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
r = notify_register_plain(n[i], &t[i]);
assert(r == 0);
}
reg_plain[j] = mach_absolute_time() - s;
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
r = notify_post(n[i]);
assert(r == 0);
}
post_plain1[j] = mach_absolute_time() - s;
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
r = notify_post(n[i]);
assert(r == 0);
}
post_plain2[j] = mach_absolute_time() - s;
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
r = notify_post(n[i]);
assert(r == 0);
}
post_plain3[j] = mach_absolute_time() - s;
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
r = notify_cancel(t[i]);
assert(r == 0);
}
cancel_plain[j] = mach_absolute_time() - s;
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
r = notify_register_mach_port(n[i], &p[i], NOTIFY_REUSE, &t[i]);
assert(r == 0);
}
reg_port[j] = mach_absolute_time() - s;
#ifdef NOTDEF
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
r = notify_post(n[i]);
assert(r == 0);
}
post_port[j] = mach_absolute_time() - s;
for (i = 0; i < cnt; i++)
{
mach_msg_empty_rcv_t msg = { .header = {
.msgh_size = sizeof(mach_msg_empty_rcv_t),
.msgh_local_port = p[i],
}};
kr = mach_msg_receive(&msg.header);
assert(kr == 0);
mach_msg_destroy(&msg.header);
}
#endif
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
r = notify_set_state(t[i], 1);
assert(r == 0);
}
set_state1[j] = mach_absolute_time() - s;
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
uint64_t dummy;
r = notify_get_state(t[i], &dummy);
assert(r == 0);
}
get_state[j] = mach_absolute_time() - s;
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
r = notify_set_state(t[i], 2);
assert(r == 0);
}
set_state2[j] = mach_absolute_time() - s;
s = mach_absolute_time();
for (i = 0; i < cnt; i++)
{
r = notify_cancel(t[i]);
assert(r == 0);
}
cancel_port[j] = mach_absolute_time() - s;
for (i = 0; i < cnt; i++)
{
free(n[i]);
kr = mach_port_mod_refs(mach_task_self(), p[i], MACH_PORT_RIGHT_RECEIVE, -1);
assert(kr == 0);
}
}
print_result(dmy, NULL);
#ifdef NO_OP_TESTS
print_result(nss, "notify_no_op_str_sync:");
print_result(nsa, "notify_no_op_str_async:");
print_result(nis, "notify_no_op_int_sync:");
print_result(nia, "notify_no_op_int_async:");
#endif
print_result(reg_plain, "notify_register_plain:");
print_result(post_plain1, "notify_post [plain 1]:");
print_result(post_plain2, "notify_post [plain 2]:");
print_result(post_plain3, "notify_post [plain 3]:");
print_result(cancel_plain, "notify_cancel [plain]:");
print_result(reg_port, "notify_register_mach_port:");
print_result(set_state1, "notify_set_state [1]:");
print_result(set_state2, "notify_set_state [2]:");
print_result(get_state, "notify_get_state:");
print_result(cancel_port, "notify_cancel [port]");
return 0;
}