1 | #include "processor_list.hpp" |
---|
2 | |
---|
3 | #include <array> |
---|
4 | #include <iomanip> |
---|
5 | #include <iostream> |
---|
6 | #include <locale> |
---|
7 | #include <string> |
---|
8 | #include <thread> |
---|
9 | |
---|
10 | #include "utils.hpp" |
---|
11 | |
---|
12 | unsigned num() { |
---|
13 | return 0x1000000; |
---|
14 | } |
---|
15 | |
---|
16 | //------------------- |
---|
17 | |
---|
18 | struct processor { |
---|
19 | unsigned id; |
---|
20 | }; |
---|
21 | void run(unsigned nthread, double duration, unsigned writes, unsigned epochs) { |
---|
22 | assert(writes < 100); |
---|
23 | |
---|
24 | // List being tested |
---|
25 | processor_list list = {}; |
---|
26 | |
---|
27 | // Barrier for synchronization |
---|
28 | barrier_t barrier(nthread + 1); |
---|
29 | |
---|
30 | // Data to check everything is OK |
---|
31 | size_t write_committed = 0ul; |
---|
32 | struct { |
---|
33 | std::atomic_size_t write = { 0ul }; |
---|
34 | std::atomic_size_t read = { 0ul }; |
---|
35 | std::atomic_size_t epoch = { 0ul }; |
---|
36 | } lock_cnt; |
---|
37 | |
---|
38 | // Flag to signal termination |
---|
39 | std::atomic_bool done = { false }; |
---|
40 | |
---|
41 | std::thread * threads[nthread]; |
---|
42 | unsigned i = 1; |
---|
43 | for(auto & t : threads) { |
---|
44 | t = new std::thread([&done, &list, &barrier, &write_committed, &lock_cnt, writes, epochs](unsigned tid) { |
---|
45 | Random rand(tid + rdtscl()); |
---|
46 | processor proc; |
---|
47 | proc.id = list.doregister(&proc); |
---|
48 | size_t writes_cnt = 0; |
---|
49 | size_t reads_cnt = 0; |
---|
50 | size_t epoch_cnt = 0; |
---|
51 | |
---|
52 | affinity(tid); |
---|
53 | |
---|
54 | barrier.wait(tid); |
---|
55 | |
---|
56 | while(__builtin_expect(!done, true)) { |
---|
57 | auto r = rand.next() % 100; |
---|
58 | if (r < writes) { |
---|
59 | auto n = list.write_lock(); |
---|
60 | write_committed++; |
---|
61 | writes_cnt++; |
---|
62 | assert(writes_cnt < -2ul); |
---|
63 | list.write_unlock(n); |
---|
64 | } |
---|
65 | else if(r < epochs) { |
---|
66 | list.epoch_check(); |
---|
67 | epoch_cnt++; |
---|
68 | } |
---|
69 | else { |
---|
70 | list.read_lock(proc.id); |
---|
71 | reads_cnt++; |
---|
72 | assert(reads_cnt < -2ul); |
---|
73 | list.read_unlock(proc.id); |
---|
74 | } |
---|
75 | } |
---|
76 | |
---|
77 | barrier.wait(tid); |
---|
78 | |
---|
79 | auto p = list.unregister(proc.id); |
---|
80 | assert(&proc == p); |
---|
81 | lock_cnt.write += writes_cnt; |
---|
82 | lock_cnt.read += reads_cnt; |
---|
83 | lock_cnt.epoch += epoch_cnt; |
---|
84 | }, i++); |
---|
85 | } |
---|
86 | |
---|
87 | auto before = Clock::now(); |
---|
88 | barrier.wait(0); |
---|
89 | |
---|
90 | while(true) { |
---|
91 | usleep(1000); |
---|
92 | auto now = Clock::now(); |
---|
93 | duration_t durr = now - before; |
---|
94 | if( durr.count() > duration ) { |
---|
95 | done = true; |
---|
96 | break; |
---|
97 | } |
---|
98 | } |
---|
99 | |
---|
100 | barrier.wait(0); |
---|
101 | auto after = Clock::now(); |
---|
102 | duration_t durr = after - before; |
---|
103 | duration = durr.count(); |
---|
104 | |
---|
105 | for(auto t : threads) { |
---|
106 | t->join(); |
---|
107 | delete t; |
---|
108 | } |
---|
109 | |
---|
110 | assert(write_committed == lock_cnt.write); |
---|
111 | |
---|
112 | size_t totalop = lock_cnt.read + lock_cnt.write + lock_cnt.epoch; |
---|
113 | size_t ops_sec = size_t(double(totalop) / duration); |
---|
114 | size_t ops_thread = ops_sec / nthread; |
---|
115 | double dur_nano = duration_cast<std::nano>(1.0); |
---|
116 | |
---|
117 | std::cout << "Duration : " << duration << "s\n"; |
---|
118 | std::cout << "Total ops : " << totalop << "(" << lock_cnt.read << "r, " << lock_cnt.write << "w, " << lock_cnt.epoch << "e)\n"; |
---|
119 | std::cout << "Ops/sec : " << ops_sec << "\n"; |
---|
120 | std::cout << "Ops/sec/thread: " << ops_thread << "\n"; |
---|
121 | std::cout << "ns/Op : " << ( dur_nano / ops_thread )<< "\n"; |
---|
122 | } |
---|
123 | |
---|
124 | void usage(char * argv[]) { |
---|
125 | std::cerr << argv[0] << ": [DURATION (FLOAT:SEC)] [NTHREADS] [%WRITES]" << std::endl;; |
---|
126 | std::exit(1); |
---|
127 | } |
---|
128 | |
---|
129 | int main(int argc, char * argv[]) { |
---|
130 | |
---|
131 | double duration = 5.0; |
---|
132 | unsigned nthreads = 2; |
---|
133 | unsigned writes = 0; |
---|
134 | unsigned epochs = 0; |
---|
135 | |
---|
136 | std::cout.imbue(std::locale("")); |
---|
137 | |
---|
138 | switch (argc) |
---|
139 | { |
---|
140 | case 5: |
---|
141 | epochs = std::stoul(argv[4]); |
---|
142 | [[fallthrough]]; |
---|
143 | case 4: |
---|
144 | writes = std::stoul(argv[3]); |
---|
145 | if( (writes + epochs) > 100 ) { |
---|
146 | std::cerr << "Writes + Epochs must be valid percentage, was " << argv[3] << " + " << argv[4] << "(" << writes << " + " << epochs << ")" << std::endl; |
---|
147 | usage(argv); |
---|
148 | } |
---|
149 | [[fallthrough]]; |
---|
150 | case 3: |
---|
151 | nthreads = std::stoul(argv[2]); |
---|
152 | [[fallthrough]]; |
---|
153 | case 2: |
---|
154 | duration = std::stod(argv[1]); |
---|
155 | if( duration <= 0.0 ) { |
---|
156 | std::cerr << "Duration must be positive, was " << argv[1] << "(" << duration << ")" << std::endl; |
---|
157 | usage(argv); |
---|
158 | } |
---|
159 | [[fallthrough]]; |
---|
160 | case 1: |
---|
161 | break; |
---|
162 | default: |
---|
163 | usage(argv); |
---|
164 | break; |
---|
165 | } |
---|
166 | |
---|
167 | check_cache_line_size(); |
---|
168 | |
---|
169 | std::cout << "Running " << nthreads << " threads for " << duration << " seconds with " << writes << "% writes and " << epochs << "% epochs" << std::endl; |
---|
170 | run(nthreads, duration, writes, epochs + writes); |
---|
171 | |
---|
172 | return 0; |
---|
173 | } |
---|