source: doc/theses/thierry_delisle_PhD/code/readyQ_proto/relaxed_list.cpp @ edc6ea2

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

Many small changes to prototype code

  • Property mode set to 100644
File size: 26.6 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, duration);
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 = nullptr;
371                        while(!node) {
372                                node = list.pop();
373                        }
374                        local.crc_out += node->value;
375                        local.out++;
376                }
377        }
378}
379
380void runPingPong(unsigned nthread, unsigned nqueues, double duration, unsigned nnodes) {
381        std::cout << "PingPong Benchmark" << std::endl;
382
383
384        // Barrier for synchronization
385        barrier_t barrier(nthread + 1);
386
387        // Data to check everything is OK
388        global_stat_t global;
389
390        // Flag to signal termination
391        std::atomic_bool done  = { false };
392
393        std::cout << "Initializing ";
394        // List being tested
395        LIST_VARIANT<Node> list = { nthread, nqueues };
396        {
397                enable_stats = true;
398
399                std::thread * threads[nthread];
400                unsigned i = 1;
401                for(auto & t : threads) {
402                        t = new std::thread([&done, &list, &barrier, &global, nnodes](unsigned tid) {
403                                Random rand(tid + rdtscl());
404
405                                Node nodes[nnodes];
406                                for(auto & n : nodes) {
407                                        n.value = (int)rand.next() % 100;
408                                }
409
410                                local_stat_t local;
411
412                                // affinity(tid);
413
414                                barrier.wait(tid);
415
416                                // EXPERIMENT START
417
418                                runPingPong_body(done, nodes, nnodes, local, list);
419
420                                // EXPERIMENT END
421
422                                barrier.wait(tid);
423
424                                tally_stats(global, local);
425                        }, i++);
426                }
427
428                waitfor(duration, barrier, done);
429
430                for(auto t : threads) {
431                        t->join();
432                        delete t;
433                }
434
435                enable_stats = false;
436        }
437
438        print_stats(duration, nthread, global);
439}
440
441// ================================================================================================
442struct __attribute__((aligned(64))) Slot {
443        Node * volatile node;
444};
445
446__attribute__((noinline)) void runProducer_body(
447        std::atomic<bool>& done,
448        Random & rand,
449        Slot * slots,
450        int nslots,
451        local_stat_t & local,
452        LIST_VARIANT<Node> & list
453) {
454        while(__builtin_expect(!done.load(std::memory_order_relaxed), true)) {
455
456                Node * node = list.pop();
457                if(!node) {
458                        local.empty ++;
459                        continue;
460                }
461
462                local.crc_out += node->value;
463                local.out++;
464
465                if(node->id == 0) {
466                        unsigned cnt = 0;
467                        for(int i = 0; i < nslots; i++) {
468                                Node * found = __atomic_exchange_n( &slots[i].node, nullptr, __ATOMIC_SEQ_CST );
469                                if( found ) {
470                                        local.crc_in += found->value;
471                                        local.in++;
472                                        cnt++;
473                                        list.push( found );
474                                }
475                        }
476
477                        local.crc_in += node->value;
478                        local.in++;
479                        list.push( node );
480
481                        local.comp.cnt++;
482                        local.comp.val += cnt;
483                }
484                else {
485                        unsigned len = 0;
486                        while(true) {
487                                auto off = rand.next();
488                                for(int i = 0; i < nslots; i++) {
489                                        Node * expected = nullptr;
490                                        int idx = (i + off) % nslots;
491                                        Slot & slot = slots[ idx ];
492                                        if(
493                                                slot.node == nullptr &&
494                                                __atomic_compare_exchange_n( &slot.node, &expected, node, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST )
495                                        ) {
496                                                local.subm.cnt++;
497                                                local.subm.val += len;
498                                                goto LOOP;
499                                        }
500                                        assert( expected != node );
501                                        len++;
502                                }
503                        }
504                }
505
506                LOOP:;
507        }
508}
509
510void runProducer(unsigned nthread, unsigned nqueues, double duration, unsigned nnodes) {
511        std::cout << "Producer Benchmark" << std::endl;
512
513        // Barrier for synchronization
514        barrier_t barrier(nthread + 1);
515
516        // Data to check everything is OK
517        global_stat_t global;
518
519        // Flag to signal termination
520        std::atomic_bool done  = { false };
521
522        std::cout << "Initializing ";
523
524        int nslots = nnodes * 4;
525        Slot * slots = new Slot[nslots];
526        std::cout << nnodes << " nodes (" << nslots << " slots)" << std::endl;
527
528        // List being tested
529        LIST_VARIANT<Node> list = { nthread, nqueues };
530        {
531                Random rand(rdtscl());
532                for(unsigned i = 0; i < nnodes; i++) {
533                        Node * node = new Node(rand.next() % 100);
534                        node->id = i;
535                        global.crc_in += node->value;
536                        list.push(node);
537                }
538
539                for(int i = 0; i < nslots; i++) {
540                        slots[i].node = nullptr;
541                }
542        }
543
544        {
545                enable_stats = true;
546
547                std::thread * threads[nthread];
548                unsigned i = 1;
549                for(auto & t : threads) {
550                        t = new std::thread([&done, &list, &barrier, &global, slots, nslots](unsigned tid) {
551                                Random rand(tid + rdtscl());
552
553                                local_stat_t local;
554                                barrier.wait(tid);
555
556                                // EXPERIMENT START
557
558                                runProducer_body(done, rand, slots, nslots, local, list);
559
560                                // EXPERIMENT END
561
562                                barrier.wait(tid);
563
564                                tally_stats(global, local);
565                        }, i++);
566                }
567
568                waitfor(duration, barrier, done);
569
570                for(auto t : threads) {
571                        t->join();
572                        delete t;
573                }
574
575                enable_stats = false;
576        }
577
578        {
579                while(Node * node = list.pop()) {
580                        global.crc_out += node->value;
581                        delete node;
582                }
583
584                for(int i = 0; i < nslots; i++) {
585                        delete slots[i].node;
586                }
587
588                delete [] slots;
589        }
590
591        print_stats(duration, nthread, global);
592}
593
594// ================================================================================================
595__attribute__((noinline)) void runFairness_body(
596        unsigned tid,
597        size_t width,
598        size_t length,
599        int output[],
600        std::atomic_size_t & count,
601        Node initial_nodes[],
602        unsigned nnodes,
603        local_stat_t & local,
604        LIST_VARIANT<Node> & list
605) {
606        Node * nodes[nnodes];
607        {
608                unsigned i = 0;
609                for(auto & n : nodes) {
610                        n = &initial_nodes[i++];
611                }
612        }
613
614        while(__builtin_expect(0 != count.load(std::memory_order_relaxed), true)) {
615
616                for(Node * & node : nodes) {
617                        local.crc_in += node->id;
618                        list.push(node);
619                        local.in++;
620                }
621
622                // -----
623
624                for(Node * & node : nodes) {
625                        node = list.pop();
626                        assert(node);
627
628                        if (unsigned(node->value) < length) {
629                                size_t idx = (node->value * width) + node->id;
630                                assert(idx < (width * length));
631                                output[idx] = tid;
632                        }
633
634                        node->value++;
635                        if(unsigned(node->value) == length) count--;
636
637                        local.crc_out += node->id;
638                        local.out++;
639                }
640        }
641}
642
643void runFairness(unsigned nthread, unsigned nqueues, double duration, unsigned nnodes, const std::string & output) {
644        std::cout << "Fairness Benchmark, outputing to : " << output << std::endl;
645
646        // Barrier for synchronization
647        barrier_t barrier(nthread + 1);
648
649        // Data to check everything is OK
650        global_stat_t global;
651
652        std::cout << "Initializing ";
653
654        // Check fairness by creating a png of where the threads ran
655        size_t width = nthread * nnodes;
656        size_t length = 100000;
657
658        std::unique_ptr<int[]> data_out { new int[width * length] };
659
660        // Flag to signal termination
661        std::atomic_size_t count = width;
662
663        // List being tested
664        LIST_VARIANT<Node> list = { nthread, nqueues };
665        {
666                enable_stats = true;
667
668                std::thread * threads[nthread];
669                unsigned i = 1;
670                for(auto & t : threads) {
671                        t = new std::thread([&count, &list, &barrier, &global, nnodes, width, length, data_out = data_out.get()](unsigned tid) {
672                                unsigned int start = (tid - 1) * nnodes;
673                                Node nodes[nnodes];
674                                for(auto & n : nodes) {
675                                        n.id = start;
676                                        n.value = 0;
677                                        start++;
678                                }
679
680                                local_stat_t local;
681
682                                // affinity(tid);
683
684                                barrier.wait(tid);
685
686                                // EXPERIMENT START
687
688                                runFairness_body(tid, width, length, data_out, count, nodes, nnodes, local, list);
689
690                                // EXPERIMENT END
691
692                                barrier.wait(tid);
693
694                                for(const auto & n : nodes) {
695                                        local.valmax = std::max(local.valmax, size_t(n.value));
696                                        local.valmin = std::min(local.valmin, size_t(n.value));
697                                }
698
699                                tally_stats(global, local);
700                        }, i++);
701                }
702
703                waitfor(duration, barrier, count);
704
705                for(auto t : threads) {
706                        t->join();
707                        delete t;
708                }
709
710                enable_stats = false;
711        }
712
713        print_stats(duration, nthread, global);
714
715        // save_fairness(data_out.get(), 100, nthread, width, length, output);
716}
717
718// ================================================================================================
719
720bool iequals(const std::string& a, const std::string& b)
721{
722    return std::equal(a.begin(), a.end(),
723                      b.begin(), b.end(),
724                      [](char a, char b) {
725                          return std::tolower(a) == std::tolower(b);
726                      });
727}
728
729int main(int argc, char * argv[]) {
730
731        double duration   = 5.0;
732        unsigned nthreads = 2;
733        unsigned nqueues  = 4;
734        unsigned nnodes   = 100;
735        unsigned nslots   = 100;
736        std::string out   = "fairness.png";
737
738        enum {
739                Churn,
740                PingPong,
741                Producer,
742                Fairness,
743                NONE
744        } benchmark = NONE;
745
746        std::cout.imbue(std::locale(""));
747
748        for(;;) {
749                static struct option options[] = {
750                        {"duration",  required_argument, 0, 'd'},
751                        {"nthreads",  required_argument, 0, 't'},
752                        {"nqueues",   required_argument, 0, 'q'},
753                        {"benchmark", required_argument, 0, 'b'},
754                        {0, 0, 0, 0}
755                };
756
757                int idx = 0;
758                int opt = getopt_long(argc, argv, "d:t:q:b:", options, &idx);
759
760                std::string arg = optarg ? optarg : "";
761                size_t len = 0;
762                switch(opt) {
763                        // Exit Case
764                        case -1:
765                                /* paranoid */ assert(optind <= argc);
766                                switch(benchmark) {
767                                case NONE:
768                                        std::cerr << "Must specify a benchmark" << std::endl;
769                                        goto usage;
770                                case PingPong:
771                                        nnodes = 1;
772                                        switch(argc - optind) {
773                                        case 0: break;
774                                        case 1:
775                                                try {
776                                                        arg = optarg = argv[optind];
777                                                        nnodes = std::stoul(optarg, &len);
778                                                        if(len != arg.size()) { throw std::invalid_argument(""); }
779                                                } catch(std::invalid_argument &) {
780                                                        std::cerr << "Number of nodes must be a positive integer, was " << arg << std::endl;
781                                                        goto usage;
782                                                }
783                                                break;
784                                        default:
785                                                std::cerr << "'PingPong' benchmark doesn't accept more than 1 extra arguments" << std::endl;
786                                                goto usage;
787                                        }
788                                        break;
789                                case Producer:
790                                        nnodes = 32;
791                                        switch(argc - optind) {
792                                        case 0: break;
793                                        case 1:
794                                                try {
795                                                        arg = optarg = argv[optind];
796                                                        nnodes = std::stoul(optarg, &len);
797                                                        if(len != arg.size()) { throw std::invalid_argument(""); }
798                                                } catch(std::invalid_argument &) {
799                                                        std::cerr << "Number of nodes must be a positive integer, was " << arg << std::endl;
800                                                        goto usage;
801                                                }
802                                                break;
803                                        default:
804                                                std::cerr << "'Producer' benchmark doesn't accept more than 1 extra arguments" << std::endl;
805                                                goto usage;
806                                        }
807                                        break;
808                                case Churn:
809                                        nnodes = 100;
810                                        nslots = 100;
811                                        switch(argc - optind) {
812                                        case 0: break;
813                                        case 1:
814                                                try {
815                                                        arg = optarg = argv[optind];
816                                                        nnodes = std::stoul(optarg, &len);
817                                                        if(len != arg.size()) { throw std::invalid_argument(""); }
818                                                        nslots = nnodes;
819                                                } catch(std::invalid_argument &) {
820                                                        std::cerr << "Number of nodes must be a positive integer, was " << arg << std::endl;
821                                                        goto usage;
822                                                }
823                                                break;
824                                        case 2:
825                                                try {
826                                                        arg = optarg = argv[optind];
827                                                        nnodes = std::stoul(optarg, &len);
828                                                        if(len != arg.size()) { throw std::invalid_argument(""); }
829                                                } catch(std::invalid_argument &) {
830                                                        std::cerr << "Number of nodes must be a positive integer, was " << arg << std::endl;
831                                                        goto usage;
832                                                }
833                                                try {
834                                                        arg = optarg = argv[optind + 1];
835                                                        nslots = std::stoul(optarg, &len);
836                                                        if(len != arg.size()) { throw std::invalid_argument(""); }
837                                                } catch(std::invalid_argument &) {
838                                                        std::cerr << "Number of slots must be a positive integer, was " << arg << std::endl;
839                                                        goto usage;
840                                                }
841                                                break;
842                                        default:
843                                                std::cerr << "'Churn' benchmark doesn't accept more than 2 extra arguments" << std::endl;
844                                                goto usage;
845                                        }
846                                        break;
847                                case Fairness:
848                                        nnodes = 1;
849                                        switch(argc - optind) {
850                                        case 0: break;
851                                        case 1:
852                                                arg = optarg = argv[optind];
853                                                out = arg;
854                                                break;
855                                        default:
856                                                std::cerr << "'Churn' benchmark doesn't accept more than 2 extra arguments" << std::endl;
857                                                goto usage;
858                                        }
859                                }
860                                goto run;
861                        // Benchmarks
862                        case 'b':
863                                if(benchmark != NONE) {
864                                        std::cerr << "Only when benchmark can be run" << std::endl;
865                                        goto usage;
866                                }
867                                if(iequals(arg, "churn")) {
868                                        benchmark = Churn;
869                                        break;
870                                }
871                                if(iequals(arg, "pingpong")) {
872                                        benchmark = PingPong;
873                                        break;
874                                }
875                                if(iequals(arg, "producer")) {
876                                        benchmark = Producer;
877                                        break;
878                                }
879                                if(iequals(arg, "fairness")) {
880                                        benchmark = Fairness;
881                                        break;
882                                }
883                                std::cerr << "Unkown benchmark " << arg << std::endl;
884                                goto usage;
885                        // Numeric Arguments
886                        case 'd':
887                                try {
888                                        duration = std::stod(optarg, &len);
889                                        if(len != arg.size()) { throw std::invalid_argument(""); }
890                                } catch(std::invalid_argument &) {
891                                        std::cerr << "Duration must be a valid double, was " << arg << std::endl;
892                                        goto usage;
893                                }
894                                break;
895                        case 't':
896                                try {
897                                        nthreads = std::stoul(optarg, &len);
898                                        if(len != arg.size()) { throw std::invalid_argument(""); }
899                                } catch(std::invalid_argument &) {
900                                        std::cerr << "Number of threads must be a positive integer, was " << arg << std::endl;
901                                        goto usage;
902                                }
903                                break;
904                        case 'q':
905                                try {
906                                        nqueues = std::stoul(optarg, &len);
907                                        if(len != arg.size()) { throw std::invalid_argument(""); }
908                                } catch(std::invalid_argument &) {
909                                        std::cerr << "Number of queues must be a positive integer, was " << arg << std::endl;
910                                        goto usage;
911                                }
912                                break;
913                        // Other cases
914                        default: /* ? */
915                                std::cerr << opt << std::endl;
916                        usage:
917                                std::cerr << "Usage: " << argv[0] << ": [options] -b churn [NNODES] [NSLOTS = NNODES]" << std::endl;
918                                std::cerr << "  or:  " << argv[0] << ": [options] -b pingpong [NNODES]" << std::endl;
919                                std::cerr << "  or:  " << argv[0] << ": [options] -b producer [NNODES]" << std::endl;
920                                std::cerr << std::endl;
921                                std::cerr << "  -d, --duration=DURATION  Duration of the experiment, in seconds" << std::endl;
922                                std::cerr << "  -t, --nthreads=NTHREADS  Number of kernel threads" << std::endl;
923                                std::cerr << "  -q, --nqueues=NQUEUES    Number of queues per threads" << std::endl;
924                                std::exit(1);
925                }
926        }
927        run:
928
929        check_cache_line_size();
930
931        std::cout << "Running " << nthreads << " threads (" << (nthreads * nqueues) << " queues) for " << duration << " seconds" << std::endl;
932        std::cout << "Relaxed list variant: " << LIST_VARIANT<Node>::name() << std::endl;
933        switch(benchmark) {
934                case Churn:
935                        runChurn(nthreads, nqueues, duration, nnodes, nslots);
936                        break;
937                case PingPong:
938                        runPingPong(nthreads, nqueues, duration, nnodes);
939                        break;
940                case Producer:
941                        runProducer(nthreads, nqueues, duration, nnodes);
942                        break;
943                case Fairness:
944                        runFairness(nthreads, nqueues, duration, nnodes, out);
945                        break;
946                default:
947                        abort();
948        }
949        return 0;
950}
951
952const char * __my_progname = "Relaxed List";
953
954struct rgb_t {
955    double r;       // a fraction between 0 and 1
956    double g;       // a fraction between 0 and 1
957    double b;       // a fraction between 0 and 1
958};
959
960struct hsv_t {
961    double h;       // angle in degrees
962    double s;       // a fraction between 0 and 1
963    double v;       // a fraction between 0 and 1
964};
965
966rgb_t hsv2rgb(hsv_t in) {
967        double hh, p, q, t, ff;
968        long   i;
969        rgb_t  out;
970
971        if(in.s <= 0.0) {       // < is bogus, just shuts up warnings
972                out.r = in.v;
973                out.g = in.v;
974                out.b = in.v;
975                return out;
976        }
977        hh = in.h;
978        if(hh >= 360.0) hh = 0.0;
979        hh /= 60.0;
980        i = (long)hh;
981        ff = hh - i;
982        p = in.v * (1.0 - in.s);
983        q = in.v * (1.0 - (in.s * ff));
984        t = in.v * (1.0 - (in.s * (1.0 - ff)));
985
986        switch(i) {
987        case 0:
988                out.r = in.v;
989                out.g = t;
990                out.b = p;
991                break;
992        case 1:
993                out.r = q;
994                out.g = in.v;
995                out.b = p;
996                break;
997        case 2:
998                out.r = p;
999                out.g = in.v;
1000                out.b = t;
1001                break;
1002
1003        case 3:
1004                out.r = p;
1005                out.g = q;
1006                out.b = in.v;
1007                break;
1008        case 4:
1009                out.r = t;
1010                out.g = p;
1011                out.b = in.v;
1012                break;
1013        case 5:
1014        default:
1015                out.r = in.v;
1016                out.g = p;
1017                out.b = q;
1018                break;
1019        }
1020        return out;
1021}
1022
1023// void save_fairness(const int data[], int factor, unsigned nthreads, size_t columns, size_t rows, const std::string & output) {
1024//      std::ofstream os(output);
1025//      os << "<html>\n";
1026//      os << "<head>\n";
1027//      os << "<style>\n";
1028//      os << "</style>\n";
1029//      os << "</head>\n";
1030//      os << "<body>\n";
1031//      os << "<table style=\"width=100%\">\n";
1032
1033//      size_t idx = 0;
1034//      for(size_t r = 0ul; r < rows; r++) {
1035//              os << "<tr>\n";
1036//              for(size_t c = 0ul; c < columns; c++) {
1037//                      os << "<td class=\"custom custom" << data[idx] << "\"></td>\n";
1038//                      idx++;
1039//              }
1040//              os << "</tr>\n";
1041//      }
1042
1043//      os << "</table>\n";
1044//      os << "</body>\n";
1045//      os << "</html>\n";
1046//      os << std::endl;
1047// }
1048
1049// #include <png.h>
1050// #include <setjmp.h>
1051
1052/*
1053void save_fairness(const int data[], int factor, unsigned nthreads, size_t columns, size_t rows, const std::string & output) {
1054        int width  = columns * factor;
1055        int height = rows / factor;
1056
1057        int code = 0;
1058        int idx = 0;
1059        FILE *fp = NULL;
1060        png_structp png_ptr = NULL;
1061        png_infop info_ptr = NULL;
1062        png_bytep row = NULL;
1063
1064        // Open file for writing (binary mode)
1065        fp = fopen(output.c_str(), "wb");
1066        if (fp == NULL) {
1067                fprintf(stderr, "Could not open file %s for writing\n", output.c_str());
1068                code = 1;
1069                goto finalise;
1070        }
1071
1072           // Initialize write structure
1073        png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1074        if (png_ptr == NULL) {
1075                fprintf(stderr, "Could not allocate write struct\n");
1076                code = 1;
1077                goto finalise;
1078        }
1079
1080        // Initialize info structure
1081        info_ptr = png_create_info_struct(png_ptr);
1082        if (info_ptr == NULL) {
1083                fprintf(stderr, "Could not allocate info struct\n");
1084                code = 1;
1085                goto finalise;
1086        }
1087
1088        // Setup Exception handling
1089        if (setjmp(png_jmpbuf(png_ptr))) {
1090                fprintf(stderr, "Error during png creation\n");
1091                code = 1;
1092                goto finalise;
1093        }
1094
1095        png_init_io(png_ptr, fp);
1096
1097        // Write header (8 bit colour depth)
1098        png_set_IHDR(png_ptr, info_ptr, width, height,
1099                8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
1100                PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
1101
1102        png_write_info(png_ptr, info_ptr);
1103
1104        // Allocate memory for one row (3 bytes per pixel - RGB)
1105        row = (png_bytep) malloc(3 * width * sizeof(png_byte));
1106
1107        // Write image data
1108        int x, y;
1109        for (y=0 ; y<height ; y++) {
1110                for (x=0 ; x<width ; x++) {
1111                        auto & r = row[(x * 3) + 0];
1112                        auto & g = row[(x * 3) + 1];
1113                        auto & b = row[(x * 3) + 2];
1114                        assert(idx < (rows * columns));
1115                        int color = data[idx] - 1;
1116                        assert(color < nthreads);
1117                        assert(color >= 0);
1118                        idx++;
1119
1120                        double angle = double(color) / double(nthreads);
1121
1122                        auto c = hsv2rgb({ 360.0 * angle, 0.8, 0.8 });
1123
1124                        r = char(c.r * 255.0);
1125                        g = char(c.g * 255.0);
1126                        b = char(c.b * 255.0);
1127
1128                }
1129                png_write_row(png_ptr, row);
1130        }
1131
1132        assert(idx == (rows * columns));
1133
1134        // End write
1135        png_write_end(png_ptr, NULL);
1136
1137        finalise:
1138        if (fp != NULL) fclose(fp);
1139        if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
1140        if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
1141        if (row != NULL) free(row);
1142}
1143*/
Note: See TracBrowser for help on using the repository browser.