source: doc/theses/thierry_delisle_PhD/code/readyQ_proto/bts.cpp @ 433e2c3

Last change on this file since 433e2c3 was f9f3775, checked in by Thierry Delisle <tdelisle@…>, 4 years ago

Moved phd code for the readQ prototype to it's own folder

  • Property mode set to 100644
File size: 6.8 KB
Line 
1#include <array>
2#include <iomanip>
3#include <iostream>
4#include <locale>
5#include <string>
6#include <thread>
7#include <vector>
8
9#include <getopt.h>
10#include <unistd.h>
11#include <sys/sysinfo.h>
12
13#include "utils.hpp"
14
15// ================================================================================================
16//                        UTILS
17// ================================================================================================
18
19struct local_stat_t {
20        size_t cnt = 0;
21};
22
23struct global_stat_t {
24        std::atomic_size_t cnt = { 0 };
25};
26
27void atomic_max(std::atomic_size_t & target, size_t value) {
28        for(;;) {
29                size_t expect = target.load(std::memory_order_relaxed);
30                if(value <= expect) return;
31                bool success = target.compare_exchange_strong(expect, value);
32                if(success) return;
33        }
34}
35
36void atomic_min(std::atomic_size_t & target, size_t value) {
37        for(;;) {
38                size_t expect = target.load(std::memory_order_relaxed);
39                if(value >= expect) return;
40                bool success = target.compare_exchange_strong(expect, value);
41                if(success) return;
42        }
43}
44
45void tally_stats(global_stat_t & global, local_stat_t & local) {
46        global.cnt   += local.cnt;
47}
48
49void waitfor(double & duration, barrier_t & barrier, std::atomic_bool & done) {
50        std::cout << "Starting" << std::endl;
51        auto before = Clock::now();
52        barrier.wait(0);
53
54        while(true) {
55                usleep(100000);
56                auto now = Clock::now();
57                duration_t durr = now - before;
58                if( durr.count() > duration ) {
59                        done = true;
60                        break;
61                }
62                std::cout << "\r" << std::setprecision(4) << durr.count();
63                std::cout.flush();
64        }
65
66        barrier.wait(0);
67        auto after = Clock::now();
68        duration_t durr = after - before;
69        duration = durr.count();
70        std::cout << "\rClosing down" << std::endl;
71}
72
73void waitfor(double & duration, barrier_t & barrier, const std::atomic_size_t & count) {
74        std::cout << "Starting" << std::endl;
75        auto before = Clock::now();
76        barrier.wait(0);
77
78        while(true) {
79                usleep(100000);
80                size_t c = count.load();
81                if( c == 0 ) {
82                        break;
83                }
84                std::cout << "\r" << c;
85                std::cout.flush();
86        }
87
88        barrier.wait(0);
89        auto after = Clock::now();
90        duration_t durr = after - before;
91        duration = durr.count();
92        std::cout << "\rClosing down" << std::endl;
93}
94
95void print_stats(double duration, unsigned nthread, global_stat_t & global) {
96        std::cout << "Done" << std::endl;
97
98        size_t ops = global.cnt;
99        size_t ops_sec = size_t(double(ops) / duration);
100        size_t ops_thread = ops_sec / nthread;
101        auto dur_nano = duration_cast<std::nano>(1.0);
102
103        std::cout << "Duration      : " << duration << "s\n";
104        std::cout << "ns/Op         : " << ( dur_nano / ops_thread )<< "\n";
105        std::cout << "Ops/sec/thread: " << ops_thread << "\n";
106        std::cout << "Ops/sec       : " << ops_sec << "\n";
107        std::cout << "Total ops     : " << ops << "\n";
108}
109
110static inline bool bts(std::atomic_size_t & target, size_t bit ) {
111        /*
112        int result = 0;
113        asm volatile(
114                "LOCK btsq %[bit], %[target]\n\t"
115                :"=@ccc" (result)
116                : [target] "m" (target), [bit] "r" (bit)
117        );
118        return result != 0;
119        /*/
120        size_t mask = 1ul << bit;
121        size_t ret = target.fetch_or(mask, std::memory_order_relaxed);
122        return (ret & mask) != 0;
123        //*/
124}
125
126static inline bool btr(std::atomic_size_t & target, size_t bit ) {
127        /*
128        int result = 0;
129        asm volatile(
130                "LOCK btrq %[bit], %[target]\n\t"
131                :"=@ccc" (result)
132                : [target] "m" (target), [bit] "r" (bit)
133        );
134        return result != 0;
135        /*/
136        size_t mask = 1ul << bit;
137        size_t ret = target.fetch_and(~mask, std::memory_order_relaxed);
138        return (ret & mask) != 0;
139        //*/
140}
141
142// ================================================================================================
143//                        EXPERIMENTS
144// ================================================================================================
145
146// ================================================================================================
147__attribute__((noinline)) void runPingPong_body(
148        std::atomic<bool>& done,
149        local_stat_t & local,
150        std::atomic_size_t & target,
151        size_t id
152) {
153        while(__builtin_expect(!done.load(std::memory_order_relaxed), true)) {
154
155                bool ret;
156                ret = bts(target, id);
157                assert(!ret);
158
159                // -----
160
161                ret = btr(target, id);
162                assert(ret);
163                local.cnt++;
164        }
165}
166
167void run(unsigned nthread, double duration) {
168        // Barrier for synchronization
169        barrier_t barrier(nthread + 1);
170
171        // Data to check everything is OK
172        global_stat_t global;
173
174        // Flag to signal termination
175        std::atomic_bool done  = { false };
176
177        std::cout << "Initializing ";
178        // List being tested
179        std::atomic_size_t word = { 0 };
180        {
181                std::thread * threads[nthread];
182                unsigned i = 1;
183                for(auto & t : threads) {
184                        t = new std::thread([&done, &word, &barrier, &global](unsigned tid) {
185                                local_stat_t local;
186
187                                // affinity(tid);
188
189                                barrier.wait(tid);
190
191                                // EXPERIMENT START
192
193                                runPingPong_body(done, local, word, tid - 1);
194
195                                // EXPERIMENT END
196
197                                barrier.wait(tid);
198
199                                tally_stats(global, local);
200                        }, i++);
201                }
202
203                waitfor(duration, barrier, done);
204
205                for(auto t : threads) {
206                        t->join();
207                        delete t;
208                }
209        }
210
211        print_stats(duration, nthread, global);
212}
213
214// ================================================================================================
215
216int main(int argc, char * argv[]) {
217
218        double duration   = 5.0;
219        unsigned nthreads = 2;
220
221        std::cout.imbue(std::locale(""));
222
223        for(;;) {
224                static struct option options[] = {
225                        {"duration",  required_argument, 0, 'd'},
226                        {"nthreads",  required_argument, 0, 't'},
227                        {0, 0, 0, 0}
228                };
229
230                int idx = 0;
231                int opt = getopt_long(argc, argv, "d:t:", options, &idx);
232
233                std::string arg = optarg ? optarg : "";
234                size_t len = 0;
235                switch(opt) {
236                        case -1:
237                                if(optind != argc) {
238                                        std::cerr << "Too many arguments " << argc << " " << idx << std::endl;
239                                        goto usage;
240                                }
241                                goto run;
242                        // Numeric Arguments
243                        case 'd':
244                                try {
245                                        duration = std::stod(optarg, &len);
246                                        if(len != arg.size()) { throw std::invalid_argument(""); }
247                                } catch(std::invalid_argument &) {
248                                        std::cerr << "Duration must be a valid double, was " << arg << std::endl;
249                                        goto usage;
250                                }
251                                break;
252                        case 't':
253                                try {
254                                        nthreads = std::stoul(optarg, &len);
255                                        if(len != arg.size() || nthreads > (8 * sizeof(size_t))) { throw std::invalid_argument(""); }
256                                } catch(std::invalid_argument &) {
257                                        std::cerr << "Number of threads must be a positive integer less than or equal to " << sizeof(size_t) * 8 << ", was " << arg << std::endl;
258                                        goto usage;
259                                }
260                                break;
261                        // Other cases
262                        default: /* ? */
263                                std::cerr << opt << std::endl;
264                        usage:
265                                std::cerr << "Usage: " << argv[0] << ": [options]" << std::endl;
266                                std::cerr << std::endl;
267                                std::cerr << "  -d, --duration=DURATION  Duration of the experiment, in seconds" << std::endl;
268                                std::cerr << "  -t, --nthreads=NTHREADS  Number of kernel threads" << std::endl;
269                                std::exit(1);
270                }
271        }
272        run:
273
274        check_cache_line_size();
275
276        std::cout << "Running " << nthreads << " threads for " << duration << " seconds" << std::endl;
277        run(nthreads, duration);
278        return 0;
279}
Note: See TracBrowser for help on using the repository browser.