blob: 32c2f25c932a902a191c16868120fb10dfcd0b3d [file] [log] [blame]
/*
Copyright 2005-2010 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.
*/
#include "rml_tbb.h"
#include "rml_omp.h"
#include "tbb/atomic.h"
#include "tbb/tick_count.h"
#define HARNESS_DEFAULT_MIN_THREADS 4
#include "harness.h"
const int OMP_ParallelRegionSize = 16;
int TBB_MaxThread = 4; // Includes master
int OMP_MaxThread = int(~0u>>1); // Includes master
template<typename Client>
class ClientBase: public Client {
protected:
typedef typename Client::version_type version_type;
typedef typename Client::job job;
typedef typename Client::policy_type policy_type;
private:
/*override*/version_type version() const {
return 0;
}
/*override*/size_t min_stack_size() const {
return 1<<20;
}
/*override*/job* create_one_job() {
return new rml::job;
}
/*override*/policy_type policy() const {
return Client::turnaround;
}
/*override*/void acknowledge_close_connection() {
delete this;
}
/*override*/void cleanup( job& j ) {delete &j;}
};
//! Represents a TBB or OpenMP run-time that uses RML.
template<typename Factory, typename Client>
class RunTime {
public:
//! Factory that run-time uses to make servers.
Factory factory;
Client* client;
typename Factory::server_type* server;
#if _WIN32||_WIN64
::rml::server::execution_resource_t me;
#endif
RunTime() {
factory.open();
}
~RunTime() {
factory.close();
}
//! Create server for this run-time
void create_connection();
//! Destroy server for this run-time
void destroy_connection();
};
class ThreadLevelRecorder {
tbb::atomic<int> level;
struct record {
tbb::tick_count time;
int nthread;
};
tbb::atomic<unsigned> next;
/** Must be power of two */
static const unsigned max_record_count = 1<<20;
record array[max_record_count];
public:
void change_level( int delta );
void dump();
};
void ThreadLevelRecorder::change_level( int delta ) {
int x = level+=delta;
tbb::tick_count t = tbb::tick_count::now();
unsigned k = next++;
if( k<max_record_count ) {
record& r = array[k];
r.time = t;
r.nthread = x;
}
}
void ThreadLevelRecorder::dump() {
FILE* f = fopen("time.txt","w");
if( !f ) {
perror("fopen(time.txt)\n");
exit(1);
}
unsigned limit = next;
if( limit>max_record_count ) {
// Clip
limit = next;
}
for( unsigned i=0; i<limit; ++i ) {
fprintf(f,"%f\t%d\n",(array[i].time-array[0].time).seconds(),array[i].nthread);
}
fclose(f);
}
ThreadLevelRecorder TotalThreadLevel;
class TBB_Client: public ClientBase<tbb::internal::rml::tbb_client> {
/*override*/void process( job& j );
/*override*/size_type max_job_count() const {
return TBB_MaxThread-1;
}
};
class OMP_Client: public ClientBase<__kmp::rml::omp_client> {
/*override*/void process( job&, void* cookie, omp_client::size_type );
/*override*/size_type max_job_count() const {
return OMP_MaxThread-1;
}
};
RunTime<tbb::internal::rml::tbb_factory, TBB_Client> TBB_RunTime;
RunTime<__kmp::rml::omp_factory, OMP_Client> OMP_RunTime;
template<typename Factory, typename Client>
void RunTime<Factory,Client>::create_connection() {
client = new Client;
typename Factory::status_type status = factory.make_server( server, *client );
ASSERT( status==Factory::st_success, NULL );
#if _WIN32||_WIN64
server->register_master( me );
#endif /* _WIN32||_WIN64 */
}
template<typename Factory, typename Client>
void RunTime<Factory,Client>::destroy_connection() {
#if _WIN32||_WIN64
server->unregister_master( me );
#endif /* _WIN32||_WIN64 */
server->request_close_connection();
server = NULL;
}
class OMP_Team {
public:
OMP_Team( __kmp::rml::omp_server& ) {}
tbb::atomic<unsigned> barrier;
};
tbb::atomic<int> AvailWork;
tbb::atomic<int> CompletionCount;
void OMPWork() {
tbb::atomic<int> x;
for( x=0; x<2000000; ++x ) {
continue;
}
}
void TBBWork() {
if( AvailWork>=0 ) {
int k = --AvailWork;
if( k==-1 ) {
TBB_RunTime.server->adjust_job_count_estimate(-(TBB_MaxThread-1));
++CompletionCount;
} else if( k>=0 ) {
for( int k=0; k<4; ++k ) {
OMP_Team team( *OMP_RunTime.server );
int n = OMP_RunTime.server->try_increase_load( OMP_ParallelRegionSize-1, /*strict=*/false );
team.barrier = 0;
::rml::job* array[OMP_ParallelRegionSize-1];
if( n>0)
OMP_RunTime.server->get_threads( n, &team, array );
// Master does work inside parallel region too.
OMPWork();
// Master waits for workers to finish
if( n>0 )
while( team.barrier!=unsigned(n) ) {
__TBB_Yield();
}
}
++CompletionCount;
}
}
}
/*override*/void TBB_Client::process( job& ) {
TotalThreadLevel.change_level(1);
TBBWork();
TotalThreadLevel.change_level(-1);
}
/*override*/void OMP_Client::process( job& /* j */, void* cookie, omp_client::size_type ) {
TotalThreadLevel.change_level(1);
ASSERT( OMP_RunTime.server, NULL );
OMPWork();
ASSERT( OMP_RunTime.server, NULL );
static_cast<OMP_Team*>(cookie)->barrier+=1;
TotalThreadLevel.change_level(-1);
}
void TBBOutSideOpenMPInside() {
TotalThreadLevel.change_level(1);
CompletionCount = 0;
int tbbtasks = 32;
AvailWork = tbbtasks;
TBB_RunTime.server->adjust_job_count_estimate(TBB_MaxThread-1);
while( CompletionCount!=tbbtasks+1 ) {
TBBWork();
}
TotalThreadLevel.change_level(-1);
}
int TestMain () {
for( int TBB_MaxThread=MinThread; TBB_MaxThread<=MaxThread; ++TBB_MaxThread ) {
REMARK("Testing with TBB_MaxThread=%d\n", TBB_MaxThread);
TBB_RunTime.create_connection();
OMP_RunTime.create_connection();
TBBOutSideOpenMPInside();
OMP_RunTime.destroy_connection();
TBB_RunTime.destroy_connection();
}
TotalThreadLevel.dump();
return Harness::Done;
}