Multi-threading Sum of 1/i Utility Routines
Keywords: multi-threading sum 1/i utility routines harmonic_work setup worker combine syntax purpose harmonic_setup num_sum num_threads harmonic_worker harmonic_combine source

| Prev | Next |  |  |  |  |  | harmonic_work.cpp |

Multi-threading Sum of 1/i Utility Routines
Syntax
`ok = harmonic_setup(num_sum, num_threads)
` `harmonic_worker()
` `ok = harmonic_combine(sum)
`
Purpose
These routines aid in the multi-threading computation of the summation that defines the harmonic series 1 + 1 / 2 + 1 / 3 + . . . + 1 / n
harmonic_setup
Calling this function splits up the computation of the summation into different parts for each thread.
num_sum
The argument `num_sum` has prototype `
size_t num_sum
` It specifies the value of n in the summation.
num_threads
The argument `num_threads` has prototype `
size_t num_threads
` It specifies the number of threads that will be used for the summation. In the special case where `num_thread` is zero, the summation should be done by one thread, but without the overhead of the multi_threading system.
harmonic_worker
Calling this function does the computation for one thread. Following a call to `harmonic_setup`, this function should be called by each of the `num_threads` threads.
harmonic_combine
After the `num_threads` threads have completed their calls to `harmonic_worker`, this function call will combine the results and return the final summation.
Source `

    # include <cppad/cppad.hpp>
    # include "harmonic_work.hpp"
    # define MAX_NUMBER_THREADS 48

    namespace {
    	using CppAD::thread_alloc;

    	// number of threads specified by previous call to harmonic_setup
    	size_t num_threads_ = 0;

    	// structure with information for one thread
    	typedef struct {
    		// index to start summation at (worker input)
    		size_t start;
    		// index to end summation at (worker input)
    		size_t stop;
    		// summation for this thread (worker output)
    		double sum;
    		// false if an error occurs, true otherwise (worker output)
    		bool   ok;
    	} work_one_t;
    	// vector with information for all threads
    	// (use pointers instead of values to avoid false sharing)
    	work_one_t* work_all_[MAX_NUMBER_THREADS];
    }
    // -----------------------------------------------------------------------
    // do the work for one thread
    void harmonic_worker(void)
    {	// sum =  1/(stop-1) + 1/(stop-2) + ... + 1/start
    	size_t thread_num  = thread_alloc::thread_num();
    	size_t num_threads = std::max(num_threads_, size_t(1));
    	bool   ok          = thread_num < num_threads;
    	size_t start       = work_all_[thread_num]->start;
    	size_t stop        = work_all_[thread_num]->stop;
    	double sum         = 0.;

    	ok &= stop > start;
    	size_t i = stop;
    	while( i > start )
    	{	i--;
    		sum += 1. / double(i);
    	}

    	work_all_[thread_num]->sum = sum;
    	work_all_[thread_num]->ok  = ok;
    }
    // -----------------------------------------------------------------------
    // setup the work up for multiple threads
    bool harmonic_setup(size_t num_sum, size_t num_threads)
    {	// sum = 1/num_sum + 1/(num_sum-1) + ... + 1
    	num_threads_ = num_threads;
    	num_threads  = std::max(num_threads_, size_t(1));
    	bool ok      = num_threads == thread_alloc::num_threads();
    	ok          &= num_sum >= num_threads;

    	size_t thread_num;
    	for(thread_num = 0; thread_num < num_threads; thread_num++)
    	{	// allocate separate memory for this thread to avoid false sharing
    		size_t min_bytes(sizeof(work_one_t)), cap_bytes;
    		void* v_ptr = thread_alloc::get_memory(min_bytes, cap_bytes);
    		work_all_[thread_num] = static_cast<work_one_t*>(v_ptr);
    		// in case this thread's worker does not get called
    		work_all_[thread_num]->ok = false;
    		// parameters that define the work for this and previous thread
    		if( thread_num == 0 )
    			work_all_[0]->start = 1;
    		else
    		{	size_t index  = (num_sum * thread_num) / num_threads;
    			work_all_[thread_num-1]->stop = index;
    			work_all_[thread_num]->start  = index;
    		}
    	}
    	work_all_[num_threads-1]->stop = num_sum + 1;
    	return ok;
    }
    // -----------------------------------------------------------------------
    // get the result of the work
    bool harmonic_combine(double& sum)
    {	// sum = 1/num_sum + 1/(num_sum-1) + ... + 1
    	bool ok            = true;
    	size_t num_threads = std::max(num_threads_, size_t(1));
    	sum                = 0.;

    	// go down so that free memory for other threads before memory for master
    	size_t thread_num = num_threads;
    	while(thread_num--)
    	{	// check that this tread was ok with the work it did
    		ok  &= work_all_[thread_num]->ok;
    		// add this threads contribution to the sum
    		sum += work_all_[thread_num]->sum;
    		// delete problem specific information
    		void* v_ptr = static_cast<void*>( work_all_[thread_num] );
    		thread_alloc::return_memory( v_ptr );
    		// check that there is no longer any memory inuse by this thread
    		// (for general applications, the master might still be using memory)
    		ok &= thread_alloc::inuse(thread_num) == 0;
    		// return all memory being held for future use by this thread
    		thread_alloc::free_available(thread_num);
    	}
    	return ok;
    }

`

Input File: multi_thread/harmonic_work.cpp