source: doc/theses/thierry_delisle_PhD/code/relaxed_list.cpp @ 62f38ff

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

Several changes to relaxed list prototype and added workstealing for comparison

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