source: doc/theses/thierry_delisle_PhD/code/readQ_example/proto-gui/main.cpp @ af5baae

Last change on this file since af5baae was 72b1800, checked in by Thierry Delisle <tdelisle@…>, 4 years ago

Some previous in progress work.
Commited only so it stops showing in my git status.

  • Property mode set to 100644
File size: 7.1 KB
Line 
1#include "thrdlib/thread.hpp"
2
3#include <cassert>
4
5#include <algorithm>
6#include <atomic>
7#include <iostream>
8#include <memory>
9#include <vector>
10
11#include <getopt.h>
12using thrdlib::thread_t;
13
14//--------------------
15// Constants
16unsigned nframes;
17unsigned fsize;
18unsigned nproduce;
19
20//--------------------
21// Frame management
22
23class Frame {
24        static const thread_t reset;
25        static const thread_t set;
26        std::atomic<thread_t> rdy_state = { reset };
27        std::atomic<thread_t> rnd_state = { set };
28public:
29        unsigned number;
30        std::unique_ptr<unsigned char[]> data;
31
32private:
33        inline bool wait( thread_t self, std::atomic<thread_t> & state, std::atomic<thread_t> & other ) {
34                bool ret;
35                while(true) {
36                        thread_t expected = state;
37                        if( expected == set ) { ret = false; goto END; }
38                        assert( expected == reset );
39                        if( std::atomic_compare_exchange_strong( &state, &expected, self) ) {
40                                thrdlib::park( self );
41                                ret = true;
42                                goto END;
43                        }
44                }
45                END:
46                assert( state == set );
47                assert( other != set );
48                state = reset;
49                return ret;
50        }
51
52        inline bool publish(  std::atomic<thread_t> & state ) {
53                thread_t got = std::atomic_exchange( &state, set );
54                assert( got != set );
55
56                if( got == reset ) return false;
57
58                thrdlib::unpark( got );
59                return true;
60        }
61
62public:
63        inline bool wait_rendered( thread_t self ) {
64                return wait( self, rnd_state, rdy_state );
65        }
66
67        inline bool wait_ready   ( thread_t self ) {
68                return wait( self, rdy_state, rnd_state );
69        }
70
71        inline bool publish() {
72                return publish( rdy_state );
73        }
74
75        inline bool release() {
76                return publish( rnd_state );
77        }
78};
79
80const thread_t Frame::reset = nullptr;
81const thread_t Frame::set   = reinterpret_cast<thread_t>(1);
82
83std::unique_ptr<Frame[]> frames;
84volatile unsigned last_produced = 0;
85
86//--------------------
87// Threads
88thread_t volatile the_stats_thread = nullptr;
89
90inline void fence(void) {
91        std::atomic_thread_fence(std::memory_order_seq_cst);
92}
93
94struct {
95        struct {
96                volatile unsigned long long   parks = 0;
97                volatile unsigned long long unparks = 0;
98        } sim;
99        struct {
100                volatile unsigned long long   parks = 0;
101                volatile unsigned long long unparks = 0;
102        } rend;
103
104        struct {
105                volatile unsigned long long ran = 0;
106                volatile unsigned long long saw = 0;
107        } stats;
108} thrd_stats;
109
110void Stats( thread_t self ) {
111        the_stats_thread = self;
112        fence();
113        thrdlib::park( self );
114
115        std::vector<bool> seen;
116        seen.resize(nproduce, false);
117
118        while(last_produced < nproduce) {
119                thrdlib::yield();
120                thrd_stats.stats.ran++;
121                if( last_produced > 0 ) seen.at(last_produced - 1) = true;
122        }
123
124        thrd_stats.stats.saw = std::count(seen.begin(), seen.end(), true);
125}
126
127typedef uint64_t __wyhash64_state_t;
128static inline uint64_t __wyhash64( __wyhash64_state_t & state ) {
129        state += 0x60bee2bee120fc15;
130        __uint128_t tmp;
131        tmp = (__uint128_t) state * 0xa3b195354a39b70d;
132        uint64_t m1 = (tmp >> 64) ^ tmp;
133        tmp = (__uint128_t)m1 * 0x1b03738712fad5c9;
134        uint64_t m2 = (tmp >> 64) ^ tmp;
135        return m2;
136}
137
138void Simulator( thread_t self ) {
139        for(unsigned i = 0; i < nproduce; i++) {
140                auto & frame = frames[i % nframes];
141                // Wait for the frames to be rendered
142                if( frame.wait_rendered( self ) ) {
143                        thrd_stats.sim.parks++;
144                }
145
146                __wyhash64_state_t state = 0;
147
148                // Write the frame information
149                frame.number = i;
150                for( unsigned x = 0; x < fsize; x++ ) {
151                        frame.data[x] = __wyhash64(state);
152                }
153                std::cout << "Simulated " << i << std::endl;
154                last_produced = i+1;
155
156                // Publish it
157                if( frame.publish()  ) {
158                        thrd_stats.sim.unparks++;
159                }
160        }
161}
162
163void Renderer( thread_t self ) {
164        thrdlib::unpark( the_stats_thread );
165        for(unsigned i = 0; i < nproduce; i++) {
166                auto & frame = frames[i % nframes];
167                // Wait for the frames to be ready
168                if( frame.wait_ready( self ) ) {
169                        thrd_stats.rend.parks++;
170                }
171
172                // Render the frame
173                unsigned total = 0;
174                for( unsigned x = 0; x < fsize; x++ ) {
175                        total += frame.data[x];
176                }
177
178                std::cout << "Rendered " << i << std::endl;
179                // assert(total == i * fsize);
180
181                // Release
182                if( frame.release() ) {
183                        thrd_stats.rend.unparks++;
184                }
185        }
186
187}
188
189
190
191int main(int argc, char * argv[]) {
192        nframes  = 3;
193        fsize    = 3840 * 2160 * 4 * 4;
194        nproduce = 60;
195
196        for(;;) {
197                static struct option options[] = {
198                        {"buff",  required_argument, 0, 'b'},
199                        {"nprod",  required_argument, 0, 'p'},
200                        {"fsize",   required_argument, 0, 'f'},
201                        {0, 0, 0, 0}
202                };
203
204                int idx = 0;
205                int opt = getopt_long(argc, argv, "b:p:f:", options, &idx);
206
207                std::string arg = optarg ? optarg : "";
208                size_t len = 0;
209                switch(opt) {
210                        // Exit Case
211                        case -1:
212                                goto run;
213                        case 'b':
214                                try {
215                                        nframes = std::stoul(optarg, &len);
216                                        if(nframes == 0 || len != arg.size()) { throw std::invalid_argument(""); }
217                                } catch(std::invalid_argument &) {
218                                        std::cerr << "Number of buffered frames must be at least 1, was" << arg << std::endl;
219                                        goto usage;
220                                }
221                                break;
222                        case 'p':
223                                try {
224                                        nproduce = std::stoul(optarg, &len);
225                                        if(nproduce == 0 || len != arg.size()) { throw std::invalid_argument(""); }
226                                } catch(std::invalid_argument &) {
227                                        std::cerr << "Number of produced frames must be at least 1, was" << arg << std::endl;
228                                        goto usage;
229                                }
230                                break;
231                        case 'f':
232                                try {
233                                        fsize = std::stoul(optarg, &len);
234                                        if(fsize == 0 || len != arg.size()) { throw std::invalid_argument(""); }
235                                } catch(std::invalid_argument &) {
236                                        std::cerr << "Size of produced frames must be at least 1, was" << arg << std::endl;
237                                        goto usage;
238                                }
239                                break;
240                        // Other cases
241                        default: /* ? */
242                                std::cerr << opt << std::endl;
243                        usage:
244                                std::cerr << "Usage: " << argv[0] << " [options]" << std::endl;
245                                std::cerr << std::endl;
246                                std::cerr << "  -b, --buff=COUNT    Number of frames to buffer" << std::endl;
247                                std::cerr << "  -p, --nprod=COUNT   Number of frames to produce" << std::endl;
248                                std::cerr << "  -f, --fsize=SIZE    Size of each frame in bytes" << std::endl;
249                                std::exit(1);
250                }
251        }
252        run:
253        frames.reset(new Frame[nframes]);
254        for(unsigned i = 0; i < nframes; i++) {
255                frames[i].number = 0;
256                frames[i].data.reset(new unsigned char[fsize]);
257        }
258        std::cout << "Created frames of " << fsize << " bytes" << std::endl;
259        std::cout << "(Buffering " << nframes << ")" << std::endl;
260
261        thrdlib::init( 2 );
262
263        thread_t stats     = thrdlib::create( Stats );
264        std::cout << "Created Stats Thread" << std::endl;
265        while( the_stats_thread == nullptr ) thrdlib::yield();
266
267        std::cout << "Creating Main Threads" << std::endl;
268        thread_t renderer  = thrdlib::create( Renderer  );
269        thread_t simulator = thrdlib::create( Simulator );
270
271        std::cout << "Running" << std::endl;
272
273        thrdlib::join( simulator );
274        thrdlib::join( renderer  );
275        thrdlib::join( stats     );
276
277        thrdlib::clean();
278
279        std::cout << "----------" << std::endl;
280        std::cout << "# Parks" << std::endl;
281        std::cout << "  Renderer   park: " << thrd_stats. sim.  parks << std::endl;
282        std::cout << "  Renderer unpark: " << thrd_stats. sim.unparks << std::endl;
283        std::cout << " Simulator   park: " << thrd_stats.rend.  parks << std::endl;
284        std::cout << " Simulator unpark: " << thrd_stats.rend.unparks << std::endl;
285
286        std::cout << "Stats thread" << std::endl;
287        std::cout << " Ran             : " << thrd_stats.stats.ran << " times" << std::endl;
288        std::cout << " Saw             : " << thrd_stats.stats.saw << " (" << ((100.f * thrd_stats.stats.saw) / nproduce) << "%)" << std::endl;
289}
Note: See TracBrowser for help on using the repository browser.