Home | History | Annotate | Download | only in test
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /*
     18  * This provides a handful of correctness and speed tests on our atomic
     19  * operations.
     20  *
     21  * This doesn't really belong here, but we currently lack a better place
     22  * for it, so this will do for now.
     23  */
     24 #include "Dalvik.h"
     25 
     26 #include <stdlib.h>
     27 #include <stdio.h>
     28 #include <pthread.h>
     29 #include <unistd.h>
     30 #include <cutils/atomic.h>
     31 #ifdef __arm__
     32 # include <machine/cpu-features.h>
     33 #endif
     34 
     35 #define USE_ATOMIC      1
     36 #define THREAD_COUNT    10
     37 #define ITERATION_COUNT 500000
     38 
     39 #ifdef HAVE_ANDROID_OS
     40 /*#define TEST_BIONIC 1*/
     41 #endif
     42 
     43 
     44 #ifdef TEST_BIONIC
     45 extern int __atomic_cmpxchg(int old, int _new, volatile int *ptr);
     46 extern int __atomic_swap(int _new, volatile int *ptr);
     47 extern int __atomic_dec(volatile int *ptr);
     48 extern int __atomic_inc(volatile int *ptr);
     49 #endif
     50 
     51 static pthread_mutex_t waitLock = PTHREAD_MUTEX_INITIALIZER;
     52 static pthread_cond_t waitCond = PTHREAD_COND_INITIALIZER;
     53 
     54 static volatile int threadsStarted = 0;
     55 
     56 /* results */
     57 static int incTest = 0;
     58 static int decTest = 0;
     59 static int addTest = 0;
     60 static int andTest = 0;
     61 static int orTest = 0;
     62 static int casTest = 0;
     63 static int failingCasTest = 0;
     64 static int swapTest = 0;
     65 static int64_t wideCasTest = 0x6600000077000000LL;
     66 
     67 /*
     68  * Get a relative time value.
     69  */
     70 static int64_t getRelativeTimeNsec(void)
     71 {
     72 #define HAVE_POSIX_CLOCKS
     73 #ifdef HAVE_POSIX_CLOCKS
     74     struct timespec now;
     75     clock_gettime(CLOCK_MONOTONIC, &now);
     76     return (int64_t) now.tv_sec*1000000000LL + now.tv_nsec;
     77 #else
     78     struct timeval now;
     79     gettimeofday(&now, NULL);
     80     return (int64_t) now.tv_sec*1000000000LL + now.tv_usec * 1000LL;
     81 #endif
     82 }
     83 
     84 
     85 /*
     86  * Non-atomic implementations, for comparison.
     87  *
     88  * If these get inlined the compiler may figure out what we're up to and
     89  * completely elide the operations.
     90  */
     91 static void incr(void) __attribute__((noinline));
     92 static void decr(void) __attribute__((noinline));
     93 static void add(int addVal) __attribute__((noinline));
     94 static int compareAndSwap(int oldVal, int newVal, int* addr) __attribute__((noinline));
     95 static int compareAndSwapWide(int64_t oldVal, int64_t newVal, int64_t* addr) __attribute__((noinline));
     96 
     97 static void incr(void)
     98 {
     99     incTest++;
    100 }
    101 static void decr(void)
    102 {
    103     decTest--;
    104 }
    105 static void add(int32_t addVal)
    106 {
    107     addTest += addVal;
    108 }
    109 static int compareAndSwap(int32_t oldVal, int32_t newVal, int32_t* addr)
    110 {
    111     if (*addr == oldVal) {
    112         *addr = newVal;
    113         return 0;
    114     }
    115     return 1;
    116 }
    117 static int compareAndSwapWide(int64_t oldVal, int64_t newVal, int64_t* addr)
    118 {
    119     if (*addr == oldVal) {
    120         *addr = newVal;
    121         return 0;
    122     }
    123     return 1;
    124 }
    125 
    126 /*
    127  * Exercise several of the atomic ops.
    128  */
    129 static void doAtomicTest(int num)
    130 {
    131     int addVal = (num & 0x01) + 1;
    132 
    133     int i;
    134     for (i = 0; i < ITERATION_COUNT; i++) {
    135         if (USE_ATOMIC) {
    136             android_atomic_inc(&incTest);
    137             android_atomic_dec(&decTest);
    138             android_atomic_add(addVal, &addTest);
    139 
    140             int val;
    141             do {
    142                 val = casTest;
    143             } while (android_atomic_release_cas(val, val+3, &casTest) != 0);
    144             do {
    145                 val = casTest;
    146             } while (android_atomic_acquire_cas(val, val-1, &casTest) != 0);
    147 
    148             int64_t wval;
    149             do {
    150                 wval = dvmQuasiAtomicRead64(&wideCasTest);
    151             } while (dvmQuasiAtomicCas64(wval,
    152                         wval + 0x0000002000000001LL, &wideCasTest) != 0);
    153             do {
    154                 wval = dvmQuasiAtomicRead64(&wideCasTest);
    155             } while (dvmQuasiAtomicCas64(wval,
    156                         wval - 0x0000002000000001LL, &wideCasTest) != 0);
    157         } else {
    158             incr();
    159             decr();
    160             add(addVal);
    161 
    162             int val;
    163             do {
    164                 val = casTest;
    165             } while (compareAndSwap(val, val+3, &casTest) != 0);
    166             do {
    167                 val = casTest;
    168             } while (compareAndSwap(val, val-1, &casTest) != 0);
    169 
    170             int64_t wval;
    171             do {
    172                 wval = wideCasTest;
    173             } while (compareAndSwapWide(wval,
    174                         wval + 0x0000002000000001LL, &wideCasTest) != 0);
    175             do {
    176                 wval = wideCasTest;
    177             } while (compareAndSwapWide(wval,
    178                         wval - 0x0000002000000001LL, &wideCasTest) != 0);
    179         }
    180     }
    181 }
    182 
    183 /*
    184  * Entry point for multi-thread test.
    185  */
    186 static void* atomicTest(void* arg)
    187 {
    188     pthread_mutex_lock(&waitLock);
    189     threadsStarted++;
    190     pthread_cond_wait(&waitCond, &waitLock);
    191     pthread_mutex_unlock(&waitLock);
    192 
    193     doAtomicTest((int) arg);
    194 
    195     return NULL;
    196 }
    197 
    198 /* lifted from a VM test */
    199 static int64_t testAtomicSpeedSub(int repeatCount)
    200 {
    201     static int value = 7;
    202     int* valuePtr = &value;
    203     int64_t start, end;
    204     int i;
    205 
    206     start = getRelativeTimeNsec();
    207 
    208     for (i = repeatCount / 10; i != 0; i--) {
    209         if (USE_ATOMIC) {
    210             // succeed 10x
    211             (void) android_atomic_release_cas(7, 7, valuePtr);
    212             (void) android_atomic_release_cas(7, 7, valuePtr);
    213             (void) android_atomic_release_cas(7, 7, valuePtr);
    214             (void) android_atomic_release_cas(7, 7, valuePtr);
    215             (void) android_atomic_release_cas(7, 7, valuePtr);
    216             (void) android_atomic_release_cas(7, 7, valuePtr);
    217             (void) android_atomic_release_cas(7, 7, valuePtr);
    218             (void) android_atomic_release_cas(7, 7, valuePtr);
    219             (void) android_atomic_release_cas(7, 7, valuePtr);
    220             (void) android_atomic_release_cas(7, 7, valuePtr);
    221         } else {
    222             // succeed 10x
    223             compareAndSwap(7, 7, valuePtr);
    224             compareAndSwap(7, 7, valuePtr);
    225             compareAndSwap(7, 7, valuePtr);
    226             compareAndSwap(7, 7, valuePtr);
    227             compareAndSwap(7, 7, valuePtr);
    228             compareAndSwap(7, 7, valuePtr);
    229             compareAndSwap(7, 7, valuePtr);
    230             compareAndSwap(7, 7, valuePtr);
    231             compareAndSwap(7, 7, valuePtr);
    232             compareAndSwap(7, 7, valuePtr);
    233         }
    234     }
    235 
    236     end = getRelativeTimeNsec();
    237 
    238     dvmFprintf(stdout, ".");
    239     fflush(stdout);
    240     return end - start;
    241 }
    242 
    243 static void testAtomicSpeed(void)
    244 {
    245     static const int kIterations = 10;
    246     static const int kRepeatCount = 5 * 1000 * 1000;
    247     static const int kDelay = 50 * 1000;
    248     int64_t results[kIterations];
    249     int i;
    250 
    251     for (i = 0; i < kIterations; i++) {
    252         results[i] = testAtomicSpeedSub(kRepeatCount);
    253         usleep(kDelay);
    254     }
    255 
    256     dvmFprintf(stdout, "\n");
    257     dvmFprintf(stdout, "%s speed test results (%d per iteration):\n",
    258         USE_ATOMIC ? "Atomic" : "Non-atomic", kRepeatCount);
    259     for (i = 0; i < kIterations; i++) {
    260         dvmFprintf(stdout,
    261             " %2d: %.3fns\n", i, (double) results[i] / kRepeatCount);
    262     }
    263 }
    264 
    265 /*
    266  * Start tests, show results.
    267  */
    268 bool dvmTestAtomicSpeed(void)
    269 {
    270     pthread_t threads[THREAD_COUNT];
    271     void *(*startRoutine)(void*) = atomicTest;
    272     int64_t startWhen, endWhen;
    273 
    274 #if defined(__ARM_ARCH__)
    275     dvmFprintf(stdout, "__ARM_ARCH__ is %d\n", __ARM_ARCH__);
    276 #endif
    277 #if defined(ANDROID_SMP)
    278     dvmFprintf(stdout, "ANDROID_SMP is %d\n", ANDROID_SMP);
    279 #endif
    280     dvmFprintf(stdout, "Creating threads\n");
    281 
    282     int i;
    283     for (i = 0; i < THREAD_COUNT; i++) {
    284         void* arg = (void*) i;
    285         if (pthread_create(&threads[i], NULL, startRoutine, arg) != 0) {
    286             dvmFprintf(stderr, "thread create failed\n");
    287         }
    288     }
    289 
    290     /* wait for all the threads to reach the starting line */
    291     while (1) {
    292         pthread_mutex_lock(&waitLock);
    293         if (threadsStarted == THREAD_COUNT) {
    294             dvmFprintf(stdout, "Starting test\n");
    295             startWhen = getRelativeTimeNsec();
    296             pthread_cond_broadcast(&waitCond);
    297             pthread_mutex_unlock(&waitLock);
    298             break;
    299         }
    300         pthread_mutex_unlock(&waitLock);
    301         usleep(100000);
    302     }
    303 
    304     for (i = 0; i < THREAD_COUNT; i++) {
    305         void* retval;
    306         if (pthread_join(threads[i], &retval) != 0) {
    307             dvmFprintf(stderr, "thread join (%d) failed\n", i);
    308         }
    309     }
    310 
    311     endWhen = getRelativeTimeNsec();
    312     dvmFprintf(stdout, "All threads stopped, time is %.6fms\n",
    313         (endWhen - startWhen) / 1000000.0);
    314 
    315     /*
    316      * Show results; expecting:
    317      *
    318      * incTest = 5000000
    319      * decTest = -5000000
    320      * addTest = 7500000
    321      * casTest = 10000000
    322      * wideCasTest = 0x6600000077000000
    323      */
    324     dvmFprintf(stdout, "incTest = %d\n", incTest);
    325     dvmFprintf(stdout, "decTest = %d\n", decTest);
    326     dvmFprintf(stdout, "addTest = %d\n", addTest);
    327     dvmFprintf(stdout, "casTest = %d\n", casTest);
    328     dvmFprintf(stdout, "wideCasTest = 0x%llx\n", wideCasTest);
    329 
    330     /* do again, serially (SMP check) */
    331     startWhen = getRelativeTimeNsec();
    332     for (i = 0; i < THREAD_COUNT; i++) {
    333         doAtomicTest(i);
    334     }
    335     endWhen = getRelativeTimeNsec();
    336     dvmFprintf(stdout, "Same iterations done serially: time is %.6fms\n",
    337         (endWhen - startWhen) / 1000000.0);
    338 
    339     /*
    340      * Hard to do a meaningful thrash test on these, so just do a simple
    341      * function test.
    342      */
    343     andTest = 0xffd7fa96;
    344     orTest = 0x122221ff;
    345     swapTest = 0x11111111;
    346     android_atomic_and(0xfffdaf96, &andTest);
    347     android_atomic_or(0xdeaaeb00, &orTest);
    348     int oldSwap = android_atomic_swap(0x22222222, &swapTest);
    349     int oldSwap2 = android_atomic_swap(0x33333333, &swapTest);
    350     if (android_atomic_release_cas(failingCasTest+1, failingCasTest-1,
    351             &failingCasTest) == 0)
    352         dvmFprintf(stdout, "failing test did not fail!\n");
    353 
    354     dvmFprintf(stdout, "andTest = 0x%x\n", andTest);
    355     dvmFprintf(stdout, "orTest = 0x%x\n", orTest);
    356     dvmFprintf(stdout, "swapTest = 0x%x -> 0x%x\n", oldSwap, oldSwap2);
    357     dvmFprintf(stdout, "swapTest = 0x%x -> 0x%x\n", oldSwap2, swapTest);
    358     dvmFprintf(stdout, "failingCasTest = %d\n", failingCasTest);
    359 
    360 #ifdef TEST_BIONIC
    361     /*
    362      * Quick function test on the bionic ops.
    363      */
    364     int prev;
    365     int tester = 7;
    366     prev = __atomic_inc(&tester);
    367     __atomic_inc(&tester);
    368     __atomic_inc(&tester);
    369     dvmFprintf(stdout, "bionic 3 inc: %d -> %d\n", prev, tester);
    370     prev = __atomic_dec(&tester);
    371     __atomic_dec(&tester);
    372     __atomic_dec(&tester);
    373     dvmFprintf(stdout, "bionic 3 dec: %d -> %d\n", prev, tester);
    374     prev = __atomic_swap(27, &tester);
    375     dvmFprintf(stdout, "bionic swap: %d -> %d\n", prev, tester);
    376     int swapok = __atomic_cmpxchg(27, 72, &tester);
    377     dvmFprintf(stdout, "bionic cmpxchg: %d (%d)\n", tester, swapok);
    378 #endif
    379 
    380     testAtomicSpeed();
    381 
    382     return 0;
    383 }
    384