133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner/* 233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * Copyright (C) 2011 The Android Open Source Project 333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * All rights reserved. 433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * 533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * Redistribution and use in source and binary forms, with or without 633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * modification, are permitted provided that the following conditions 733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * are met: 833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * * Redistributions of source code must retain the above copyright 933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * notice, this list of conditions and the following disclaimer. 1033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * * Redistributions in binary form must reproduce the above copyright 1133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * notice, this list of conditions and the following disclaimer in 1233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * the documentation and/or other materials provided with the 1333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * distribution. 1433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * 1533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 1833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 1933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 2233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * SUCH DAMAGE. 2733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner */ 2833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 2933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner/* This program is used to benchmark various pthread operations 3033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * Note that we want to be able to build it with GLibc, both on 3133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * a Linux host and an Android device. For example, on ARM, one 3233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * can build it manually with: 3333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * 3433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * arm-linux-none-gnueabi-gcc -static -o bench_pthread_gnueabi \ 3533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * bench_pthread.c -O2 -lpthread -lrt 3633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner */ 3733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#define _GNU_SOURCE 1 3833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#include <time.h> 3933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#include <stdio.h> 4033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#include <stdint.h> 4133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#include <limits.h> 4233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#include <pthread.h> 4333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#include <semaphore.h> 4433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#include <stdlib.h> 4533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#include <unistd.h> 4633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 4733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#define S(x) S_(x) 4833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#define S_(x) #x 4933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 5033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#define C(x,y) C_(x,y) 5133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#define C_(x,y) x ## y 5233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 5333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#ifndef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER 5433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP 5533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#endif 5633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 5733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER 5833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP 5933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#endif 6033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 6133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turnerstatic int64_t now_ns(void) 6233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner{ 6333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner struct timespec ts; 6433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner /* NOTE: get thread-specific CPU-time clock to ensure 6533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * we don't measure stuff like kernel thread preemptions 6633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner * that might happen during the benchmark 6733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner */ 6833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner clock_gettime(CLOCK_THREAD_CPUTIME_ID,&ts); 6933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner return ts.tv_sec*1000000000LL + ts.tv_nsec; 7033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner} 7133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 7233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#define SUBCOUNT 10000 7333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#define MAX_STATS 1000000 7433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 7533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner/* Maximum time we'll wait for a single bench run */ 7633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#define MAX_WAIT_MS 1000 7733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 7833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turnerstatic int64_t stats[MAX_STATS]; 7933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 8033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turnerstatic int 8133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turnercompare_stats(const void* a, const void* b) 8233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner{ 8333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner uint64_t sa = *(const uint64_t*)a; 8433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner uint64_t sb = *(const uint64_t*)b; 8533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner if (sa < sb) 8633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner return -1; 8733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner if (sa > sb) 8833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner return +1; 8933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner else 9033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner return 0; 9133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner} 9233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 9333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turnerstatic void 9433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turnerfilter_stats(int count, const char* statement) 9533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner{ 9633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner int64_t min, max, avg, median; 9733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 9833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner /* sort the array in increasing order */ 9933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner qsort(stats, count, sizeof(stats[0]), compare_stats); 10033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 10133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner /* trim 10% to remove outliers */ 10233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner int min_index = count*0.05; 10333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner int max_index = count - min_index; 10433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner if (max_index >= count) 10533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner max_index = count-1; 10633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 10733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner count = (max_index - min_index)+1; 10833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 10933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner /* the median is the center item */ 11033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner median = stats[(min_index+max_index)/2]; 11133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 11233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner /* the minimum is the first, the max the last */ 11333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner min = stats[min_index]; 11433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner max = stats[max_index]; 11533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 11633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner /* compute the average */ 11733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner int nn; 11833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner int64_t total = 0; 11933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner for (nn = min_index; nn <= max_index; nn++) { 12033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner total += stats[nn]; 12133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner } 12233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 12333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner printf("BENCH: %5.1f %5.1f %5.1f, %s\n", 12433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner min*1./SUBCOUNT, 12533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner max*1./SUBCOUNT, 12633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner median*1./SUBCOUNT, 12733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner statement); 12833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner if (0) { 12933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner for (nn = min_index; nn <= max_index; nn++) { 13033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner printf(" %lld", (long long)stats[nn]); 13133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner } 13233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner printf("\n"); 13333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner } 13433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner} 13533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 13633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#define BENCH_COUNT(stmnt,total) do { \ 13733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner int64_t count = total; \ 13833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner int num_stats = 0; \ 13933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner int64_t bench_start = now_ns(); \ 14033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner while (num_stats < MAX_STATS && count >= SUBCOUNT) { \ 14133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner int tries = SUBCOUNT; \ 14233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner int64_t sub_start = now_ns(); \ 14333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner count -= tries; \ 14433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner for ( ; tries > 0; tries-- ) {\ 14533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner stmnt;\ 14633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner }\ 14733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner int64_t sub_end = now_ns(); \ 14833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner stats[num_stats++] = sub_end - sub_start; \ 14933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner if (sub_end - bench_start >= MAX_WAIT_MS*1e6) \ 15033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner break; \ 15133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner } \ 15233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner filter_stats(num_stats, #stmnt); \ 15333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner } while (0) 15433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 15533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#define DEFAULT_COUNT 10000000 15633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 15733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner#define BENCH(stmnt) BENCH_COUNT(stmnt,DEFAULT_COUNT) 15833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 15933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner/* Will be called by pthread_once() for benchmarking */ 16033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turnerstatic void _dummy_init(void) 16133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner{ 16233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner /* nothing */ 16333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner} 16433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 16533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner/* Used when creating the key */ 16633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turnerstatic void key_destroy(void* param) 16733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner{ 16833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner /* nothing */ 16933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner} 17033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 17133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turnerint main(void) 17233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner{ 17333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner pthread_once_t once = PTHREAD_ONCE_INIT; 17433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner pthread_once(&once, _dummy_init); 17533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 17633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner pthread_key_t key; 17733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner pthread_key_create(&key, key_destroy); 17833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner pthread_setspecific(key, (void*)(int)100); 17933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 18033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner BENCH(getpid()); 18133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner BENCH(pthread_self()); 18233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner BENCH(pthread_getspecific(key)); 18333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner BENCH(pthread_once(&once, _dummy_init)); 18433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 18533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 18633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner BENCH(pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex)); 18733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 18833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner pthread_mutex_t errorcheck_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER; 18933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner BENCH(pthread_mutex_lock(&errorcheck_mutex); pthread_mutex_unlock(&errorcheck_mutex)); 19033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 19133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner pthread_mutex_t recursive_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; 19233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner BENCH(pthread_mutex_lock(&recursive_mutex); pthread_mutex_unlock(&recursive_mutex)); 19333cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 19433cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner /* TODO: Benchmark pshared mutexes */ 19533cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner 19633cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner sem_t semaphore; 19733cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner int dummy; 19833cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner sem_init(&semaphore, 1, 1); 19933cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner BENCH(sem_getvalue(&semaphore,&dummy)); 20033cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner BENCH(sem_wait(&semaphore); sem_post(&semaphore)); 20133cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner return 0; 20233cf0fee9523f7c796a71f42d2031a532351bb88David 'Digit' Turner} 203