<commit_before>
#include <q/promise.hpp>
#include <q/lib.hpp>
#include <q/blocking_dispatcher.hpp>
#include <q/threadpool.hpp>
#include <q/execution_context.hpp>
#include <q/scheduler.hpp>
#include <q/timer.hpp>

#include <iomanip>
#include <numeric>

void print_timer( std::ostream& os, q::timer::duration_type dur, std::size_t denominator )
{
	auto ns = std::chrono::duration_cast< std::chrono::nanoseconds >( dur );
	auto float_ns = static_cast< double >( ns.count( ) );
	double partdur = float_ns / denominator;

	os
		<< std::fixed << std::setprecision( 0 )
		<< partdur << "ns (i.e. "
		<< ( 1000.0 * 1000.0 * 1000.0 ) / partdur << "/s)"
		<< std::endl;
}

void benchmark_title( const std::string& title )
{
	std::string msg = "Benchmarking: " + title;
	std::string lines( msg.size( ), '=' );

	std::cout << lines << std::endl;
	std::cout << msg << std::endl;
	std::cout << lines << std::endl;
}

template< typename Start, typename Stop >
void benchmark_queueing_and_scheduling( Start&& start, Stop&& stop, bool parallel, q::queue_ptr queue, std::size_t iterations )
{
	q::shared_promise< std::tuple< int > > promise = q::with( queue, 0 ).share( );
	std::vector< q::promise< std::tuple< int > > > waitable;
	q::promise< std::tuple< int > > serial_promise = q::with( queue, 0 );
	waitable.reserve( iterations );

	q::timer::duration_type total_dur( 0 );

	{
		q::scoped_timer::function_type fn = [ &total_dur, iterations ]( q::timer::duration_type dur )
		{
			total_dur += dur;

			std::cout << "Queueing a task takes ";
			print_timer( std::cout, dur, iterations );
		};
		q::scoped_timer timer( std::move( fn ) );

		if ( parallel )
			for ( std::size_t i = 0; i < iterations; ++i )
				waitable.push_back(
					promise.then( [ ]( int value )
					{
						return value + 1;
					}, queue )
				);
		else
			for ( std::size_t i = 0; i < iterations; ++i )
				serial_promise = serial_promise
				.then( [ ]( int value )
				{
					return value + 1;
				}, queue );
	}

	std::size_t final = 0;
	if ( parallel )
	{
		q::scoped_timer::function_type fn = [ &total_dur, iterations ]( q::timer::duration_type dur )
		{
			total_dur += dur;

			std::cout << "Scheduling completions for " << iterations << " tasks using q::all() takes per task ";
			print_timer( std::cout, dur, iterations );
		};

		q::scoped_timer timer( std::move( fn ) );

		q::all( std::move( waitable ), queue )
		.then( [ &final, stop ]( std::vector< int >&& values )
		{
			int sum = std::accumulate( values.begin( ), values.end( ), 0 );
			final += sum;
			stop( );
		} );
	}
	else
	{
		serial_promise
		.then( [ &final, stop ]( int val )
		{
			final = val;
			stop( );
		} );
	}

	{
		q::scoped_timer::function_type fn = [ &total_dur, iterations ]( q::timer::duration_type dur )
		{
			total_dur += dur;

			std::cout << "Scheduling and running a task takes ";
			print_timer( std::cout, dur, iterations );
		};

		q::scoped_timer timer( std::move( fn ) );

		start( );
	}

	if ( final != iterations )
	{
		std::cerr
			<< "Error running benchmark, wrong amount iterations: "
			<< final << " != " << iterations << "!" << std::endl;
		return;
	}

	std::cout << "Queueing, scheduling and running takes ";
	print_timer( std::cout, total_dur, iterations );
}

void benchmark_tasks_on_main_queue( std::size_t iterations, bool parallel )
{
	auto bd2 = q::make_shared< q::blocking_dispatcher >( "test" );
	auto s2 = q::make_shared< q::direct_scheduler >( bd2 );
	auto ctx2 = q::make_shared< q::execution_context >( bd2, s2 );
	auto queue = ctx2->queue( );

	auto start = [ bd2 ]( )
	{
		bd2->start( );
	};
	auto stop = [ bd2 ]( )
	{
		bd2->terminate( q::termination::linger );
	};

	std::string in_parallel;
	if ( parallel )
		in_parallel = " in parallel";

	benchmark_title( "queueing, scheduling and running trivial tasks" + in_parallel );
	benchmark_queueing_and_scheduling( start, stop, parallel, queue, iterations );
	std::cout << std::endl;
}

void benchmark_tasks_on_threadpool( std::size_t iterations, bool parallel )
{
	auto bd2 = q::make_shared< q::blocking_dispatcher >( "test" );
	auto s2 = q::make_shared< q::direct_scheduler >( bd2 );
	auto ctx2 = q::make_shared< q::execution_context >( bd2, s2 );
	auto queue = ctx2->queue( );

	auto tp = q::make_shared< q::threadpool >( "threadpool", queue );
	auto bg_queue = q::make_shared< q::queue >( 0 );
	auto bg_sched = q::make_shared< q::direct_scheduler >( tp );

	auto start = [ bd2, bg_sched, bg_queue ]( )
	{
		bg_sched->add_queue( bg_queue );
		bd2->start( );
	};
	auto stop = [ bd2, tp ]( )
	{
		bd2->terminate( q::termination::linger );
		tp->terminate( q::termination::linger );
		tp->await_termination( );
	};

	std::string in_parallel;
	if ( parallel )
		in_parallel = " in parallel";

	benchmark_title( "queueing, scheduling and running trivial tasks" + in_parallel + " on thread pool" );
	benchmark_queueing_and_scheduling( start, stop, parallel, bg_queue, iterations );
	std::cout << std::endl;
}

int main( int argc, char** argv )
{
	q::settings settings;<commit_msg>benchmark slight fix for newer q API<commit_after>
#include <q/promise.hpp>
#include <q/lib.hpp>
#include <q/blocking_dispatcher.hpp>
#include <q/threadpool.hpp>
#include <q/execution_context.hpp>
#include <q/scheduler.hpp>
#include <q/timer.hpp>

#include <iomanip>
#include <numeric>

void print_timer( std::ostream& os, q::timer::duration_type dur, std::size_t denominator )
{
	auto ns = std::chrono::duration_cast< std::chrono::nanoseconds >( dur );
	auto float_ns = static_cast< double >( ns.count( ) );
	double partdur = float_ns / denominator;

	os
		<< std::fixed << std::setprecision( 0 )
		<< partdur << "ns (i.e. "
		<< ( 1000.0 * 1000.0 * 1000.0 ) / partdur << "/s)"
		<< std::endl;
}

void benchmark_title( const std::string& title )
{
	std::string msg = "Benchmarking: " + title;
	std::string lines( msg.size( ), '=' );

	std::cout << lines << std::endl;
	std::cout << msg << std::endl;
	std::cout << lines << std::endl;
}

template< typename Start, typename Stop >
void benchmark_queueing_and_scheduling( Start&& start, Stop&& stop, bool parallel, q::queue_ptr queue, std::size_t iterations )
{
	q::shared_promise< std::tuple< int > > promise = q::with( queue, 0 ).share( );
	std::vector< q::promise< std::tuple< int > > > waitable;
	q::promise< std::tuple< int > > serial_promise = q::with( queue, 0 );
	waitable.reserve( iterations );

	q::timer::duration_type total_dur( 0 );

	{
		q::scoped_timer::function_type fn = [ &total_dur, iterations ]( q::timer::duration_type dur )
		{
			total_dur += dur;

			std::cout << "Queueing a task takes ";
			print_timer( std::cout, dur, iterations );
		};
		q::scoped_timer timer( std::move( fn ) );

		if ( parallel )
			for ( std::size_t i = 0; i < iterations; ++i )
				waitable.push_back(
					promise.then( [ ]( int value )
					{
						return value + 1;
					}, queue )
				);
		else
			for ( std::size_t i = 0; i < iterations; ++i )
				serial_promise = serial_promise
				.then( [ ]( int value )
				{
					return value + 1;
				}, queue );
	}

	std::size_t final = 0;
	if ( parallel )
	{
		q::scoped_timer::function_type fn = [ &total_dur, iterations ]( q::timer::duration_type dur )
		{
			total_dur += dur;

			std::cout << "Scheduling completions for " << iterations << " tasks using q::all() takes per task ";
			print_timer( std::cout, dur, iterations );
		};

		q::scoped_timer timer( std::move( fn ) );

		q::all( std::move( waitable ), queue )
		.then( [ &final, stop ]( std::vector< int >&& values )
		{
			int sum = std::accumulate( values.begin( ), values.end( ), 0 );
			final += sum;
			stop( );
		} );
	}
	else
	{
		serial_promise
		.then( [ &final, stop ]( int val )
		{
			final = val;
			stop( );
		} );
	}

	{
		q::scoped_timer::function_type fn = [ &total_dur, iterations ]( q::timer::duration_type dur )
		{
			total_dur += dur;

			std::cout << "Scheduling and running a task takes ";
			print_timer( std::cout, dur, iterations );
		};

		q::scoped_timer timer( std::move( fn ) );

		start( );
	}

	if ( final != iterations )
	{
		std::cerr
			<< "Error running benchmark, wrong amount iterations: "
			<< final << " != " << iterations << "!" << std::endl;
		return;
	}

	std::cout << "Queueing, scheduling and running takes ";
	print_timer( std::cout, total_dur, iterations );
}

void benchmark_tasks_on_main_queue( std::size_t iterations, bool parallel )
{
	auto bd2 = q::make_shared< q::blocking_dispatcher >( "test" );
	auto s2 = q::make_shared< q::direct_scheduler >( bd2 );
	auto ctx2 = q::make_shared< q::execution_context >( bd2, s2 );
	auto queue = ctx2->queue( );

	auto start = [ bd2 ]( )
	{
		bd2->start( );
	};
	auto stop = [ bd2 ]( )
	{
		bd2->terminate( q::termination::linger );
	};

	std::string in_parallel;
	if ( parallel )
		in_parallel = " in parallel";

	benchmark_title( "queueing, scheduling and running trivial tasks" + in_parallel );
	benchmark_queueing_and_scheduling( start, stop, parallel, queue, iterations );
	std::cout << std::endl;
}

void benchmark_tasks_on_threadpool( std::size_t iterations, bool parallel )
{
	auto bd2 = q::make_shared< q::blocking_dispatcher >( "test" );
	auto s2 = q::make_shared< q::direct_scheduler >( bd2 );
	auto ctx2 = q::make_shared< q::execution_context >( bd2, s2 );
	auto queue = ctx2->queue( );

	auto tp = q::make_shared< q::threadpool >( "threadpool", queue );
	auto bg_queue = q::make_shared< q::queue >( 0 );
	auto bg_sched = q::make_shared< q::direct_scheduler >( tp );

	auto start = [ tp, bd2, bg_sched, bg_queue ]( )
	{
		bg_sched->add_queue( bg_queue );
		tp->start( );
		bd2->start( );
	};
	auto stop = [ bd2, tp ]( )
	{
		bd2->terminate( q::termination::linger );
		tp->terminate( q::termination::linger );
		tp->await_termination( );
	};

	std::string in_parallel;
	if ( parallel )
		in_parallel = " in parallel";

	benchmark_title( "queueing, scheduling and running trivial tasks" + in_parallel + " on thread pool" );
	benchmark_queueing_and_scheduling( start, stop, parallel, bg_queue, iterations );
	std::cout << std::endl;
}

int main( int argc, char** argv )
{
	q::settings settings;
{'fraction_non_alphanumeric': 0.171320679717778, 'fraction_numerical': 0.009539898638576965, 'mean_word_length': 3.9454545454545453, 'pattern_counts': {'":': 0, '<': 173, '<?xml version=': 0, '>': 80, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4705534', 'n_tokens_mistral': 4056, 'n_tokens_neox': 3616, 'n_words': 867}