root/regress/sys/kern/nameibench/nameibench.c
/*      $NetBSD: nameibench.c,v 1.3 2019/11/23 20:37:49 ad Exp $        */

/*-
 * Copyright (c) 2009 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Andrew Doran.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/param.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <inttypes.h>
#include <signal.h>

#define MAXFILES        4096

const int primes[] = {
        233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283,
        293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359,
        367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431,
        433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491,
        499, 503, 509, 521, 523, 541, 
};

char    **sa;
u_int   sasize;
u_int   sacnt;
struct  timespec sts;
struct  timespec ets;
pthread_barrier_t       barrier;
pthread_mutex_t lock;
volatile sig_atomic_t   stop;
double  nlookups;
double  ideal;

static void
makelist(void)
{
        char buf[MAXPATHLEN], *p;
        size_t l;
        FILE *fp;

        fp = popen("/usr/bin/locate x", "r");
        if (fp == NULL) {
                perror("popen");
                exit(EXIT_FAILURE);
        }

        while (fgets(buf, sizeof(buf), fp) != NULL) {
                l = strlen(buf) + 1;
                p = malloc(l);
                if (p == NULL) {
                        perror("malloc");
                        exit(EXIT_FAILURE);
                }
                strcpy(p, buf);
                p[l - 2] = '\0';
                if (sacnt == sasize) {
                        sasize += 256;
                        sa = realloc(sa, sizeof(*sa) * sasize);
                        if (sa == NULL) {
                                perror("realloc");
                                exit(EXIT_FAILURE);
                        }
                }
                sa[sacnt++] = p;
                if (sacnt == MAXFILES) {
                        break;
                }
        }

        fclose(fp);
}

static void
lookups(int idx)
{
        u_int p, c;

        for (c = 0, p = 0; !stop; c++) {
                p += primes[idx];
                if (p >= sacnt) {
                        p %= sacnt;
                }
                (void)access(sa[p], F_OK);
                
        }

        pthread_mutex_lock(&lock);
        nlookups += c;
        pthread_mutex_unlock(&lock);
}

static void
start(void)
{

        (void)pthread_barrier_wait(&barrier);
        if (clock_gettime(CLOCK_MONOTONIC, &sts)) {
                perror("clock_gettime");
                exit(EXIT_FAILURE);
        }
}

static void
end(void)
{

        if (clock_gettime(CLOCK_MONOTONIC, &ets)) {
                perror("clock_gettime");
                exit(EXIT_FAILURE);
        }
        (void)pthread_barrier_wait(&barrier);
}

static void *
thread(void *cookie)
{

        start();
        lookups((int)(uintptr_t)cookie);
        end();

        return NULL;
}

static void
sigalrm(int junk)
{

        stop = (sig_atomic_t)1;
}

static void
run(int nt)
{
        pthread_t pt;
        double c;
        int i;
        long us;

        if (pthread_barrier_init(&barrier, NULL, nt + 1)) {
                fprintf(stderr, "pthread_barrier_init\n");
                exit(EXIT_FAILURE);
        }

        nlookups = 0;
        stop = 0;
        for (i = 0; i < nt; i++) {
                if (pthread_create(&pt, NULL, thread, (void *)(uintptr_t)i)) {
                        fprintf(stderr, "pthread_create\n");
                        exit(EXIT_FAILURE);
                }
        }
        start();
        alarm(10);
        end();
        us = (long)(ets.tv_sec * (uint64_t)1000000 + ets.tv_nsec / 1000);
        us -= (long)(sts.tv_sec * (uint64_t)1000000 + sts.tv_nsec / 1000);
        c = nlookups * 1000000.0 / us;
        if (ideal == 0) {
                ideal = c;
        }
        printf("%d\t%d\t%.0f\t%.0f\n", sacnt, nt, c, ideal * nt);

        if (pthread_barrier_destroy(&barrier)) {
                fprintf(stderr, "pthread_barrier_destroy\n");
                exit(EXIT_FAILURE);
        }
}

int
main(int argc, char **argv)
{
        int i, mt;

        (void)setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
        if (argc < 2) {
                mt = sysconf(_SC_NPROCESSORS_ONLN);
        } else {
                mt = atoi(argv[1]);
                if (mt < 1) {
                        mt = 1;
                }
        }
        if (pthread_mutex_init(&lock, NULL)) {
                fprintf(stderr, "pthread_mutex_init\n");
                exit(EXIT_FAILURE);
        }
        makelist();
        (void)signal(SIGALRM, sigalrm);
        printf("# nname\tnthr\tpersec\tideal\n");
        for (i = 1; i <= mt; i++) {
                run(i); 
        }
        exit(EXIT_SUCCESS);
}