WIP: Add pause.h

This commit is contained in:
Ondřej Surý
2021-04-08 15:02:06 +02:00
parent b517108cbc
commit 6417bc131c
3 changed files with 290 additions and 0 deletions
+1
View File
@@ -57,6 +57,7 @@ libisc_la_HEADERS = \
include/isc/nonce.h \
include/isc/os.h \
include/isc/parseint.h \
include/isc/pause.h \
include/isc/pool.h \
include/isc/portset.h \
include/isc/platform.h \
+41
View File
@@ -0,0 +1,41 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#pragma once
#if defined(_MSC_VER)
#include <intrin.h>
#define isc__pause() YieldProcessor()
#elif defined(__x86_64__)
#include <immintrin.h>
#define isc__pause() _mm_pause()
#elif defined(__i386__)
#define isc__pause() __asm__ __volatile__("rep; nop")
#elif defined(__ia64__)
#define isc__pause() __asm__ __volatile__("hint @pause")
#elif defined(__arm__) && HAVE_ARM_YIELD
#define isc__pause() __asm__ __volatile__("yield")
#elif defined(sun) && (defined(__sparc) || defined(__sparc__))
#include <synch.h>
#define isc__pause() smt_pause()
#elif (defined(__sparc) || defined(__sparc__)) && HAVE_SPARC_PAUSE
#define isc__pause() __asm__ __volatile__("pause")
#elif defined(__ppc__) || defined(_ARCH_PPC) || defined(_ARCH_PWR) || \
defined(_ARCH_PWR2) || defined(_POWER)
#define isc__pause() __asm__ volatile("or 27,27,27")
#else /* if defined(_MSC_VER) */
#define isc__pause() sched_yield()
#endif /* if defined(_MSC_VER) */
#define isc_pause(iters) \
for (size_t __pause = 0; __pause < iters; __pause++) { \
isc__pause(); \
}
+248
View File
@@ -0,0 +1,248 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#if HAVE_CMOCKA
#include <fcntl.h>
#include <sched.h> /* IWYU pragma: keep */
#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#define UNIT_TESTING
#include <cmocka.h>
#include <isc/atomic.h>
#include <isc/file.h>
#include <isc/mem.h>
#include <isc/mutex.h>
#include <isc/os.h>
#include <isc/pause.h>
#include <isc/print.h>
#include <isc/result.h>
#include <isc/stdio.h>
#include <isc/thread.h>
#include <isc/time.h>
#include <isc/util.h>
#include "isctest.h"
#define LOOPS 1000
#define DELAY_LOOP 100
static unsigned int workers = 0;
static int
_setup(void **state) {
isc_result_t result;
char *p;
if (workers == 0) {
workers = isc_os_ncpus();
}
p = getenv("ISC_TASK_WORKERS");
if (p != NULL) {
workers = atoi(p);
}
INSIST(workers != 0);
UNUSED(state);
result = isc_test_begin(NULL, true, workers);
assert_int_equal(result, ISC_R_SUCCESS);
return (0);
}
static int
_teardown(void **state) {
UNUSED(state);
isc_test_end();
return (0);
}
static void
isc_mutex_test(void **state) {
isc_mutex_t lock;
UNUSED(state);
isc_mutex_init(&lock);
for (size_t i = 0; i < LOOPS; i++) {
isc_mutex_lock(&lock);
isc_pause(DELAY_LOOP);
isc_mutex_unlock(&lock);
}
isc_mutex_destroy(&lock);
}
#define ITERS 20
#define DC 200
#define MIN 800
#define MAX 1600
static size_t shared_counter = 0;
static size_t expected_counter = SIZE_MAX;
static isc_mutex_t lock = ISC_MUTEX_INITIALIZER;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static isc_threadresult_t
pthread_mutex_thread(isc_threadarg_t arg) {
size_t cont = *(size_t *)arg;
for (size_t i = 0; i < LOOPS; i++) {
pthread_mutex_lock(&mutex);
size_t v = shared_counter;
isc_pause(DELAY_LOOP);
shared_counter = v + 1;
pthread_mutex_unlock(&mutex);
isc_pause(cont);
}
return ((isc_threadresult_t)0);
}
static isc_threadresult_t
isc_mutex_thread(isc_threadarg_t arg) {
size_t cont = *(size_t *)arg;
for (size_t i = 0; i < LOOPS; i++) {
isc_mutex_lock(&lock);
size_t v = shared_counter;
isc_pause(DELAY_LOOP);
shared_counter = v + 1;
isc_mutex_unlock(&lock);
isc_pause(cont);
}
return ((isc_threadresult_t)0);
}
static void
isc_mutex_benchmark(void **state) {
isc_thread_t *threads = isc_mem_get(test_mctx,
sizeof(*threads) * workers);
isc_time_t ts1, ts2;
double t;
isc_result_t result;
int dc;
size_t cont;
UNUSED(state);
memset(threads, 0, sizeof(*threads) * workers);
expected_counter = ITERS * workers * LOOPS * ((MAX - MIN) / DC + 1);
/* PTHREAD MUTEX */
result = isc_time_now_hires(&ts1);
assert_int_equal(result, ISC_R_SUCCESS);
shared_counter = 0;
dc = DC;
for (size_t l = 0; l < ITERS; l++) {
for (cont = (dc > 0) ? MIN : MAX; cont <= MAX && cont >= MIN;
cont += dc) {
for (size_t i = 0; i < workers; i++) {
isc_thread_create(pthread_mutex_thread, &cont,
&threads[i]);
}
for (size_t i = 0; i < workers; i++) {
isc_thread_join(threads[i], NULL);
}
}
dc = -dc;
}
assert_int_equal(shared_counter, expected_counter);
result = isc_time_now_hires(&ts2);
assert_int_equal(result, ISC_R_SUCCESS);
t = isc_time_microdiff(&ts2, &ts1);
printf("[ TIME ] isc_mutex_benchmark: %zu pthread_mutex loops in "
"%u threads, %2.3f seconds, %2.3f calls/second\n",
shared_counter, workers, t / 1000000.0,
shared_counter / (t / 1000000.0));
/* ISC MUTEX */
result = isc_time_now_hires(&ts1);
assert_int_equal(result, ISC_R_SUCCESS);
dc = DC;
shared_counter = 0;
for (size_t l = 0; l < ITERS; l++) {
for (cont = (dc > 0) ? MIN : MAX; cont <= MAX && cont >= MIN;
cont += dc) {
for (size_t i = 0; i < workers; i++) {
isc_thread_create(isc_mutex_thread, &cont,
&threads[i]);
}
for (size_t i = 0; i < workers; i++) {
isc_thread_join(threads[i], NULL);
}
}
dc = -dc;
}
assert_int_equal(shared_counter, expected_counter);
result = isc_time_now_hires(&ts2);
assert_int_equal(result, ISC_R_SUCCESS);
t = isc_time_microdiff(&ts2, &ts1);
printf("[ TIME ] isc_mutex_benchmark: %zu isc_mutex loops in %u "
"threads, %2.3f seconds, %2.3f calls/second\n",
shared_counter, workers, t / 1000000.0,
shared_counter / (t / 1000000.0));
isc_mem_put(test_mctx, threads, sizeof(*threads) * workers);
}
/*
* Main
*/
int
main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(isc_mutex_test, _setup,
_teardown),
#if !defined(__SANITIZE_THREAD__)
cmocka_unit_test_setup_teardown(isc_mutex_benchmark, _setup,
_teardown),
#endif /* __SANITIZE_THREAD__ */
};
return (cmocka_run_group_tests(tests, NULL, NULL));
}
#else /* HAVE_CMOCKA */
#include <stdio.h>
int
main(void) {
printf("1..0 # Skipped: cmocka not available\n");
return (SKIPPED_TEST_EXIT_CODE);
}
#endif /* if HAVE_CMOCKA */