這裡以多執行緒操作long型別變數,進行加法運算1億次的時間作為效能對比的標準。
測試使用SLES 11SP2作業系統,3.0.80核心,CPU使用Xeon 5506(2 socket, 4 cores, 1thread)
由於針對64位型別的atomic glibc沒有提供相應的庫,將核心實現程式碼移植到應用層
atomic64.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
#ifndef __HI_ATOMIC64_H__ #define __HI_ATOMIC64_H__ #include <stdio.h> #include <getopt.h> #include <limits.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <sys/time.h> #include <arpa/inet.h> #include <fcntl.h> #include <signal.h> #include <errno.h> #include <sys/time.h> /* Learn from kernel */ #ifdef __x86_64__ #define LOCK_PREFIX "lock ;" typedef struct { long long counter; } atomic64_t; /** * atomic64_read - read atomic64 variable * @v: pointer of type atomic64_t * * Atomically reads the value of <a href="http://www.jobbole.com/members/q1317827412">@v.</a> * Doesn't imply a read memory barrier. */ static inline long atomic64_read(const atomic64_t *v) { return (*(volatile long *)&(v)->counter); } /** * atomic64_set - set atomic64 variable * @v: pointer to type atomic64_t * @i: required value * * Atomically sets the value of <a href="http://www.jobbole.com/members/shoujiliuyi6455">@v</a> to @i. */ static inline void atomic64_set(atomic64_t *v, long i) { v->counter = i; } /** * atomic64_add - add integer to atomic64 variable * @i: integer value to add * @v: pointer to type atomic64_t * * Atomically adds <a href="http://www.jobbole.com/members/zhoann">@i</a> to <a href="http://www.jobbole.com/members/q1317827412">@v.</a> */ static inline void atomic64_add(long i, atomic64_t *v) { asm volatile(LOCK_PREFIX "addq %1,%0" : "=m" (v->counter) : "er" (i), "m" (v->counter)); } /** * atomic64_sub - subtract the atomic64 variable * @i: integer value to subtract * @v: pointer to type atomic64_t * * Atomically subtracts <a href="http://www.jobbole.com/members/zhoann">@i</a> from <a href="http://www.jobbole.com/members/q1317827412">@v.</a> */ static inline void atomic64_sub(long i, atomic64_t *v) { asm volatile(LOCK_PREFIX "subq %1,%0" : "=m" (v->counter) : "er" (i), "m" (v->counter)); } #else /* __x86_64__ */ /*FIXME: * This program will run on x86_64 machine in the expected future, we * do _not_ need to care other cpu architecture. */ #endif #endif |
測試程式碼performance.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
/******************************************************************************* Copyright(c) 2008-2014 This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Date: 2014-04-15 21:27:57 CST Contact Information: Tony <tingw.liu@gmail.com> Home, Qingdao, China. *******************************************************************************/ #include "atomic64.h" atomic64_t num; long mutexnum = 0; long maxnum; struct timeval tv; long starttime; //FIXME: gettimeofday is a non-thread safe sysycall static pthread_mutex_t timelock = PTHREAD_MUTEX_INITIALIZER; #define TIME_LOCK() pthread_mutex_lock(&timelock) #define TIME_UNLOCK() pthread_mutex_unlock(&timelock) static pthread_mutex_t numlock = PTHREAD_MUTEX_INITIALIZER; #define MUTEX_LOCK() pthread_mutex_lock(&numlock) #define MUTEX_UNLOCK() pthread_mutex_unlock(&numlock) static pthread_rwlock_t rwnumlock = PTHREAD_RWLOCK_INITIALIZER; #define RW_LOCK() pthread_rwlock_wrlock(&rwnumlock) #define RW_UNLOCK() pthread_rwlock_unlock(&rwnumlock); static void * add_func(void *arg) { long stoptime; while(1) { atomic64_add(1, &num); if (atomic64_read(&num) > maxnum) { TIME_LOCK(); gettimeofday(&tv, 0); TIME_UNLOCK(); stoptime= (long)tv.tv_sec * (long)1000000 + (long)tv.tv_usec; printf("Used %ld microseconds\n", stoptime - starttime); break; } } } static void *add_func_rwlock(void *arg) { long stoptime; while(1) { RW_LOCK(); ++mutexnum; if (mutexnum > maxnum) { RW_UNLOCK(); TIME_LOCK(); gettimeofday(&tv, 0); TIME_UNLOCK(); stoptime= (long)tv.tv_sec * (long)1000000 + (long)tv.tv_usec; printf("Used %ld microseconds\n", stoptime - starttime); break; } RW_UNLOCK(); } } static void *add_func_mutex(void *arg) { long stoptime; while(1) { MUTEX_LOCK(); ++mutexnum; if (mutexnum > maxnum) { MUTEX_UNLOCK(); TIME_LOCK(); gettimeofday(&tv, 0); TIME_UNLOCK(); stoptime= (long)tv.tv_sec * (long)1000000 + (long)tv.tv_usec; printf("Used %ld microseconds\n", stoptime - starttime); break; } MUTEX_UNLOCK(); } } #define ATOMIC_TYPE 0 #define MUTEX_TYPE 1 #define RW_TYPE 2 int main(int argc, char **argv) { pthread_t thread; pthread_attr_t thread_attr; int threadnum, i, type; if (argc != 4) { printf("Usage: %s threadnum maxnum type[0-atomic, 1-mutex, 2-rwlock]\n", argv[0]); exit(0); } threadnum = atoi(argv[1]); maxnum = atoll(argv[2]); type = atoi(argv[3]); printf("Use %d threads add num from 0 to %ld\n", threadnum, maxnum); gettimeofday(&tv, 0); starttime= (long)tv.tv_sec * (long)1000000 + (long)tv.tv_usec; atomic64_set(&num, 0); pthread_attr_init(&thread_attr); //pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); for (i = 0; i < threadnum; i++) { if (type == ATOMIC_TYPE) pthread_create(&thread, &thread_attr, add_func, 0); else if (type == MUTEX_TYPE) pthread_create(&thread, &thread_attr, add_func_mutex, 0); else if (type == RW_TYPE) pthread_create(&thread, &thread_attr, add_func_rwlock, 0); } while(1) sleep(10); } |
執行結果這裡就不詳細列出來了,可以直接看這個曲線圖。
橫座標是執行緒數,縱座標是運算1億次耗費的微秒數。
從這個圖中可以看出,對於頻繁寫操作的情況atomic > mutex > rwlock
對於同一種同步型別,並不是隨著執行緒數的增加而一直增加,不過因為只有8個核心,所以沒有測試更多執行緒的情況。