source: doc/theses/thierry_delisle_PhD/code/relaxed_list.cpp @ 64e9fef

ADTarm-ehast-experimentalenumforall-pointer-decayjacob/cs343-translationnew-astnew-ast-unique-exprpthread-emulationqualifiedEnum
Last change on this file since 64e9fef was 16ee228, checked in by Thierry Delisle <tdelisle@…>, 4 years ago

Changed benchmark to only print progress if in a tty

  • Property mode set to 100644
File size: 26.4 KB
Line 
1#include "relaxed_list.hpp"
2
3#include <array>
4#include <iomanip>
5#include <iostream>
6#include <locale>
7#include <string>
8#include <thread>
9#include <vector>
10
11#include <getopt.h>
12#include <unistd.h>
13#include <sys/sysinfo.h>
14
15#include "utils.hpp"
16
17struct __attribute__((aligned(64))) Node {
18        static std::atomic_size_t creates;
19        static std::atomic_size_t destroys;
20
21        _LinksFields_t<Node> _links;
22
23        int value;
24        int id;
25
26        Node() { creates++; }
27        Node(int value): value(value) { creates++; }
28        ~Node() { destroys++; }
29};
30
31std::atomic_size_t Node::creates  = { 0 };
32std::atomic_size_t Node::destroys = { 0 };
33
34bool enable_stats = false;
35
36template<>
37thread_local relaxed_list<Node>::TLS relaxed_list<Node>::tls = {};
38
39template<>
40relaxed_list<Node> * relaxed_list<Node>::head = nullptr;
41
42#ifndef NO_STATS
43template<>
44relaxed_list<Node>::GlobalStats relaxed_list<Node>::global_stats = {};
45#endif
46
47// ================================================================================================
48//                        UTILS
49// ================================================================================================
50
51struct local_stat_t {
52        size_t in  = 0;
53        size_t out = 0;
54        size_t empty = 0;
55        size_t crc_in  = 0;
56        size_t crc_out = 0;
57        size_t valmax = 0;
58        size_t valmin = 100000000ul;
59        struct {
60                size_t val = 0;
61                size_t cnt = 0;
62        } comp;
63        struct {
64                size_t val = 0;
65                size_t cnt = 0;
66        } subm;
67};
68
69struct global_stat_t {
70        std::atomic_size_t in  = { 0 };
71        std::atomic_size_t out = { 0 };
72        std::atomic_size_t empty = { 0 };
73        std::atomic_size_t crc_in  = { 0 };
74        std::atomic_size_t crc_out = { 0 };
75        std::atomic_size_t valmax = { 0 };
76        std::atomic_size_t valmin = { 100000000ul };
77        struct {
78                std::atomic_size_t val = { 0 };
79                std::atomic_size_t cnt = { 0 };
80        } comp;
81        struct {
82                std::atomic_size_t val = { 0 };
83                std::atomic_size_t cnt = { 0 };
84        } subm;
85};
86
87void atomic_max(std::atomic_size_t & target, size_t value) {
88        for(;;) {
89                size_t expect = target.load(std::memory_order_relaxed);
90                if(value <= expect) return;
91                bool success = target.compare_exchange_strong(expect, value);
92                if(success) return;
93        }
94}
95
96void atomic_min(std::atomic_size_t & target, size_t value) {
97        for(;;) {
98                size_t expect = target.load(std::memory_order_relaxed);
99                if(value >= expect) return;
100                bool success = target.compare_exchange_strong(expect, value);
101                if(success) return;
102        }
103}
104
105void tally_stats(global_stat_t & global, local_stat_t & local) {
106
107        global.in    += local.in;
108        global.out   += local.out;
109        global.empty += local.empty;
110
111        global.crc_in  += local.crc_in;
112        global.crc_out += local.crc_out;
113
114        global.comp.val += local.comp.val;
115        global.comp.cnt += local.comp.cnt;
116        global.subm.val += local.subm.val;
117        global.subm.cnt += local.subm.cnt;
118
119        atomic_max(global.valmax, local.valmax);
120        atomic_min(global.valmin, local.valmin);
121
122        relaxed_list<Node>::stats_tls_tally();
123}
124
125void waitfor(double & duration, barrier_t & barrier, std::atomic_bool & done) {
126        std::cout << "Starting" << std::endl;
127        auto before = Clock::now();
128        barrier.wait(0);
129        bool is_tty = isatty(STDOUT_FILENO);
130
131        while(true) {
132                usleep(100000);
133                auto now = Clock::now();
134                duration_t durr = now - before;
135                if( durr.count() > duration ) {
136                        done = true;
137                        break;
138                }
139                if(is_tty) {
140                        std::cout << "\r" << std::setprecision(4) << durr.count();
141                        std::cout.flush();
142                }
143        }
144
145        barrier.wait(0);
146        auto after = Clock::now();
147        duration_t durr = after - before;
148        duration = durr.count();
149        std::cout << "\rClosing down" << std::endl;
150}
151
152void waitfor(double & duration, barrier_t & barrier, const std::atomic_size_t & count) {
153        std::cout << "Starting" << std::endl;
154        auto before = Clock::now();
155        barrier.wait(0);
156
157        while(true) {
158                usleep(100000);
159                size_t c = count.load();
160                if( c == 0 ) {
161                        break;
162                }
163                std::cout << "\r" << c;
164                std::cout.flush();
165        }
166
167        barrier.wait(0);
168        auto after = Clock::now();
169        duration_t durr = after - before;
170        duration = durr.count();
171        std::cout << "\rClosing down" << std::endl;
172}
173
174void print_stats(double duration, unsigned nthread, global_stat_t & global) {
175        assert(Node::creates == Node::destroys);
176        assert(global.crc_in == global.crc_out);
177
178        std::cout << "Done" << std::endl;
179
180        size_t ops = global.in + global.out;
181        size_t ops_sec = size_t(double(ops) / duration);
182        size_t ops_thread = ops_sec / nthread;
183        auto dur_nano = duration_cast<std::nano>(1.0);
184
185        if(global.valmax != 0) {
186                std::cout << "Max runs      : " << global.valmax << "\n";
187                std::cout << "Min runs      : " << global.valmin << "\n";
188        }
189        if(global.comp.cnt != 0) {
190                std::cout << "Submit count  : " << global.subm.cnt << "\n";
191                std::cout << "Submit average: " << ((double(global.subm.val)) / global.subm.cnt) << "\n";
192                std::cout << "Complete count: " << global.comp.cnt << "\n";
193                std::cout << "Complete avg  : " << ((double(global.comp.val)) / global.comp.cnt) << "\n";
194        }
195        std::cout << "Duration      : " << duration << "s\n";
196        std::cout << "ns/Op         : " << ( dur_nano / ops_thread )<< "\n";
197        std::cout << "Ops/sec/thread: " << ops_thread << "\n";
198        std::cout << "Ops/sec       : " << ops_sec << "\n";
199        std::cout << "Total ops     : " << ops << "(" << global.in << "i, " << global.out << "o, " << global.empty << "e)\n";
200        #ifndef NO_STATS
201                relaxed_list<Node>::stats_print(std::cout);
202        #endif
203}
204
205void save_fairness(const int data[], int factor, unsigned nthreads, size_t columns, size_t rows, const std::string & output);
206
207// ================================================================================================
208//                        EXPERIMENTS
209// ================================================================================================
210
211// ================================================================================================
212__attribute__((noinline)) void runChurn_body(
213        std::atomic<bool>& done,
214        Random & rand,
215        Node * my_nodes[],
216        unsigned nslots,
217        local_stat_t & local,
218        relaxed_list<Node> & list
219) {
220        while(__builtin_expect(!done.load(std::memory_order_relaxed), true)) {
221                int idx = rand.next() % nslots;
222                if (auto node = my_nodes[idx]) {
223                        local.crc_in += node->value;
224                        list.push(node);
225                        my_nodes[idx] = nullptr;
226                        local.in++;
227                }
228                else if(auto node = list.pop()) {
229                        local.crc_out += node->value;
230                        my_nodes[idx] = node;
231                        local.out++;
232                }
233                else {
234                        local.empty++;
235                }
236        }
237}
238
239void runChurn(unsigned nthread, unsigned nqueues, double duration, unsigned nnodes, const unsigned nslots) {
240        std::cout << "Churn Benchmark" << std::endl;
241        assert(nnodes <= nslots);
242        // List being tested
243
244        // Barrier for synchronization
245        barrier_t barrier(nthread + 1);
246
247        // Data to check everything is OK
248        global_stat_t global;
249
250        // Flag to signal termination
251        std::atomic_bool done  = { false };
252
253        // Prep nodes
254        std::cout << "Initializing ";
255        size_t npushed = 0;
256        relaxed_list<Node> list = { nthread * nqueues };
257        {
258                Node** all_nodes[nthread];
259                for(auto & nodes : all_nodes) {
260                        nodes = new __attribute__((aligned(64))) Node*[nslots + 8];
261                        Random rand(rdtscl());
262                        for(unsigned i = 0; i < nnodes; i++) {
263                                nodes[i] = new Node(rand.next() % 100);
264                        }
265
266                        for(unsigned i = nnodes; i < nslots; i++) {
267                                nodes[i] = nullptr;
268                        }
269
270                        for(int i = 0; i < 10 && i < (int)nslots; i++) {
271                                int idx = rand.next() % nslots;
272                                if (auto node = nodes[idx]) {
273                                        global.crc_in += node->value;
274                                        list.push(node);
275                                        npushed++;
276                                        nodes[idx] = nullptr;
277                                }
278                        }
279                }
280
281                std::cout << nnodes << " nodes (" << nslots << " slots)" << std::endl;
282
283                enable_stats = true;
284
285                std::thread * threads[nthread];
286                unsigned i = 1;
287                for(auto & t : threads) {
288                        auto & my_nodes = all_nodes[i - 1];
289                        t = new std::thread([&done, &list, &barrier, &global, &my_nodes, nslots](unsigned tid) {
290                                Random rand(tid + rdtscl());
291
292                                local_stat_t local;
293
294                                // affinity(tid);
295
296                                barrier.wait(tid);
297
298                                // EXPERIMENT START
299
300                                runChurn_body(done, rand, my_nodes, nslots, local, list);
301
302                                // EXPERIMENT END
303
304                                barrier.wait(tid);
305
306                                tally_stats(global, local);
307
308                                for(unsigned i = 0; i < nslots; i++) {
309                                        delete my_nodes[i];
310                                }
311                        }, i++);
312                }
313
314                waitfor(duration, barrier, done);
315
316                for(auto t : threads) {
317                        t->join();
318                        delete t;
319                }
320
321                enable_stats = false;
322
323                while(auto node = list.pop()) {
324                        global.crc_out += node->value;
325                        delete node;
326                }
327
328                for(auto nodes : all_nodes) {
329                        delete[] nodes;
330                }
331        }
332
333        print_stats(duration, nthread, global);
334}
335
336// ================================================================================================
337__attribute__((noinline)) void runPingPong_body(
338        std::atomic<bool>& done,
339        Node initial_nodes[],
340        unsigned nnodes,
341        local_stat_t & local,
342        relaxed_list<Node> & list
343) {
344        Node * nodes[nnodes];
345        {
346                unsigned i = 0;
347                for(auto & n : nodes) {
348                        n = &initial_nodes[i++];
349                }
350        }
351
352        while(__builtin_expect(!done.load(std::memory_order_relaxed), true)) {
353
354                for(Node * & node : nodes) {
355                        local.crc_in += node->value;
356                        list.push(node);
357                        local.in++;
358                }
359
360                // -----
361
362                for(Node * & node : nodes) {
363                        node = list.pop();
364                        assert(node);
365                        local.crc_out += node->value;
366                        local.out++;
367                }
368        }
369}
370
371void runPingPong(unsigned nthread, unsigned nqueues, double duration, unsigned nnodes) {
372        std::cout << "PingPong Benchmark" << std::endl;
373
374
375        // Barrier for synchronization
376        barrier_t barrier(nthread + 1);
377
378        // Data to check everything is OK
379        global_stat_t global;
380
381        // Flag to signal termination
382        std::atomic_bool done  = { false };
383
384        std::cout << "Initializing ";
385        // List being tested
386        relaxed_list<Node> list = { nthread * nqueues };
387        {
388                enable_stats = true;
389
390                std::thread * threads[nthread];
391                unsigned i = 1;
392                for(auto & t : threads) {
393                        t = new std::thread([&done, &list, &barrier, &global, nnodes](unsigned tid) {
394                                Random rand(tid + rdtscl());
395
396                                Node nodes[nnodes];
397                                for(auto & n : nodes) {
398                                        n.value = (int)rand.next() % 100;
399                                }
400
401                                local_stat_t local;
402
403                                // affinity(tid);
404
405                                barrier.wait(tid);
406
407                                // EXPERIMENT START
408
409                                runPingPong_body(done, nodes, nnodes, local, list);
410
411                                // EXPERIMENT END
412
413                                barrier.wait(tid);
414
415                                tally_stats(global, local);
416                        }, i++);
417                }
418
419                waitfor(duration, barrier, done);
420
421                for(auto t : threads) {
422                        t->join();
423                        delete t;
424                }
425
426                enable_stats = false;
427        }
428
429        print_stats(duration, nthread, global);
430}
431
432// ================================================================================================
433struct __attribute__((aligned(64))) Slot {
434        Node * volatile node;
435};
436
437__attribute__((noinline)) void runProducer_body(
438        std::atomic<bool>& done,
439        Random & rand,
440        Slot * slots,
441        int nslots,
442        local_stat_t & local,
443        relaxed_list<Node> & list
444) {
445        while(__builtin_expect(!done.load(std::memory_order_relaxed), true)) {
446
447                Node * node = list.pop();
448                if(!node) {
449                        local.empty ++;
450                        continue;
451                }
452
453                local.crc_out += node->value;
454                local.out++;
455
456                if(node->id == 0) {
457                        unsigned cnt = 0;
458                        for(int i = 0; i < nslots; i++) {
459                                Node * found = __atomic_exchange_n( &slots[i].node, nullptr, __ATOMIC_SEQ_CST );
460                                if( found ) {
461                                        local.crc_in += found->value;
462                                        local.in++;
463                                        cnt++;
464                                        list.push( found );
465                                }
466                        }
467
468                        local.crc_in += node->value;
469                        local.in++;
470                        list.push( node );
471
472                        local.comp.cnt++;
473                        local.comp.val += cnt;
474                }
475                else {
476                        unsigned len = 0;
477                        while(true) {
478                                auto off = rand.next();
479                                for(int i = 0; i < nslots; i++) {
480                                        Node * expected = nullptr;
481                                        int idx = (i + off) % nslots;
482                                        Slot & slot = slots[ idx ];
483                                        if(
484                                                slot.node == nullptr &&
485                                                __atomic_compare_exchange_n( &slot.node, &expected, node, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST )
486                                        ) {
487                                                local.subm.cnt++;
488                                                local.subm.val += len;
489                                                goto LOOP;
490                                        }
491                                        assert( expected != node );
492                                        len++;
493                                }
494                        }
495                }
496
497                LOOP:;
498        }
499}
500
501void runProducer(unsigned nthread, unsigned nqueues, double duration, unsigned nnodes) {
502        std::cout << "Producer Benchmark" << std::endl;
503
504        // Barrier for synchronization
505        barrier_t barrier(nthread + 1);
506
507        // Data to check everything is OK
508        global_stat_t global;
509
510        // Flag to signal termination
511        std::atomic_bool done  = { false };
512
513        std::cout << "Initializing ";
514
515        int nslots = nnodes * 4;
516        Slot * slots = new Slot[nslots];
517        std::cout << nnodes << " nodes (" << nslots << " slots)" << std::endl;
518
519        // List being tested
520        relaxed_list<Node> list = { nthread * nqueues };
521        {
522                Random rand(rdtscl());
523                for(unsigned i = 0; i < nnodes; i++) {
524                        Node * node = new Node(rand.next() % 100);
525                        node->id = i;
526                        global.crc_in += node->value;
527                        list.push(node);
528                }
529
530                for(int i = 0; i < nslots; i++) {
531                        slots[i].node = nullptr;
532                }
533        }
534
535        {
536                enable_stats = true;
537
538                std::thread * threads[nthread];
539                unsigned i = 1;
540                for(auto & t : threads) {
541                        t = new std::thread([&done, &list, &barrier, &global, slots, nslots](unsigned tid) {
542                                Random rand(tid + rdtscl());
543
544                                local_stat_t local;
545                                barrier.wait(tid);
546
547                                // EXPERIMENT START
548
549                                runProducer_body(done, rand, slots, nslots, local, list);
550
551                                // EXPERIMENT END
552
553                                barrier.wait(tid);
554
555                                tally_stats(global, local);
556                        }, i++);
557                }
558
559                waitfor(duration, barrier, done);
560
561                for(auto t : threads) {
562                        t->join();
563                        delete t;
564                }
565
566                enable_stats = false;
567        }
568
569        {
570                while(Node * node = list.pop()) {
571                        global.crc_out += node->value;
572                        delete node;
573                }
574
575                for(int i = 0; i < nslots; i++) {
576                        delete slots[i].node;
577                }
578
579                delete [] slots;
580        }
581
582        print_stats(duration, nthread, global);
583}
584
585// ================================================================================================
586__attribute__((noinline)) void runFairness_body(
587        unsigned tid,
588        size_t width,
589        size_t length,
590        int output[],
591        std::atomic_size_t & count,
592        Node initial_nodes[],
593        unsigned nnodes,
594        local_stat_t & local,
595        relaxed_list<Node> & list
596) {
597        Node * nodes[nnodes];
598        {
599                unsigned i = 0;
600                for(auto & n : nodes) {
601                        n = &initial_nodes[i++];
602                }
603        }
604
605        while(__builtin_expect(0 != count.load(std::memory_order_relaxed), true)) {
606
607                for(Node * & node : nodes) {
608                        local.crc_in += node->id;
609                        list.push(node);
610                        local.in++;
611                }
612
613                // -----
614
615                for(Node * & node : nodes) {
616                        node = list.pop();
617                        assert(node);
618
619                        if (unsigned(node->value) < length) {
620                                size_t idx = (node->value * width) + node->id;
621                                assert(idx < (width * length));
622                                output[idx] = tid;
623                        }
624
625                        node->value++;
626                        if(unsigned(node->value) == length) count--;
627
628                        local.crc_out += node->id;
629                        local.out++;
630                }
631        }
632}
633
634void runFairness(unsigned nthread, unsigned nqueues, double duration, unsigned nnodes, const std::string & output) {
635        std::cout << "Fairness Benchmark, outputing to : " << output << std::endl;
636
637        // Barrier for synchronization
638        barrier_t barrier(nthread + 1);
639
640        // Data to check everything is OK
641        global_stat_t global;
642
643        std::cout << "Initializing ";
644
645        // Check fairness by creating a png of where the threads ran
646        size_t width = nthread * nnodes;
647        size_t length = 100000;
648
649        std::unique_ptr<int[]> data_out { new int[width * length] };
650
651        // Flag to signal termination
652        std::atomic_size_t count = width;
653
654        // List being tested
655        relaxed_list<Node> list = { nthread * nqueues };
656        {
657                enable_stats = true;
658
659                std::thread * threads[nthread];
660                unsigned i = 1;
661                for(auto & t : threads) {
662                        t = new std::thread([&count, &list, &barrier, &global, nnodes, width, length, data_out = data_out.get()](unsigned tid) {
663                                unsigned int start = (tid - 1) * nnodes;
664                                Node nodes[nnodes];
665                                for(auto & n : nodes) {
666                                        n.id = start;
667                                        n.value = 0;
668                                        start++;
669                                }
670
671                                local_stat_t local;
672
673                                // affinity(tid);
674
675                                barrier.wait(tid);
676
677                                // EXPERIMENT START
678
679                                runFairness_body(tid, width, length, data_out, count, nodes, nnodes, local, list);
680
681                                // EXPERIMENT END
682
683                                barrier.wait(tid);
684
685                                for(const auto & n : nodes) {
686                                        local.valmax = max(local.valmax, size_t(n.value));
687                                        local.valmin = min(local.valmin, size_t(n.value));
688                                }
689
690                                tally_stats(global, local);
691                        }, i++);
692                }
693
694                waitfor(duration, barrier, count);
695
696                for(auto t : threads) {
697                        t->join();
698                        delete t;
699                }
700
701                enable_stats = false;
702        }
703
704        print_stats(duration, nthread, global);
705
706        // save_fairness(data_out.get(), 100, nthread, width, length, output);
707}
708
709// ================================================================================================
710
711bool iequals(const std::string& a, const std::string& b)
712{
713    return std::equal(a.begin(), a.end(),
714                      b.begin(), b.end(),
715                      [](char a, char b) {
716                          return std::tolower(a) == std::tolower(b);
717                      });
718}
719
720int main(int argc, char * argv[]) {
721
722        double duration   = 5.0;
723        unsigned nthreads = 2;
724        unsigned nqueues  = 4;
725        unsigned nnodes   = 100;
726        unsigned nslots   = 100;
727        std::string out   = "fairness.png";
728
729        enum {
730                Churn,
731                PingPong,
732                Producer,
733                Fairness,
734                NONE
735        } benchmark = NONE;
736
737        std::cout.imbue(std::locale(""));
738
739        for(;;) {
740                static struct option options[] = {
741                        {"duration",  required_argument, 0, 'd'},
742                        {"nthreads",  required_argument, 0, 't'},
743                        {"nqueues",   required_argument, 0, 'q'},
744                        {"benchmark", required_argument, 0, 'b'},
745                        {0, 0, 0, 0}
746                };
747
748                int idx = 0;
749                int opt = getopt_long(argc, argv, "d:t:q:b:", options, &idx);
750
751                std::string arg = optarg ? optarg : "";
752                size_t len = 0;
753                switch(opt) {
754                        // Exit Case
755                        case -1:
756                                /* paranoid */ assert(optind <= argc);
757                                switch(benchmark) {
758                                case NONE:
759                                        std::cerr << "Must specify a benchmark" << std::endl;
760                                        goto usage;
761                                case PingPong:
762                                        nnodes = 1;
763                                        switch(argc - optind) {
764                                        case 0: break;
765                                        case 1:
766                                                try {
767                                                        arg = optarg = argv[optind];
768                                                        nnodes = stoul(optarg, &len);
769                                                        if(len != arg.size()) { throw std::invalid_argument(""); }
770                                                } catch(std::invalid_argument &) {
771                                                        std::cerr << "Number of nodes must be a positive integer, was " << arg << std::endl;
772                                                        goto usage;
773                                                }
774                                                break;
775                                        default:
776                                                std::cerr << "'PingPong' benchmark doesn't accept more than 1 extra arguments" << std::endl;
777                                                goto usage;
778                                        }
779                                        break;
780                                case Producer:
781                                        nnodes = 32;
782                                        switch(argc - optind) {
783                                        case 0: break;
784                                        case 1:
785                                                try {
786                                                        arg = optarg = argv[optind];
787                                                        nnodes = stoul(optarg, &len);
788                                                        if(len != arg.size()) { throw std::invalid_argument(""); }
789                                                } catch(std::invalid_argument &) {
790                                                        std::cerr << "Number of nodes must be a positive integer, was " << arg << std::endl;
791                                                        goto usage;
792                                                }
793                                                break;
794                                        default:
795                                                std::cerr << "'Producer' benchmark doesn't accept more than 1 extra arguments" << std::endl;
796                                                goto usage;
797                                        }
798                                        break;
799                                case Churn:
800                                        nnodes = 100;
801                                        nslots = 100;
802                                        switch(argc - optind) {
803                                        case 0: break;
804                                        case 1:
805                                                try {
806                                                        arg = optarg = argv[optind];
807                                                        nnodes = stoul(optarg, &len);
808                                                        if(len != arg.size()) { throw std::invalid_argument(""); }
809                                                        nslots = nnodes;
810                                                } catch(std::invalid_argument &) {
811                                                        std::cerr << "Number of nodes must be a positive integer, was " << arg << std::endl;
812                                                        goto usage;
813                                                }
814                                                break;
815                                        case 2:
816                                                try {
817                                                        arg = optarg = argv[optind];
818                                                        nnodes = stoul(optarg, &len);
819                                                        if(len != arg.size()) { throw std::invalid_argument(""); }
820                                                } catch(std::invalid_argument &) {
821                                                        std::cerr << "Number of nodes must be a positive integer, was " << arg << std::endl;
822                                                        goto usage;
823                                                }
824                                                try {
825                                                        arg = optarg = argv[optind + 1];
826                                                        nslots = stoul(optarg, &len);
827                                                        if(len != arg.size()) { throw std::invalid_argument(""); }
828                                                } catch(std::invalid_argument &) {
829                                                        std::cerr << "Number of slots must be a positive integer, was " << arg << std::endl;
830                                                        goto usage;
831                                                }
832                                                break;
833                                        default:
834                                                std::cerr << "'Churn' benchmark doesn't accept more than 2 extra arguments" << std::endl;
835                                                goto usage;
836                                        }
837                                        break;
838                                case Fairness:
839                                        nnodes = 1;
840                                        switch(argc - optind) {
841                                        case 0: break;
842                                        case 1:
843                                                arg = optarg = argv[optind];
844                                                out = arg;
845                                                break;
846                                        default:
847                                                std::cerr << "'Churn' benchmark doesn't accept more than 2 extra arguments" << std::endl;
848                                                goto usage;
849                                        }
850                                }
851                                goto run;
852                        // Benchmarks
853                        case 'b':
854                                if(benchmark != NONE) {
855                                        std::cerr << "Only when benchmark can be run" << std::endl;
856                                        goto usage;
857                                }
858                                if(iequals(arg, "churn")) {
859                                        benchmark = Churn;
860                                        break;
861                                }
862                                if(iequals(arg, "pingpong")) {
863                                        benchmark = PingPong;
864                                        break;
865                                }
866                                if(iequals(arg, "producer")) {
867                                        benchmark = Producer;
868                                        break;
869                                }
870                                if(iequals(arg, "fairness")) {
871                                        benchmark = Fairness;
872                                        break;
873                                }
874                                std::cerr << "Unkown benchmark " << arg << std::endl;
875                                goto usage;
876                        // Numeric Arguments
877                        case 'd':
878                                try {
879                                        duration = stod(optarg, &len);
880                                        if(len != arg.size()) { throw std::invalid_argument(""); }
881                                } catch(std::invalid_argument &) {
882                                        std::cerr << "Duration must be a valid double, was " << arg << std::endl;
883                                        goto usage;
884                                }
885                                break;
886                        case 't':
887                                try {
888                                        nthreads = stoul(optarg, &len);
889                                        if(len != arg.size()) { throw std::invalid_argument(""); }
890                                } catch(std::invalid_argument &) {
891                                        std::cerr << "Number of threads must be a positive integer, was " << arg << std::endl;
892                                        goto usage;
893                                }
894                                break;
895                        case 'q':
896                                try {
897                                        nqueues = stoul(optarg, &len);
898                                        if(len != arg.size()) { throw std::invalid_argument(""); }
899                                } catch(std::invalid_argument &) {
900                                        std::cerr << "Number of queues must be a positive integer, was " << arg << std::endl;
901                                        goto usage;
902                                }
903                                break;
904                        // Other cases
905                        default: /* ? */
906                                std::cerr << opt << std::endl;
907                        usage:
908                                std::cerr << "Usage: " << argv[0] << ": [options] -b churn [NNODES] [NSLOTS = NNODES]" << std::endl;
909                                std::cerr << "  or:  " << argv[0] << ": [options] -b pingpong [NNODES]" << std::endl;
910                                std::cerr << "  or:  " << argv[0] << ": [options] -b producer [NNODES]" << std::endl;
911                                std::cerr << std::endl;
912                                std::cerr << "  -d, --duration=DURATION  Duration of the experiment, in seconds" << std::endl;
913                                std::cerr << "  -t, --nthreads=NTHREADS  Number of kernel threads" << std::endl;
914                                std::cerr << "  -q, --nqueues=NQUEUES    Number of queues per threads" << std::endl;
915                                std::exit(1);
916                }
917        }
918        run:
919
920        check_cache_line_size();
921
922        std::cout << "Running " << nthreads << " threads (" << (nthreads * nqueues) << " queues) for " << duration << " seconds" << std::endl;
923        std::cout << "Relaxed list variant: " << relaxed_list<Node>::name() << std::endl;
924        switch(benchmark) {
925                case Churn:
926                        runChurn(nthreads, nqueues, duration, nnodes, nslots);
927                        break;
928                case PingPong:
929                        runPingPong(nthreads, nqueues, duration, nnodes);
930                        break;
931                case Producer:
932                        runProducer(nthreads, nqueues, duration, nnodes);
933                        break;
934                case Fairness:
935                        runFairness(nthreads, nqueues, duration, nnodes, out);
936                        break;
937                default:
938                        abort();
939        }
940        return 0;
941}
942
943const char * __my_progname = "Relaxed List";
944
945struct rgb_t {
946    double r;       // a fraction between 0 and 1
947    double g;       // a fraction between 0 and 1
948    double b;       // a fraction between 0 and 1
949};
950
951struct hsv_t {
952    double h;       // angle in degrees
953    double s;       // a fraction between 0 and 1
954    double v;       // a fraction between 0 and 1
955};
956
957rgb_t hsv2rgb(hsv_t in) {
958        double hh, p, q, t, ff;
959        long   i;
960        rgb_t  out;
961
962        if(in.s <= 0.0) {       // < is bogus, just shuts up warnings
963                out.r = in.v;
964                out.g = in.v;
965                out.b = in.v;
966                return out;
967        }
968        hh = in.h;
969        if(hh >= 360.0) hh = 0.0;
970        hh /= 60.0;
971        i = (long)hh;
972        ff = hh - i;
973        p = in.v * (1.0 - in.s);
974        q = in.v * (1.0 - (in.s * ff));
975        t = in.v * (1.0 - (in.s * (1.0 - ff)));
976
977        switch(i) {
978        case 0:
979                out.r = in.v;
980                out.g = t;
981                out.b = p;
982                break;
983        case 1:
984                out.r = q;
985                out.g = in.v;
986                out.b = p;
987                break;
988        case 2:
989                out.r = p;
990                out.g = in.v;
991                out.b = t;
992                break;
993
994        case 3:
995                out.r = p;
996                out.g = q;
997                out.b = in.v;
998                break;
999        case 4:
1000                out.r = t;
1001                out.g = p;
1002                out.b = in.v;
1003                break;
1004        case 5:
1005        default:
1006                out.r = in.v;
1007                out.g = p;
1008                out.b = q;
1009                break;
1010        }
1011        return out;
1012}
1013
1014// void save_fairness(const int data[], int factor, unsigned nthreads, size_t columns, size_t rows, const std::string & output) {
1015//      std::ofstream os(output);
1016//      os << "<html>\n";
1017//      os << "<head>\n";
1018//      os << "<style>\n";
1019//      os << "</style>\n";
1020//      os << "</head>\n";
1021//      os << "<body>\n";
1022//      os << "<table style=\"width=100%\">\n";
1023
1024//      size_t idx = 0;
1025//      for(size_t r = 0ul; r < rows; r++) {
1026//              os << "<tr>\n";
1027//              for(size_t c = 0ul; c < columns; c++) {
1028//                      os << "<td class=\"custom custom" << data[idx] << "\"></td>\n";
1029//                      idx++;
1030//              }
1031//              os << "</tr>\n";
1032//      }
1033
1034//      os << "</table>\n";
1035//      os << "</body>\n";
1036//      os << "</html>\n";
1037//      os << std::endl;
1038// }
1039
1040// #include <png.h>
1041// #include <setjmp.h>
1042
1043/*
1044void save_fairness(const int data[], int factor, unsigned nthreads, size_t columns, size_t rows, const std::string & output) {
1045        int width  = columns * factor;
1046        int height = rows / factor;
1047
1048        int code = 0;
1049        int idx = 0;
1050        FILE *fp = NULL;
1051        png_structp png_ptr = NULL;
1052        png_infop info_ptr = NULL;
1053        png_bytep row = NULL;
1054
1055        // Open file for writing (binary mode)
1056        fp = fopen(output.c_str(), "wb");
1057        if (fp == NULL) {
1058                fprintf(stderr, "Could not open file %s for writing\n", output.c_str());
1059                code = 1;
1060                goto finalise;
1061        }
1062
1063           // Initialize write structure
1064        png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1065        if (png_ptr == NULL) {
1066                fprintf(stderr, "Could not allocate write struct\n");
1067                code = 1;
1068                goto finalise;
1069        }
1070
1071        // Initialize info structure
1072        info_ptr = png_create_info_struct(png_ptr);
1073        if (info_ptr == NULL) {
1074                fprintf(stderr, "Could not allocate info struct\n");
1075                code = 1;
1076                goto finalise;
1077        }
1078
1079        // Setup Exception handling
1080        if (setjmp(png_jmpbuf(png_ptr))) {
1081                fprintf(stderr, "Error during png creation\n");
1082                code = 1;
1083                goto finalise;
1084        }
1085
1086        png_init_io(png_ptr, fp);
1087
1088        // Write header (8 bit colour depth)
1089        png_set_IHDR(png_ptr, info_ptr, width, height,
1090                8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
1091                PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
1092
1093        png_write_info(png_ptr, info_ptr);
1094
1095        // Allocate memory for one row (3 bytes per pixel - RGB)
1096        row = (png_bytep) malloc(3 * width * sizeof(png_byte));
1097
1098        // Write image data
1099        int x, y;
1100        for (y=0 ; y<height ; y++) {
1101                for (x=0 ; x<width ; x++) {
1102                        auto & r = row[(x * 3) + 0];
1103                        auto & g = row[(x * 3) + 1];
1104                        auto & b = row[(x * 3) + 2];
1105                        assert(idx < (rows * columns));
1106                        int color = data[idx] - 1;
1107                        assert(color < nthreads);
1108                        assert(color >= 0);
1109                        idx++;
1110
1111                        double angle = double(color) / double(nthreads);
1112
1113                        auto c = hsv2rgb({ 360.0 * angle, 0.8, 0.8 });
1114
1115                        r = char(c.r * 255.0);
1116                        g = char(c.g * 255.0);
1117                        b = char(c.b * 255.0);
1118
1119                }
1120                png_write_row(png_ptr, row);
1121        }
1122
1123        assert(idx == (rows * columns));
1124
1125        // End write
1126        png_write_end(png_ptr, NULL);
1127
1128        finalise:
1129        if (fp != NULL) fclose(fp);
1130        if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
1131        if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
1132        if (row != NULL) free(row);
1133}
1134*/
Note: See TracBrowser for help on using the repository browser.