📄 fibonacci.cpp
字号:
/* Copyright 2005-2007 Intel Corporation. All Rights Reserved. This file is part of Threading Building Blocks. Threading Building Blocks is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Threading Building Blocks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Threading Building Blocks; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA As a special exception, you may use this file as part of a free software library without restriction. Specifically, if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other files to produce an executable, this file does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU General Public License.*//* Example program that computes Fibonacci numbers in different ways. Arguments are: [ Number [Threads [Repeats]]] The defaults are Number=100 Threads=1:4 Repeats=1. The point of this program is to check that the library is working properly. Most of the computations are deliberately silly and not expected to show any speedup on multiprocessors.*/#include <cstdio>#include <cstdlib>#include <cassert>#include <utility>#include "tbb/task.h"#include "tbb/task_scheduler_init.h"#include "tbb/tick_count.h"#include "tbb/blocked_range.h"#include "tbb/concurrent_vector.h"#include "tbb/concurrent_queue.h"#include "tbb/concurrent_hash_map.h"#include "tbb/parallel_while.h"#include "tbb/parallel_for.h"#include "tbb/parallel_reduce.h"#include "tbb/parallel_scan.h"#include "tbb/pipeline.h"#include "tbb/atomic.h"#include "tbb/mutex.h"#include "tbb/spin_mutex.h"#include "tbb/queuing_mutex.h"using namespace std;using namespace tbb;//! type used for Fibonacci number computationstypedef long long value;//! Matrix 2x2 classstruct Matrix2x2{ //! Array of values value v[2][2]; Matrix2x2() {} Matrix2x2(value v00, value v01, value v10, value v11) { v[0][0] = v00; v[0][1] = v01; v[1][0] = v10; v[1][1] = v11; } Matrix2x2 operator * (const Matrix2x2 &to) const; //< Multiply two Matrices};//! Default matrix to multiplystatic const Matrix2x2 Matrix1110(1, 1, 1, 0);//! Raw arrays matrices multiplyvoid Matrix2x2Multiply(const value a[2][2], const value b[2][2], value c[2][2]);/////////////////////// Serial methods //////////////////////////! Plain serial sumvalue SerialFib(int n){ if(n < 2) return n; value a = 0, b = 1, sum; int i; for( i = 2; i <= n; i++ ) { // n is really index of Fibonacci number sum = a + b; a = b; b = sum; } return sum;}//! Serial n-1 matrices multiplicationvalue SerialMatrixFib(int n){ value c[2][2], a[2][2] = {{1, 1}, {1, 0}}, b[2][2] = {{1, 1}, {1, 0}}; int i; for(i = 2; i < n; i++) { // Using condition to prevent copying of values if(i & 1) Matrix2x2Multiply(a, c, b); else Matrix2x2Multiply(a, b, c); } return (i & 1) ? c[0][0] : b[0][0]; // get result from upper left cell}//! Recursive summing. Just for complete list of serial algorithms, not usedvalue SerialRecursiveFib(int n){ value result; if(n < 2) result = n; else result = SerialRecursiveFib(n - 1) + SerialRecursiveFib(n - 2); return result;}//! Introducing of queue method in serialvalue SerialQueueFib(int n){ concurrent_queue<Matrix2x2> Q; for(int i = 1; i < n; i++) Q.push(Matrix1110); Matrix2x2 A, B; while(true) { Q.pop(A); if(Q.empty()) break; Q.pop(B); Q.push(A * B); } return A.v[0][0];}//! Trying to use concurrent_vectorvalue SerialVectorFib(int n){ concurrent_vector<value> A; A.grow_by(2); A[0] = 0; A[1] = 1; for( int i = 2; i <= n; i++) { A.grow_to_at_least(i+1); A[i] = A[i-1] + A[i-2]; } return A[n];}///////////////////// Parallel methods ////////////////////////// *** Serial shared by mutexes *** ////! Shared glabalsvalue SharedA = 0, SharedB = 1; int SharedI = 1, SharedN;//! Template task class which computes Fibonacci numbers with shared globalstemplate<typename M>class SharedSerialFibBody { M &mutex;public: SharedSerialFibBody( M &m ) : mutex( m ) {} //! main loop void operator()( const blocked_range<int>& range ) const { for(;;) { typename M::scoped_lock lock( mutex ); if(SharedI >= SharedN) break; value sum = SharedA + SharedB; SharedA = SharedB; SharedB = sum; ++SharedI; } }};//! Root functiontemplate<class M>value SharedSerialFib(int n){ SharedA = 0; SharedB = 1; SharedI = 1; SharedN = n; M mutex; parallel_for( blocked_range<int>(0,4,1), SharedSerialFibBody<M>( mutex ) ); return SharedB;}// *** Serial shared by concurrent hash *** ////! Hash comparerstruct IntHashCompare { bool equal( const int j, const int k ) const { return j == k; } unsigned long hash( const int k ) const { return (unsigned long)k; } };//! NumbersTable type based on concurrent_hash_maptypedef concurrent_hash_map<int, value, IntHashCompare> NumbersTable;//! task for serial method using shared concurrent_hash_mapclass ConcurrentHashSerialFibTask: public task { NumbersTable &Fib; int my_n;public: //! constructor ConcurrentHashSerialFibTask( NumbersTable &cht, int n ) : Fib(cht), my_n(n) { } //! executing task /*override*/ task* execute() { for( int i = 2; i <= my_n; ++i ) { // there is no difference in to recycle or to make loop NumbersTable::const_accessor f1, f2; // same as iterators if( !Fib.find(f1, i-1) || !Fib.find(f2, i-2) ) { // Something is seriously wrong, because i-1 and i-2 must have been inserted // earlier by this thread or another thread. assert(0); } value sum = f1->second + f2->second; NumbersTable::accessor fsum; if(Fib.insert(fsum, i)) // inserting. is new element? fsum->second = sum; // new, assign value else assert( fsum->second == sum ); // no, check value } return 0; }};//! Root functionvalue ConcurrentHashSerialFib(int n){ NumbersTable Fib; { // We need to declare the accessor in a local block before using its values outside the block. NumbersTable::accessor fref; bool okay; okay = Fib.insert(fref, 0); assert(okay); fref->second = 0; // assign initial values okay = Fib.insert(fref, 1); assert(okay); fref->second = 1; // Lifetime of fref ends here, which implicitly releases the write lock on what it points to. } task_list list; // allocate tasks list.push_back(*new(task::allocate_root()) ConcurrentHashSerialFibTask(Fib, n)); list.push_back(*new(task::allocate_root()) ConcurrentHashSerialFibTask(Fib, n)); task::spawn_root_and_wait(list); NumbersTable::const_accessor fresult; bool okay = Fib.find( fresult, n ); assert(okay); return fresult->second;}// *** Queue with parallel_for and parallel_while *** ////! Stream of matricesstruct QueueStream { volatile bool producer_is_done; concurrent_queue<Matrix2x2> Queue; //! Get pair of matricies if present bool pop_if_present( pair<Matrix2x2, Matrix2x2> &mm ) { // get first matrix if present if(!Queue.pop_if_present(mm.first)) return false; // get second matrix if present if(!Queue.pop_if_present(mm.second)) { // if not, then push back first matrix Queue.push(mm.first); return false; } return true; }};//! Functor for parallel_for which fills the queuestruct parallel_forFibBody { QueueStream &my_stream; //! fill functor arguments parallel_forFibBody(QueueStream &s) : my_stream(s) { } //! iterate thorough range void operator()( const blocked_range<int> &range ) const { int i_end = range.end(); for( int i = range.begin(); i != i_end; ++i ) { my_stream.Queue.push( Matrix1110 ); // push initial matrix } }};//! Functor for parallel_while which process the queueclass parallel_whileFibBody{ QueueStream &my_stream; parallel_while<parallel_whileFibBody> &my_while;public: typedef pair<Matrix2x2, Matrix2x2> argument_type; //! fill functor arguments parallel_whileFibBody(parallel_while<parallel_whileFibBody> &w, QueueStream &s) : my_while(w), my_stream(s) { } //! process pair of matrices void operator() (argument_type mm) const { mm.first = mm.first * mm.second; // note: it can run concurrently with QueueStream::pop_if_present() if(my_stream.Queue.pop_if_present(mm.second)) my_while.add( mm ); // now, two matrices available. Add next iteration. else my_stream.Queue.push( mm.first ); // or push back calculated value if queue is empty }};//! Parallel queue's filling taskstruct QueueInsertTask: public task { QueueStream &my_stream; int my_n;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -