-
Notifications
You must be signed in to change notification settings - Fork 667
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f0632ed
commit de50d6e
Showing
11 changed files
with
377 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
core/iwasm/libraries/lib-wasi-threads/stress_test/build.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
#!/bin/bash | ||
|
||
# | ||
# Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
# | ||
|
||
set -eo pipefail | ||
CC=${CC:=/opt/wasi-sdk/bin/clang} | ||
WAMR_DIR=../../../../.. | ||
|
||
show_usage() { | ||
echo "Usage: $0 [--sysroot PATH_TO_SYSROOT]" | ||
echo "--sysroot PATH_TO_SYSROOT specify to build with custom sysroot for wasi-libc" | ||
} | ||
|
||
while [[ $# -gt 0 ]]; do | ||
key="$1" | ||
case $key in | ||
--sysroot) | ||
sysroot_path="$2" | ||
shift | ||
shift | ||
;; | ||
--help) | ||
show_usage | ||
exit | ||
;; | ||
*) | ||
echo "Unknown option: $1" | ||
exit 1 | ||
;; | ||
esac | ||
done | ||
|
||
rm -rf *.wasm | ||
rm -rf *.aot | ||
|
||
for test_c in *.c; do | ||
test_wasm="$(basename $test_c .c).wasm" | ||
|
||
if [[ -n "$sysroot_path" ]]; then | ||
if [ ! -d "$sysroot_path" ]; then | ||
echo "Directory $sysroot_path doesn't exist. Aborting" | ||
exit 1 | ||
fi | ||
sysroot_command="--sysroot $sysroot_path" | ||
fi | ||
|
||
echo "Compiling $test_c to $test_wasm" | ||
$CC \ | ||
-target wasm32-wasi-threads \ | ||
-O2 \ | ||
-pthread \ | ||
-z stack-size=32768 \ | ||
-Wl,--export=__heap_base \ | ||
-Wl,--export=__data_end \ | ||
-Wl,--shared-memory,--max-memory=1966080 \ | ||
-Wl,--export=wasi_thread_start \ | ||
-Wl,--export=malloc \ | ||
-Wl,--export=free \ | ||
-I $WAMR_DIR/samples/wasi-threads/wasm-apps \ | ||
$sysroot_command \ | ||
$test_c -o $test_wasm | ||
done |
3 changes: 3 additions & 0 deletions
3
core/iwasm/libraries/lib-wasi-threads/stress_test/manifest.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"name": "lib-wasi-threads stress tests" | ||
} |
261 changes: 261 additions & 0 deletions
261
core/iwasm/libraries/lib-wasi-threads/stress_test/mutex_common.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,261 @@ | ||
/* | ||
* Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. | ||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
*/ | ||
|
||
#ifndef MUTEX_COMMON_H | ||
#define MUTEX_COMMON_H | ||
|
||
#include <pthread.h> | ||
#include <stdio.h> | ||
#include <assert.h> | ||
#include <errno.h> | ||
#include <unistd.h> | ||
#include <stdbool.h> | ||
|
||
typedef struct { | ||
pthread_mutex_t *mutex; | ||
int *counter; | ||
int limit; | ||
bool is_sleeping; | ||
} MutexCounter; | ||
|
||
enum constants { | ||
NUM_ITER = 100000, | ||
NUM_THREADS = 12, | ||
NUM_RETRY = 8, | ||
RETRY_SLEEP_TIME_US = 1000, | ||
}; | ||
|
||
// This enum defines whether thread should sleep to increase contention | ||
enum sleep_state { | ||
NON_SLEEP = 0, | ||
SLEEP = 1, | ||
}; | ||
|
||
void | ||
spawn_thread(pthread_t *tid, void *func, void *arg) | ||
{ | ||
int status_code = -1; | ||
int timeout_us = RETRY_SLEEP_TIME_US; | ||
for (int tries = 0; status_code != 0 && tries < NUM_RETRY; ++tries) { | ||
status_code = pthread_create(tid, NULL, (void *(*)(void *))func, arg); | ||
assert(status_code == 0 || status_code == EAGAIN); | ||
if (status_code == EAGAIN) { | ||
usleep(timeout_us); | ||
timeout_us *= 2; | ||
} | ||
} | ||
|
||
assert(status_code == 0 && "Thread creation should succeed"); | ||
} | ||
|
||
// We're counting how many times each thread was called using this array | ||
// Main thread is also counted here so we need to make arrays bigger | ||
typedef struct { | ||
int tids[NUM_THREADS + 1]; | ||
int calls[NUM_THREADS + 1]; | ||
} StatCollector; | ||
|
||
StatCollector call_stat; | ||
|
||
void | ||
clear_stat() | ||
{ | ||
for (int i = 0; i < NUM_THREADS + 1; ++i) { | ||
call_stat.tids[i] = 0; | ||
call_stat.calls[i] = 0; | ||
} | ||
} | ||
|
||
void | ||
add_to_stat(int tid) | ||
{ | ||
int tid_num = 0; | ||
for (; tid_num < NUM_THREADS + 1 && call_stat.tids[tid_num] != 0; | ||
++tid_num) { | ||
if (call_stat.tids[tid_num] == tid) { | ||
call_stat.calls[tid_num]++; | ||
return; | ||
} | ||
} | ||
|
||
assert(tid_num < NUM_THREADS + 1); | ||
call_stat.tids[tid_num] = tid; | ||
call_stat.calls[tid_num] = 1; | ||
} | ||
|
||
// This function ensures that every of NUM_THREADS+1 threads number of | ||
// operations is in between [NUM_ITER/NUM_THREADS, NUM_ITER/(NUM_THREADS+2)]. | ||
// For 100'000 operations it means that every thread should be called [7142, | ||
// 8333] times. Also, such a simple hash function might be not the ideal one so | ||
// the confidence interval is kept quite wide | ||
void | ||
check_stat() | ||
{ | ||
for (int i = 0; i < NUM_THREADS + 1; ++i) { | ||
if (call_stat.calls[i] != 0) { | ||
assert(call_stat.calls[i] < NUM_ITER / NUM_THREADS | ||
&& "Call distribution by TID is out of range"); | ||
assert(call_stat.calls[i] > NUM_ITER / (NUM_THREADS + 2) | ||
&& "Call distribution by TID is out of range"); | ||
} | ||
} | ||
} | ||
|
||
void | ||
print_stat() | ||
{ | ||
fprintf(stderr, "Thread calls count by TID\n"); | ||
for (int i = 0; i < NUM_THREADS + 1; ++i) { | ||
if (call_stat.tids[i] != 0) { | ||
fprintf(stderr, "TID: %d; Calls: %d\n", call_stat.tids[i], | ||
call_stat.calls[i]); | ||
} | ||
} | ||
} | ||
|
||
// xor with a random value usually gives quite a uniform distribution | ||
uint32_t | ||
hash(uint32_t counter) | ||
{ | ||
return (counter << 22) ^ (0xA2BC22A); // Random value for hash func | ||
} | ||
|
||
// Locking and unlocking the same thread | ||
void | ||
same_thread_test(pthread_mutex_t *mutex) | ||
{ | ||
assert(pthread_mutex_lock(mutex) == 0 | ||
&& "Main thread should be able to lock a mutex"); | ||
assert(pthread_mutex_unlock(mutex) == 0 | ||
&& "Main thread should be able to unlock a mutex"); | ||
} | ||
|
||
void * | ||
inc_until_limit(void *arg) | ||
{ | ||
MutexCounter mutex_counter = *(MutexCounter *)(arg); | ||
int sleep_us = 0; | ||
while (!pthread_mutex_lock(mutex_counter.mutex) | ||
&& *mutex_counter.counter < mutex_counter.limit) { | ||
(*mutex_counter.counter)++; | ||
add_to_stat((int)(pthread_self())); | ||
if (mutex_counter.is_sleeping) { | ||
sleep_us = hash(*mutex_counter.counter) % 1000; | ||
} | ||
|
||
assert(pthread_mutex_unlock(mutex_counter.mutex) == 0 | ||
&& "Should be able to unlock a mutex"); | ||
if (mutex_counter.is_sleeping) { | ||
usleep(sleep_us); | ||
} | ||
} | ||
|
||
assert(*mutex_counter.counter == mutex_counter.limit); | ||
assert(pthread_mutex_unlock(mutex_counter.mutex) == 0 | ||
&& "Should be able to unlock the mutex after test execution"); | ||
|
||
return NULL; | ||
} | ||
|
||
void | ||
non_main_test(pthread_mutex_t *mutex, int num_iter) | ||
{ | ||
MutexCounter mutex_counter; | ||
int counter = 0; | ||
mutex_counter.mutex = mutex; | ||
mutex_counter.counter = &counter; | ||
mutex_counter.limit = num_iter; | ||
mutex_counter.is_sleeping = false; | ||
|
||
pthread_t tid = 0; | ||
spawn_thread(&tid, inc_until_limit, &mutex_counter); | ||
|
||
assert(tid != 0 && "TID can't be 0 after successful thread creation"); | ||
pthread_join(tid, NULL); | ||
assert(*mutex_counter.counter == mutex_counter.limit); | ||
} | ||
|
||
void | ||
main_non_main_test(pthread_mutex_t *mutex, int num_iter) | ||
{ | ||
MutexCounter mutex_counter; | ||
int counter = 0; | ||
mutex_counter.mutex = mutex; | ||
mutex_counter.counter = &counter; | ||
mutex_counter.limit = num_iter; | ||
mutex_counter.is_sleeping = false; | ||
|
||
pthread_t tid = 0; | ||
spawn_thread(&tid, inc_until_limit, &mutex_counter); | ||
|
||
assert(tid != 0 && "TID can't be 0 after successful thread creation"); | ||
inc_until_limit(&mutex_counter); | ||
assert(pthread_join(tid, NULL) == 0 | ||
&& "Thread should be joined without errors"); | ||
; | ||
assert(*mutex_counter.counter == mutex_counter.limit); | ||
} | ||
|
||
void | ||
concurrent_inc_test(pthread_mutex_t *mutex, int num_iter, int threads_num, | ||
bool is_sleeping) | ||
{ | ||
MutexCounter mutex_counter; | ||
int counter = 0; | ||
mutex_counter.mutex = mutex; | ||
mutex_counter.counter = &counter; | ||
mutex_counter.limit = num_iter; | ||
mutex_counter.is_sleeping = is_sleeping; | ||
|
||
pthread_t tids[threads_num]; | ||
for (int i = 0; i < threads_num; ++i) { | ||
spawn_thread(&tids[i], inc_until_limit, &mutex_counter); | ||
} | ||
|
||
inc_until_limit(&mutex_counter); | ||
|
||
for (int i = 0; i < threads_num; ++i) { | ||
assert(pthread_join(tids[i], NULL) == 0 | ||
&& "Thread should be joined without errors"); | ||
; | ||
} | ||
} | ||
|
||
// This function just runs all the tests described above | ||
void | ||
run_common_tests(pthread_mutex_t *mutex) | ||
{ | ||
fprintf(stderr, "Starting main_thread test\n"); | ||
clear_stat(); | ||
for (int i = 0; i < NUM_ITER; ++i) { | ||
same_thread_test(mutex); | ||
} | ||
fprintf(stderr, "Finished main_thread test\n"); | ||
|
||
fprintf(stderr, "Starting non_main test\n"); | ||
clear_stat(); | ||
non_main_test(mutex, NUM_ITER); | ||
fprintf(stderr, "Finished non_main test\n"); | ||
|
||
fprintf(stderr, "Starting main_non_main test\n"); | ||
clear_stat(); | ||
main_non_main_test(mutex, NUM_ITER); | ||
fprintf(stderr, "Finished main_non_main test\n"); | ||
|
||
fprintf(stderr, "Starting concurrent_inc_sleep test\n"); | ||
clear_stat(); | ||
concurrent_inc_test(mutex, NUM_ITER, NUM_THREADS, SLEEP); | ||
print_stat(); | ||
fprintf(stderr, "Finished concurrent_inc sleep test\n"); | ||
|
||
fprintf(stderr, "Starting concurrent_inc_non_sleep test\n"); | ||
check_stat(); | ||
clear_stat(); | ||
concurrent_inc_test(mutex, NUM_ITER, NUM_THREADS, NON_SLEEP); | ||
print_stat(); | ||
fprintf(stderr, "Finished concurrent_inc sleep test\n"); | ||
} | ||
|
||
#endif // MUTEX_COMMON_H |
15 changes: 15 additions & 0 deletions
15
core/iwasm/libraries/lib-wasi-threads/stress_test/normal_mutex_stress_test.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#include <pthread.h> | ||
#include <errno.h> | ||
#include "mutex_common.h" | ||
|
||
int | ||
main() | ||
{ | ||
pthread_mutex_t mutex; | ||
pthread_mutex_init(&mutex, NULL); | ||
|
||
run_common_tests(&mutex); | ||
|
||
fprintf(stderr, "Normal mutex test is completed\n"); | ||
pthread_mutex_destroy(&mutex); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.