blob: c7c043bb611ed127f2987e06fcb0081ab46d9890 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>
#define ZX_CPRNG_DRAW_MAX_LEN 256
const int kDefaultMaxWorkers = 64;
const size_t kTotalDrawLen = 1UL << 24;
typedef struct {
std::mutex mtx;
std::condition_variable cnd;
bool waved;
} green_flag_t;
void print_usage(const char* argv0) {
printf("usage: %s [-n <num_workers>]\n\n", argv0);
printf("Spawn threads to zx_crpng_draw %zu bytes.\n\n", kTotalDrawLen);
printf("-n <num_workers> Specifies the max number of threads.\n");
printf(" Defaults to %d.\n", kDefaultMaxWorkers);
}
void worker(green_flag_t* green_flag) {
ssize_t rc;
uint8_t buf[ZX_CPRNG_DRAW_MAX_LEN];
size_t iters = kTotalDrawLen / ZX_CPRNG_DRAW_MAX_LEN;
size_t actual;
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
perror("open('udev/random', O_RDONLY)");
return;
}
{
std::unique_lock<std::mutex> guard(green_flag->mtx);
if (!green_flag->waved && green_flag->cnd.wait_for(guard, std::chrono::seconds(10)) == std::cv_status::timeout) {
printf("timeout\n");
return;
}
}
for (size_t i = 0; i < iters; ++i) {
if ((rc = read(fd, buf, sizeof(buf))) < 0) {
perror("read");
return;
}
}
}
int main(int argc, char** argv) {
int max_workers;
switch (argc) {
case 1:
max_workers = kDefaultMaxWorkers;
break;
case 3:
max_workers = atoi(argv[2]);
if (strcmp(argv[1], "-n") == 0 && max_workers > 0) {
break;
}
// fall through
default:
print_usage(argv[0]);
return EINVAL;
}
green_flag_t green_flag;
std::vector<std::thread> threads;
struct timespec tp;
uint64_t start, finish, per_call;
for (int num_workers = 1; num_workers <= max_workers; ++num_workers) {
{
std::unique_lock<std::mutex> guard(green_flag.mtx);
threads.clear();
green_flag.waved = false;
for (int i = 0; i < num_workers; ++i) {
threads.push_back(std::thread(worker, &green_flag));
}
if (clock_gettime(CLOCK_MONOTONIC, &tp) < 0) {
perror("clock_gettime");
return errno;
}
start = (tp.tv_sec * 1000000000) + tp.tv_nsec;
green_flag.waved = true;
green_flag.cnd.notify_all();
}
for (auto& thrd : threads) {
thrd.join();
}
if (clock_gettime(CLOCK_MONOTONIC, &tp) < 0) {
perror("clock_gettime");
return errno;
}
finish = (tp.tv_sec * 1000000000) + tp.tv_nsec;
per_call = (finish - start) / ((kTotalDrawLen / 1024) * num_workers);
printf("%d workers, %zu bytes, %d bytes/call => %" PRIu64 " ns/kb.\n",
num_workers, kTotalDrawLen, ZX_CPRNG_DRAW_MAX_LEN, per_call);
}
return 0;
}