• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
getRelativeTimeNsec(void)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 
incr(void)97 static void incr(void)
98 {
99     incTest++;
100 }
decr(void)101 static void decr(void)
102 {
103     decTest--;
104 }
add(int32_t addVal)105 static void add(int32_t addVal)
106 {
107     addTest += addVal;
108 }
compareAndSwap(int32_t oldVal,int32_t newVal,int32_t * addr)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 }
compareAndSwapWide(int64_t oldVal,int64_t newVal,int64_t * addr)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  */
doAtomicTest(int num)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  */
atomicTest(void * arg)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 */
testAtomicSpeedSub(int repeatCount)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 
testAtomicSpeed(void)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  */
dvmTestAtomicSpeed(void)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