|  | 
 | 
  
    |  | // includes (STL)
 | 
  
    |  | #include <algorithm>
 | 
  
    |  | #include <future>
 | 
  
    |  | #include <iostream>
 | 
  
    |  | #include <memory>
 | 
  
    |  | #include <thread>
 | 
  
    |  | #include <vector>
 | 
  
    |  | // includes (Wt)
 | 
  
    |  | #include <Wt/WContainerWidget.h>
 | 
  
    |  | 
 | 
  
    |  | #include "Timer.hpp"
 | 
  
    |  | 
 | 
  
    |  | using namespace Wt;
 | 
  
    |  | 
 | 
  
    |  | //!
 | 
  
    |  | //!
 | 
  
    |  | //!
 | 
  
    |  | void testUniqueness()
 | 
  
    |  | {
 | 
  
    |  | 	static const size_t NumTasks = 100;
 | 
  
    |  | 	static const size_t ObjectsPerTask = 100000;
 | 
  
    |  | 
 | 
  
    |  | 	// task for creating and returning a vector containing 'ObjectsPerTask' WContainerWidgets
 | 
  
    |  | 	auto createTask = []()
 | 
  
    |  | 	{
 | 
  
    |  | 		std::vector<std::unique_ptr<Wt::WContainerWidget>> result;
 | 
  
    |  | 		result.reserve(ObjectsPerTask);
 | 
  
    |  | 		for (size_t i = 0; i < ObjectsPerTask; ++i)
 | 
  
    |  | 		{
 | 
  
    |  | 			result.emplace_back(std::make_unique<Wt::WContainerWidget>());
 | 
  
    |  | 		}
 | 
  
    |  | 		return result;
 | 
  
    |  | 	};
 | 
  
    |  | 
 | 
  
    |  | 	// task for creating and returning a vector containing the string IDs of the passed WContainerWidgets
 | 
  
    |  | 	// this task also removes duplicated IDs
 | 
  
    |  | 	auto idTask = [](const std::vector<std::unique_ptr<Wt::WContainerWidget>>& ws, size_t taskId)
 | 
  
    |  | 	{
 | 
  
    |  | 		// create IDs
 | 
  
    |  | 		std::vector<std::string> result;
 | 
  
    |  | 		result.reserve(ObjectsPerTask);
 | 
  
    |  | 		for (const auto& w : ws)
 | 
  
    |  | 		{
 | 
  
    |  | 			result.emplace_back(w->id());
 | 
  
    |  | 		}
 | 
  
    |  | 		// remove duplicates that occured in the current task
 | 
  
    |  | 		std::sort(std::begin(result), std::end(result));
 | 
  
    |  | 		result.erase(std::unique(std::begin(result), std::end(result)), std::end(result));
 | 
  
    |  | 		
 | 
  
    |  | 		return result;
 | 
  
    |  | 	};
 | 
  
    |  | 
 | 
  
    |  | 	std::cout << "creating widgets ...\n";
 | 
  
    |  | 	std::vector<std::future<std::vector<std::unique_ptr<Wt::WContainerWidget>>>> wfs;
 | 
  
    |  | 	wfs.reserve(NumTasks);
 | 
  
    |  | 	// spawn 'NumTasks' threads, each performing the 'createTask' task
 | 
  
    |  | 	for (size_t i = 0; i < NumTasks; ++i)
 | 
  
    |  | 	{
 | 
  
    |  | 		wfs.emplace_back(std::async(std::launch::async, createTask));
 | 
  
    |  | 	}
 | 
  
    |  | 	// wait for all tasks to finish
 | 
  
    |  | 	for (const auto& f : wfs)
 | 
  
    |  | 	{
 | 
  
    |  | 		f.wait();
 | 
  
    |  | 	}
 | 
  
    |  | 
 | 
  
    |  | 	std::cout << "creating ids ...\n";
 | 
  
    |  | 	std::vector<std::future<std::vector<std::string>>> idfs;
 | 
  
    |  | 	idfs.reserve(NumTasks);
 | 
  
    |  | 	// spawn 'NumTasks' threads, each performing the 'idTask' task for one of the results
 | 
  
    |  | 	// from previously executed 'createTask'
 | 
  
    |  | 	for (size_t i = 0; i < NumTasks; ++i)
 | 
  
    |  | 	{
 | 
  
    |  | 		idfs.emplace_back(std::async(std::launch::async, idTask, wfs[i].get(), i));
 | 
  
    |  | 	}
 | 
  
    |  | 	// wait for all tasks to finish
 | 
  
    |  | 	for (const auto& f : idfs)
 | 
  
    |  | 	{
 | 
  
    |  | 		f.wait();
 | 
  
    |  | 	}
 | 
  
    |  | 
 | 
  
    |  | 	std::cout << "merging ids & removing duplicates ...\n\n";
 | 
  
    |  | 	std::vector<std::string> ids;
 | 
  
    |  | 	ids.reserve(NumTasks * ObjectsPerTask);
 | 
  
    |  | 	for (size_t i = 0; i < NumTasks; ++i)
 | 
  
    |  | 	{
 | 
  
    |  | 		// get the string IDs for task #i
 | 
  
    |  | 		auto tmp = idfs[i].get();
 | 
  
    |  | 
 | 
  
    |  | 		// print the number of WContainerWidgets with unique IDs built in task #i
 | 
  
    |  | 		// (duplicates already removed in 'idTask')
 | 
  
    |  | 		std::cout << "task #" << i << ": " << tmp.size() << " of " << ObjectsPerTask << " ids are unique\n";
 | 
  
    |  | 
 | 
  
    |  | 		// add all IDs container for all IDs
 | 
  
    |  | 		ids.insert(std::end(ids), std::make_move_iterator(std::begin(tmp)), std::make_move_iterator(std::end(tmp)));
 | 
  
    |  | 	}
 | 
  
    |  | 
 | 
  
    |  | 	// remove all duplicated IDs that occured in any task
 | 
  
    |  | 	std::sort(std::begin(ids), std::end(ids));
 | 
  
    |  | 	ids.erase(std::unique(std::begin(ids), std::end(ids)), std::end(ids));
 | 
  
    |  | 	std::cout << "\noverall: " << ids.size() << " of " << NumTasks * ObjectsPerTask << " ids are unique\n\n";
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | //!
 | 
  
    |  | //!
 | 
  
    |  | //!
 | 
  
    |  | void measurePerformance()
 | 
  
    |  | {
 | 
  
    |  | 	static size_t N = 5000000;
 | 
  
    |  | 
 | 
  
    |  | 	double elapsed = 0;
 | 
  
    |  | 	for (size_t k = 0; k < 10; ++k)
 | 
  
    |  | 	{
 | 
  
    |  | 		std::vector<std::unique_ptr<Wt::WContainerWidget>> ws;
 | 
  
    |  | 		ws.reserve(N);
 | 
  
    |  | 
 | 
  
    |  | 		sk::tools::Timer t;
 | 
  
    |  | 		t.start();
 | 
  
    |  | 
 | 
  
    |  | 		for (size_t i = 0; i < N; ++i)
 | 
  
    |  | 		{
 | 
  
    |  | 			ws.emplace_back(std::make_unique<Wt::WContainerWidget>());
 | 
  
    |  | 		}
 | 
  
    |  | 
 | 
  
    |  | 		t.stop();
 | 
  
    |  | 		elapsed += t.elapsed();
 | 
  
    |  | 		std::cout << "elapsed time #" << k << ": " << t.elapsed() << "ms\n";
 | 
  
    |  | 	}
 | 
  
    |  | 
 | 
  
    |  | 	std::cout << "elapsed time (mean): " << elapsed / 10 << "ms\n\n";
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | //!
 | 
  
    |  | //!
 | 
  
    |  | //!
 | 
  
    |  | int main(int argc, char** args)
 | 
  
    |  | {
 | 
  
    |  | 	testUniqueness();
 | 
  
    |  | 	measurePerformance();
 | 
  
    |  | 
 | 
  
    |  | 	return 0;
 | 
  
    |  | }
 |