source: doc/theses/thierry_delisle_PhD/code/relaxed_list.cpp@ a3fab47

ADT arm-eh ast-experimental enum forall-pointer-decay jacob/cs343-translation new-ast new-ast-unique-expr pthread-emulation qualifiedEnum
Last change on this file since a3fab47 was 807a632, checked in by Thierry Delisle <tdelisle@…>, 6 years ago

Adding current version of the C++ relaxed_list code and benchmark

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