1d86ed7fbStbbdev /*
2*b15aabb3Stbbdev Copyright (c) 2005-2021 Intel Corporation
3d86ed7fbStbbdev
4d86ed7fbStbbdev Licensed under the Apache License, Version 2.0 (the "License");
5d86ed7fbStbbdev you may not use this file except in compliance with the License.
6d86ed7fbStbbdev You may obtain a copy of the License at
7d86ed7fbStbbdev
8d86ed7fbStbbdev http://www.apache.org/licenses/LICENSE-2.0
9d86ed7fbStbbdev
10d86ed7fbStbbdev Unless required by applicable law or agreed to in writing, software
11d86ed7fbStbbdev distributed under the License is distributed on an "AS IS" BASIS,
12d86ed7fbStbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d86ed7fbStbbdev See the License for the specific language governing permissions and
14d86ed7fbStbbdev limitations under the License.
15d86ed7fbStbbdev */
16d86ed7fbStbbdev
17d86ed7fbStbbdev #if _MSC_VER
18d86ed7fbStbbdev // Suppress "decorated name length exceeded, name was truncated" warning
19d86ed7fbStbbdev #pragma warning(disable : 4503)
20d86ed7fbStbbdev #endif
21d86ed7fbStbbdev
22d86ed7fbStbbdev #include <cstdlib>
23d86ed7fbStbbdev #include <cstdio>
24d86ed7fbStbbdev
25d86ed7fbStbbdev #include <iostream>
26d86ed7fbStbbdev #include <thread>
27d86ed7fbStbbdev #include <chrono>
28d86ed7fbStbbdev #include <tuple>
29d86ed7fbStbbdev
30d86ed7fbStbbdev #include "oneapi/tbb/flow_graph.h"
31d86ed7fbStbbdev #include "oneapi/tbb/tick_count.h"
32d86ed7fbStbbdev #include "oneapi/tbb/spin_mutex.h"
33d86ed7fbStbbdev #include "oneapi/tbb/global_control.h"
34d86ed7fbStbbdev
35d86ed7fbStbbdev #include "common/utility/utility.hpp"
36d86ed7fbStbbdev #include "common/utility/get_default_num_threads.hpp"
37d86ed7fbStbbdev
38d86ed7fbStbbdev // Each philosopher is an object, and is invoked in the think() function_node, the
39d86ed7fbStbbdev // eat() function_node and forward() multifunction_node.
40d86ed7fbStbbdev //
41d86ed7fbStbbdev // The graph is constructed, and each think() function_node is started with a continue_msg.
42d86ed7fbStbbdev //
43d86ed7fbStbbdev // The philosopher will think, then gather two chopsticks, eat, place the chopsticks back,
44d86ed7fbStbbdev // and if they have not completed the required number of cycles, will start to think() again
45d86ed7fbStbbdev // by sending a continue_msg to their corresponding think() function_node.
46d86ed7fbStbbdev //
47d86ed7fbStbbdev // The reserving join has as its inputs the left and right chopstick queues an a queue
48d86ed7fbStbbdev // that stores the continue_msg emitted by the function_node after think()ing is done.
49d86ed7fbStbbdev // When all three inputs are available, a tuple of the inputs will be forwarded to the
50d86ed7fbStbbdev // eat() function_node. The output of the eat() function_node is sent to the forward()
51d86ed7fbStbbdev // multifunction_node.
52d86ed7fbStbbdev
53d86ed7fbStbbdev const std::chrono::seconds think_time(1);
54d86ed7fbStbbdev const std::chrono::seconds eat_time(1);
55d86ed7fbStbbdev const int num_times = 10;
56d86ed7fbStbbdev
57d86ed7fbStbbdev oneapi::tbb::tick_count t0;
58d86ed7fbStbbdev bool verbose = false;
59d86ed7fbStbbdev
60d86ed7fbStbbdev const char *names[] = { "Archimedes", "Bakunin", "Confucius", "Democritus", "Euclid",
61d86ed7fbStbbdev "Favorinus", "Geminus", "Heraclitus", "Ichthyas", "Jason of Nysa",
62d86ed7fbStbbdev "Kant", "Lavrov", "Metrocles", "Nausiphanes", "Onatas",
63d86ed7fbStbbdev "Phaedrus", "Quillot", "Russell", "Socrates", "Thales",
64d86ed7fbStbbdev "Udayana", "Vernadsky", "Wittgenstein", "Xenophilus", "Yen Yuan",
65d86ed7fbStbbdev "Zenodotus" };
66d86ed7fbStbbdev const int NumPhilosophers = sizeof(names) / sizeof(char *);
67d86ed7fbStbbdev
68d86ed7fbStbbdev struct RunOptions {
69d86ed7fbStbbdev utility::thread_number_range threads;
70d86ed7fbStbbdev int number_of_philosophers;
71d86ed7fbStbbdev bool silent;
RunOptionsRunOptions72d86ed7fbStbbdev RunOptions(utility::thread_number_range threads_, int number_of_philosophers_, bool silent_)
73d86ed7fbStbbdev : threads(threads_),
74d86ed7fbStbbdev number_of_philosophers(number_of_philosophers_),
75d86ed7fbStbbdev silent(silent_) {}
76d86ed7fbStbbdev };
77d86ed7fbStbbdev
ParseCommandLine(int argc,char * argv[])78d86ed7fbStbbdev RunOptions ParseCommandLine(int argc, char *argv[]) {
79d86ed7fbStbbdev int auto_threads = utility::get_default_num_threads();
80d86ed7fbStbbdev utility::thread_number_range threads(
81d86ed7fbStbbdev utility::get_default_num_threads, auto_threads, auto_threads);
82d86ed7fbStbbdev int nPhilosophers = 5;
83d86ed7fbStbbdev bool verbose = false;
84d86ed7fbStbbdev char charbuf[100];
85d86ed7fbStbbdev std::sprintf(charbuf, "%d", NumPhilosophers);
86d86ed7fbStbbdev std::string pCount = "how many philosophers, from 2-";
87d86ed7fbStbbdev pCount += charbuf;
88d86ed7fbStbbdev
89d86ed7fbStbbdev utility::cli_argument_pack cli_pack;
90d86ed7fbStbbdev cli_pack.positional_arg(threads, "n-of_threads", utility::thread_number_range_desc)
91d86ed7fbStbbdev .positional_arg(nPhilosophers, "n-of-philosophers", pCount)
92d86ed7fbStbbdev .arg(verbose, "verbose", "verbose output");
93d86ed7fbStbbdev utility::parse_cli_arguments(argc, argv, cli_pack);
94d86ed7fbStbbdev if (nPhilosophers < 2 || nPhilosophers > NumPhilosophers) {
95d86ed7fbStbbdev std::cout << "Number of philosophers (" << nPhilosophers
96d86ed7fbStbbdev << ") out of range [2:" << NumPhilosophers << "]\n";
97d86ed7fbStbbdev std::cout << cli_pack.usage_string(argv[0]) << std::flush;
98d86ed7fbStbbdev std::exit(-1);
99d86ed7fbStbbdev }
100d86ed7fbStbbdev return RunOptions(threads, nPhilosophers, !verbose);
101d86ed7fbStbbdev }
102d86ed7fbStbbdev
103d86ed7fbStbbdev oneapi::tbb::spin_mutex my_mutex;
104d86ed7fbStbbdev
105d86ed7fbStbbdev class chopstick {};
106d86ed7fbStbbdev
107d86ed7fbStbbdev typedef std::tuple<oneapi::tbb::flow::continue_msg, chopstick, chopstick> join_output;
108d86ed7fbStbbdev typedef oneapi::tbb::flow::join_node<join_output, oneapi::tbb::flow::reserving> join_node_type;
109d86ed7fbStbbdev
110d86ed7fbStbbdev typedef oneapi::tbb::flow::function_node<oneapi::tbb::flow::continue_msg,
111d86ed7fbStbbdev oneapi::tbb::flow::continue_msg>
112d86ed7fbStbbdev think_node_type;
113d86ed7fbStbbdev typedef oneapi::tbb::flow::function_node<join_output, oneapi::tbb::flow::continue_msg>
114d86ed7fbStbbdev eat_node_type;
115d86ed7fbStbbdev typedef oneapi::tbb::flow::multifunction_node<oneapi::tbb::flow::continue_msg, join_output>
116d86ed7fbStbbdev forward_node_type;
117d86ed7fbStbbdev
118d86ed7fbStbbdev class philosopher {
119d86ed7fbStbbdev public:
philosopher(const char * name)120d86ed7fbStbbdev philosopher(const char *name) : my_name(name), my_count(num_times) {}
121d86ed7fbStbbdev
~philosopher()122d86ed7fbStbbdev ~philosopher() {}
123d86ed7fbStbbdev
124d86ed7fbStbbdev void check();
name() const125d86ed7fbStbbdev const char *name() const {
126d86ed7fbStbbdev return my_name;
127d86ed7fbStbbdev }
128d86ed7fbStbbdev
129d86ed7fbStbbdev private:
130d86ed7fbStbbdev friend std::ostream &operator<<(std::ostream &o, philosopher const &p);
131d86ed7fbStbbdev
132d86ed7fbStbbdev const char *my_name;
133d86ed7fbStbbdev int my_count;
134d86ed7fbStbbdev
135d86ed7fbStbbdev friend class think_node_body;
136d86ed7fbStbbdev friend class eat_node_body;
137d86ed7fbStbbdev friend class forward_node_body;
138d86ed7fbStbbdev
139d86ed7fbStbbdev void think();
140d86ed7fbStbbdev void eat();
141d86ed7fbStbbdev void forward(const oneapi::tbb::flow::continue_msg &in,
142d86ed7fbStbbdev forward_node_type::output_ports_type &out_ports);
143d86ed7fbStbbdev };
144d86ed7fbStbbdev
operator <<(std::ostream & o,philosopher const & p)145d86ed7fbStbbdev std::ostream &operator<<(std::ostream &o, philosopher const &p) {
146d86ed7fbStbbdev o << "< philosopher[" << reinterpret_cast<uintptr_t>(const_cast<philosopher *>(&p)) << "] "
147d86ed7fbStbbdev << p.name() << ", my_count=" << p.my_count;
148d86ed7fbStbbdev
149d86ed7fbStbbdev return o;
150d86ed7fbStbbdev }
151d86ed7fbStbbdev
152d86ed7fbStbbdev class think_node_body {
153d86ed7fbStbbdev philosopher &my_philosopher;
154d86ed7fbStbbdev
155d86ed7fbStbbdev public:
think_node_body(philosopher & p)156d86ed7fbStbbdev think_node_body(philosopher &p) : my_philosopher(p) {}
think_node_body(const think_node_body & other)157d86ed7fbStbbdev think_node_body(const think_node_body &other) : my_philosopher(other.my_philosopher) {}
operator ()(oneapi::tbb::flow::continue_msg)158d86ed7fbStbbdev oneapi::tbb::flow::continue_msg operator()(oneapi::tbb::flow::continue_msg /*m*/) {
159d86ed7fbStbbdev my_philosopher.think();
160d86ed7fbStbbdev return oneapi::tbb::flow::continue_msg();
161d86ed7fbStbbdev }
162d86ed7fbStbbdev };
163d86ed7fbStbbdev
164d86ed7fbStbbdev class eat_node_body {
165d86ed7fbStbbdev philosopher &my_philosopher;
166d86ed7fbStbbdev
167d86ed7fbStbbdev public:
eat_node_body(philosopher & p)168d86ed7fbStbbdev eat_node_body(philosopher &p) : my_philosopher(p) {}
eat_node_body(const eat_node_body & other)169d86ed7fbStbbdev eat_node_body(const eat_node_body &other) : my_philosopher(other.my_philosopher) {}
operator ()(const join_output & in)170d86ed7fbStbbdev oneapi::tbb::flow::continue_msg operator()(const join_output &in) {
171d86ed7fbStbbdev my_philosopher.eat();
172d86ed7fbStbbdev return oneapi::tbb::flow::continue_msg();
173d86ed7fbStbbdev }
174d86ed7fbStbbdev };
175d86ed7fbStbbdev
176d86ed7fbStbbdev class forward_node_body {
177d86ed7fbStbbdev philosopher &my_philosopher;
178d86ed7fbStbbdev
179d86ed7fbStbbdev public:
forward_node_body(philosopher & p)180d86ed7fbStbbdev forward_node_body(philosopher &p) : my_philosopher(p) {}
forward_node_body(const forward_node_body & other)181d86ed7fbStbbdev forward_node_body(const forward_node_body &other) : my_philosopher(other.my_philosopher) {}
operator ()(const oneapi::tbb::flow::continue_msg & in,forward_node_type::output_ports_type & out)182d86ed7fbStbbdev void operator()(const oneapi::tbb::flow::continue_msg &in,
183d86ed7fbStbbdev forward_node_type::output_ports_type &out) {
184d86ed7fbStbbdev my_philosopher.forward(in, out);
185d86ed7fbStbbdev }
186d86ed7fbStbbdev };
187d86ed7fbStbbdev
check()188d86ed7fbStbbdev void philosopher::check() {
189d86ed7fbStbbdev if (my_count != 0) {
190d86ed7fbStbbdev std::printf("ERROR: philosopher %s still had to run %d more times\n", name(), my_count);
191d86ed7fbStbbdev std::exit(-1);
192d86ed7fbStbbdev }
193d86ed7fbStbbdev }
194d86ed7fbStbbdev
forward(const oneapi::tbb::flow::continue_msg &,forward_node_type::output_ports_type & out_ports)195d86ed7fbStbbdev void philosopher::forward(const oneapi::tbb::flow::continue_msg & /*in*/,
196d86ed7fbStbbdev forward_node_type::output_ports_type &out_ports) {
197d86ed7fbStbbdev if (my_count < 0)
198d86ed7fbStbbdev abort();
199d86ed7fbStbbdev --my_count;
200d86ed7fbStbbdev (void)std::get<1>(out_ports).try_put(chopstick());
201d86ed7fbStbbdev (void)std::get<2>(out_ports).try_put(chopstick());
202d86ed7fbStbbdev if (my_count > 0) {
203d86ed7fbStbbdev (void)std::get<0>(out_ports).try_put(
204d86ed7fbStbbdev oneapi::tbb::flow::continue_msg()); //start thinking again
205d86ed7fbStbbdev }
206d86ed7fbStbbdev else {
207d86ed7fbStbbdev if (verbose) {
208d86ed7fbStbbdev oneapi::tbb::spin_mutex::scoped_lock lock(my_mutex);
209d86ed7fbStbbdev std::printf("%s has left the building\n", name());
210d86ed7fbStbbdev }
211d86ed7fbStbbdev }
212d86ed7fbStbbdev }
213d86ed7fbStbbdev
eat()214d86ed7fbStbbdev void philosopher::eat() {
215d86ed7fbStbbdev if (verbose) {
216d86ed7fbStbbdev oneapi::tbb::spin_mutex::scoped_lock lock(my_mutex);
217d86ed7fbStbbdev std::printf("%s eating\n", name());
218d86ed7fbStbbdev }
219d86ed7fbStbbdev std::this_thread::sleep_for(eat_time);
220d86ed7fbStbbdev if (verbose) {
221d86ed7fbStbbdev oneapi::tbb::spin_mutex::scoped_lock lock(my_mutex);
222d86ed7fbStbbdev std::printf("%s done eating\n", name());
223d86ed7fbStbbdev }
224d86ed7fbStbbdev }
225d86ed7fbStbbdev
think()226d86ed7fbStbbdev void philosopher::think() {
227d86ed7fbStbbdev if (verbose) {
228d86ed7fbStbbdev oneapi::tbb::spin_mutex::scoped_lock lock(my_mutex);
229d86ed7fbStbbdev std::printf("%s thinking\n", name());
230d86ed7fbStbbdev }
231d86ed7fbStbbdev std::this_thread::sleep_for(think_time);
232d86ed7fbStbbdev if (verbose) {
233d86ed7fbStbbdev oneapi::tbb::spin_mutex::scoped_lock lock(my_mutex);
234d86ed7fbStbbdev std::printf("%s done thinking\n", name());
235d86ed7fbStbbdev }
236d86ed7fbStbbdev }
237d86ed7fbStbbdev
238d86ed7fbStbbdev typedef oneapi::tbb::flow::queue_node<oneapi::tbb::flow::continue_msg> thinking_done_type;
239d86ed7fbStbbdev
main(int argc,char * argv[])240d86ed7fbStbbdev int main(int argc, char *argv[]) {
241d86ed7fbStbbdev using oneapi::tbb::flow::make_edge;
242d86ed7fbStbbdev using oneapi::tbb::flow::input_port;
243d86ed7fbStbbdev using oneapi::tbb::flow::output_port;
244d86ed7fbStbbdev
245d86ed7fbStbbdev oneapi::tbb::tick_count main_time = oneapi::tbb::tick_count::now();
246d86ed7fbStbbdev int num_threads;
247d86ed7fbStbbdev int num_philosophers;
248d86ed7fbStbbdev
249d86ed7fbStbbdev RunOptions options = ParseCommandLine(argc, argv);
250d86ed7fbStbbdev num_philosophers = options.number_of_philosophers;
251d86ed7fbStbbdev verbose = !options.silent;
252d86ed7fbStbbdev
253d86ed7fbStbbdev for (num_threads = options.threads.first; num_threads <= options.threads.last;
254d86ed7fbStbbdev num_threads = options.threads.step(num_threads)) {
255d86ed7fbStbbdev oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism,
256d86ed7fbStbbdev num_threads);
257d86ed7fbStbbdev
258d86ed7fbStbbdev oneapi::tbb::flow::graph g;
259d86ed7fbStbbdev
260d86ed7fbStbbdev if (verbose) {
261d86ed7fbStbbdev std::cout << "\n"
262d86ed7fbStbbdev << num_philosophers << " philosophers with " << num_threads << " threads"
263d86ed7fbStbbdev << "\n"
264d86ed7fbStbbdev << "\n";
265d86ed7fbStbbdev }
266d86ed7fbStbbdev t0 = oneapi::tbb::tick_count::now();
267d86ed7fbStbbdev
268d86ed7fbStbbdev std::vector<oneapi::tbb::flow::queue_node<chopstick>> places(
269d86ed7fbStbbdev num_philosophers, oneapi::tbb::flow::queue_node<chopstick>(g));
270d86ed7fbStbbdev std::vector<philosopher> philosophers;
271d86ed7fbStbbdev philosophers.reserve(num_philosophers);
272d86ed7fbStbbdev std::vector<think_node_type *> think_nodes;
273d86ed7fbStbbdev think_nodes.reserve(num_philosophers);
274d86ed7fbStbbdev std::vector<thinking_done_type> done_vector(num_philosophers, thinking_done_type(g));
275d86ed7fbStbbdev std::vector<join_node_type> join_vector(num_philosophers, join_node_type(g));
276d86ed7fbStbbdev std::vector<eat_node_type *> eat_nodes;
277d86ed7fbStbbdev eat_nodes.reserve(num_philosophers);
278d86ed7fbStbbdev std::vector<forward_node_type *> forward_nodes;
279d86ed7fbStbbdev forward_nodes.reserve(num_philosophers);
280d86ed7fbStbbdev for (int i = 0; i < num_philosophers; ++i) {
281d86ed7fbStbbdev places[i].try_put(chopstick());
282d86ed7fbStbbdev philosophers.push_back(
283d86ed7fbStbbdev philosopher(names[i])); // allowed because of default generated assignment
284d86ed7fbStbbdev if (verbose) {
285d86ed7fbStbbdev oneapi::tbb::spin_mutex::scoped_lock lock(my_mutex);
286d86ed7fbStbbdev std::cout << "Built philosopher " << philosophers[i] << "\n";
287d86ed7fbStbbdev }
288d86ed7fbStbbdev think_nodes.push_back(new think_node_type(
289d86ed7fbStbbdev g, oneapi::tbb::flow::unlimited, think_node_body(philosophers[i])));
290d86ed7fbStbbdev eat_nodes.push_back(
291d86ed7fbStbbdev new eat_node_type(g, oneapi::tbb::flow::unlimited, eat_node_body(philosophers[i])));
292d86ed7fbStbbdev forward_nodes.push_back(new forward_node_type(
293d86ed7fbStbbdev g, oneapi::tbb::flow::unlimited, forward_node_body(philosophers[i])));
294d86ed7fbStbbdev }
295d86ed7fbStbbdev
296d86ed7fbStbbdev // attach chopstick buffers and think function_nodes to joins
297d86ed7fbStbbdev for (int i = 0; i < num_philosophers; ++i) {
298d86ed7fbStbbdev make_edge(*think_nodes[i], done_vector[i]);
299d86ed7fbStbbdev make_edge(done_vector[i], input_port<0>(join_vector[i]));
300d86ed7fbStbbdev make_edge(places[i], input_port<1>(join_vector[i])); // left chopstick
301d86ed7fbStbbdev make_edge(places[(i + 1) % num_philosophers],
302d86ed7fbStbbdev input_port<2>(join_vector[i])); // right chopstick
303d86ed7fbStbbdev make_edge(join_vector[i], *eat_nodes[i]);
304d86ed7fbStbbdev make_edge(*eat_nodes[i], *forward_nodes[i]);
305d86ed7fbStbbdev make_edge(output_port<0>(*forward_nodes[i]), *think_nodes[i]);
306d86ed7fbStbbdev make_edge(output_port<1>(*forward_nodes[i]), places[i]);
307d86ed7fbStbbdev make_edge(output_port<2>(*forward_nodes[i]), places[(i + 1) % num_philosophers]);
308d86ed7fbStbbdev }
309d86ed7fbStbbdev
310d86ed7fbStbbdev // start all the philosophers thinking
311d86ed7fbStbbdev for (int i = 0; i < num_philosophers; ++i)
312d86ed7fbStbbdev think_nodes[i]->try_put(oneapi::tbb::flow::continue_msg());
313d86ed7fbStbbdev
314d86ed7fbStbbdev g.wait_for_all();
315d86ed7fbStbbdev
316d86ed7fbStbbdev oneapi::tbb::tick_count t1 = oneapi::tbb::tick_count::now();
317d86ed7fbStbbdev if (verbose)
318d86ed7fbStbbdev std::cout << "\n"
319d86ed7fbStbbdev << num_philosophers << " philosophers with " << num_threads
320d86ed7fbStbbdev << " threads have taken " << (t1 - t0).seconds() << "seconds"
321d86ed7fbStbbdev << "\n";
322d86ed7fbStbbdev
323d86ed7fbStbbdev for (int i = 0; i < num_philosophers; ++i)
324d86ed7fbStbbdev philosophers[i].check();
325d86ed7fbStbbdev
326d86ed7fbStbbdev for (int i = 0; i < num_philosophers; ++i) {
327d86ed7fbStbbdev delete think_nodes[i];
328d86ed7fbStbbdev delete eat_nodes[i];
329d86ed7fbStbbdev delete forward_nodes[i];
330d86ed7fbStbbdev }
331d86ed7fbStbbdev }
332d86ed7fbStbbdev
333d86ed7fbStbbdev utility::report_elapsed_time((oneapi::tbb::tick_count::now() - main_time).seconds());
334d86ed7fbStbbdev return 0;
335d86ed7fbStbbdev }
336