]>
Commit | Line | Data |
---|---|---|
1 | ||
2 | // Copyright (c) 2008 Peter Dimov | |
3 | // | |
4 | // Distributed under the Boost Software License, Version 1.0. | |
5 | // See accompanying file LICENSE_1_0.txt or copy at | |
6 | // http://www.boost.org/LICENSE_1_0.txt | |
7 | ||
8 | #include <boost/config.hpp> | |
9 | ||
10 | #include <boost/shared_ptr.hpp> | |
11 | #include <boost/bind.hpp> | |
12 | ||
13 | #include <boost/thread/shared_mutex.hpp> | |
14 | #include <boost/thread/locks.hpp> | |
15 | ||
16 | #include <boost/detail/lightweight_mutex.hpp> | |
17 | #include <boost/detail/lightweight_thread.hpp> | |
18 | ||
19 | #include <vector> | |
20 | #include <numeric> | |
21 | #include <cstdio> | |
22 | #include <cstdlib> | |
23 | #include <cstddef> | |
24 | #include <ctime> | |
25 | ||
26 | // | |
27 | ||
28 | static void next_value( unsigned & v ) | |
29 | { | |
30 | v = v % 2? 3 * v + 1: v / 2; | |
31 | } | |
32 | ||
33 | struct X | |
34 | { | |
35 | std::vector<unsigned> v_; | |
36 | ||
37 | explicit X( std::size_t n ): v_( n ) | |
38 | { | |
39 | for( std::size_t i = 0; i < n; ++i ) | |
40 | { | |
41 | v_[ i ] = i; | |
42 | } | |
43 | } | |
44 | ||
45 | unsigned get() const | |
46 | { | |
47 | return std::accumulate( v_.begin(), v_.end(), 0 ); | |
48 | } | |
49 | ||
50 | void set() | |
51 | { | |
52 | std::for_each( v_.begin(), v_.end(), next_value ); | |
53 | } | |
54 | }; | |
55 | ||
56 | static boost::shared_ptr<X> ps; | |
57 | ||
58 | static boost::detail::lightweight_mutex lm; | |
59 | static boost::shared_mutex rw; | |
60 | ||
61 | enum prim_type | |
62 | { | |
63 | pt_mutex, | |
64 | pt_rwlock, | |
65 | pt_atomics | |
66 | }; | |
67 | ||
68 | int read_access( prim_type pt ) | |
69 | { | |
70 | switch( pt ) | |
71 | { | |
72 | case pt_mutex: | |
73 | { | |
74 | boost::detail::lightweight_mutex::scoped_lock lock( lm ); | |
75 | return ps->get(); | |
76 | } | |
77 | ||
78 | case pt_rwlock: | |
79 | { | |
80 | boost::shared_lock<boost::shared_mutex> lock( rw ); | |
81 | return ps->get(); | |
82 | } | |
83 | ||
84 | case pt_atomics: | |
85 | { | |
86 | boost::shared_ptr<X> p2 = boost::atomic_load( &ps ); | |
87 | return p2->get(); | |
88 | } | |
89 | } | |
90 | } | |
91 | ||
92 | void write_access( prim_type pt ) | |
93 | { | |
94 | switch( pt ) | |
95 | { | |
96 | case pt_mutex: | |
97 | { | |
98 | boost::detail::lightweight_mutex::scoped_lock lock( lm ); | |
99 | ps->set(); | |
100 | } | |
101 | break; | |
102 | ||
103 | case pt_rwlock: | |
104 | { | |
105 | boost::unique_lock<boost::shared_mutex> lock( rw ); | |
106 | ps->set(); | |
107 | } | |
108 | break; | |
109 | ||
110 | case pt_atomics: | |
111 | { | |
112 | boost::shared_ptr<X> p1 = boost::atomic_load( &ps ); | |
113 | ||
114 | for( ;; ) | |
115 | { | |
116 | boost::shared_ptr<X> p2( new X( *p1 ) ); | |
117 | p2->set(); | |
118 | ||
119 | if( boost::atomic_compare_exchange( &ps, &p1, p2 ) ) break; | |
120 | } | |
121 | } | |
122 | break; | |
123 | } | |
124 | } | |
125 | ||
126 | void worker( int k, prim_type pt, int n, int r ) | |
127 | { | |
128 | ++r; | |
129 | ||
130 | unsigned s = 0, nr = 0, nw = 0; | |
131 | ||
132 | for( int i = 0; i < n; ++i ) | |
133 | { | |
134 | if( i % r ) | |
135 | { | |
136 | s += read_access( pt ); | |
137 | ++nr; | |
138 | } | |
139 | else | |
140 | { | |
141 | write_access( pt ); | |
142 | ++s; | |
143 | ++nw; | |
144 | } | |
145 | } | |
146 | ||
147 | printf( "Worker %2d: %u:%u, %10u\n", k, nr, nw, s ); | |
148 | } | |
149 | ||
150 | #if defined( BOOST_HAS_PTHREADS ) | |
151 | char const * thmodel = "POSIX"; | |
152 | #else | |
153 | char const * thmodel = "Windows"; | |
154 | #endif | |
155 | ||
156 | char const * pt_to_string( prim_type pt ) | |
157 | { | |
158 | switch( pt ) | |
159 | { | |
160 | case pt_mutex: | |
161 | ||
162 | return "mutex"; | |
163 | ||
164 | case pt_rwlock: | |
165 | ||
166 | return "rwlock"; | |
167 | ||
168 | case pt_atomics: | |
169 | ||
170 | return "atomics"; | |
171 | } | |
172 | } | |
173 | ||
174 | static void handle_pt_option( std::string const & opt, prim_type & pt, prim_type pt2 ) | |
175 | { | |
176 | if( opt == pt_to_string( pt2 ) ) | |
177 | { | |
178 | pt = pt2; | |
179 | } | |
180 | } | |
181 | ||
182 | static void handle_int_option( std::string const & opt, std::string const & prefix, int & k, int kmin, int kmax ) | |
183 | { | |
184 | if( opt.substr( 0, prefix.size() ) == prefix ) | |
185 | { | |
186 | int v = atoi( opt.substr( prefix.size() ).c_str() ); | |
187 | ||
188 | if( v >= kmin && v <= kmax ) | |
189 | { | |
190 | k = v; | |
191 | } | |
192 | } | |
193 | } | |
194 | ||
195 | int main( int ac, char const * av[] ) | |
196 | { | |
197 | using namespace std; // printf, clock_t, clock | |
198 | ||
199 | int m = 4; // threads | |
200 | int n = 10000; // vector size | |
201 | int k = 1000000; // iterations | |
202 | int r = 100; // read/write ratio, r:1 | |
203 | ||
204 | prim_type pt = pt_atomics; | |
205 | ||
206 | for( int i = 1; i < ac; ++i ) | |
207 | { | |
208 | handle_pt_option( av[i], pt, pt_mutex ); | |
209 | handle_pt_option( av[i], pt, pt_rwlock ); | |
210 | handle_pt_option( av[i], pt, pt_atomics ); | |
211 | ||
212 | handle_int_option( av[i], "n=", n, 1, INT_MAX ); | |
213 | handle_int_option( av[i], "size=", n, 1, INT_MAX ); | |
214 | ||
215 | handle_int_option( av[i], "k=", k, 1, INT_MAX ); | |
216 | handle_int_option( av[i], "iterations=", k, 1, INT_MAX ); | |
217 | ||
218 | handle_int_option( av[i], "m=", m, 1, INT_MAX ); | |
219 | handle_int_option( av[i], "threads=", m, 1, INT_MAX ); | |
220 | ||
221 | handle_int_option( av[i], "r=", r, 1, INT_MAX ); | |
222 | handle_int_option( av[i], "ratio=", r, 1, INT_MAX ); | |
223 | } | |
224 | ||
225 | printf( "%s: threads=%d size=%d iterations=%d ratio=%d %s\n\n", thmodel, m, n, k, r, pt_to_string( pt ) ); | |
226 | ||
227 | ps.reset( new X( n ) ); | |
228 | ||
229 | clock_t t = clock(); | |
230 | ||
231 | std::vector<pthread_t> a( m ); | |
232 | ||
233 | for( int i = 0; i < m; ++i ) | |
234 | { | |
235 | boost::detail::lw_thread_create( a[ i ], boost::bind( worker, i, pt, k, r ) ); | |
236 | } | |
237 | ||
238 | for( int j = 0; j < m; ++j ) | |
239 | { | |
240 | pthread_join( a[ j ], 0 ); | |
241 | } | |
242 | ||
243 | t = clock() - t; | |
244 | ||
245 | double ts = static_cast<double>( t ) / CLOCKS_PER_SEC; | |
246 | printf( "%.3f seconds, %.3f accesses per microsecond.\n", ts, m * k / ts / 1e+6 ); | |
247 | } |