2012-11-26 18:58:24 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Doom 3 BFG Edition GPL Source Code
2012-11-28 15:47:07 +00:00
Copyright ( C ) 1993 - 2012 id Software LLC , a ZeniMax Media company .
2012-11-26 18:58:24 +00:00
2012-11-28 15:47:07 +00:00
This file is part of the Doom 3 BFG Edition GPL Source Code ( " Doom 3 BFG Edition Source Code " ) .
2012-11-26 18:58:24 +00:00
Doom 3 BFG Edition Source Code is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code . If not , see < http : //www.gnu.org/licenses/>.
In addition , the Doom 3 BFG Edition Source Code is also subject to certain additional terms . You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code . If not , please request a copy in writing from id Software at the address below .
If you have questions concerning this license or the applicable additional terms , you may contact in writing id Software LLC , c / o ZeniMax Media Inc . , Suite 120 , Rockville , Maryland 20850 USA .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# pragma hdrstop
# include "precompiled.h"
# include "ParallelJobList.h"
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Job and Job - List names
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
const char * jobNames [ ] =
{
2012-11-26 18:58:24 +00:00
ASSERT_ENUM_STRING ( JOBLIST_RENDERER_FRONTEND , 0 ) ,
ASSERT_ENUM_STRING ( JOBLIST_RENDERER_BACKEND , 1 ) ,
ASSERT_ENUM_STRING ( JOBLIST_UTILITY , 9 ) ,
} ;
static const int MAX_REGISTERED_JOBS = 128 ;
2012-11-28 15:47:07 +00:00
struct registeredJob
{
2012-11-26 18:58:24 +00:00
jobRun_t function ;
2012-11-28 15:47:07 +00:00
const char * name ;
2012-11-26 18:58:24 +00:00
} registeredJobs [ MAX_REGISTERED_JOBS ] ;
static int numRegisteredJobs ;
2012-11-28 15:47:07 +00:00
const char * GetJobListName ( jobListId_t id )
{
2012-11-26 18:58:24 +00:00
return jobNames [ id ] ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
IsRegisteredJob
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
static bool IsRegisteredJob ( jobRun_t function )
{
for ( int i = 0 ; i < numRegisteredJobs ; i + + )
{
if ( registeredJobs [ i ] . function = = function )
{
2012-11-26 18:58:24 +00:00
return true ;
}
}
return false ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
RegisterJob
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void RegisterJob ( jobRun_t function , const char * name )
{
if ( IsRegisteredJob ( function ) )
{
2012-11-26 18:58:24 +00:00
return ;
}
registeredJobs [ numRegisteredJobs ] . function = function ;
registeredJobs [ numRegisteredJobs ] . name = name ;
numRegisteredJobs + + ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
GetJobName
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
const char * GetJobName ( jobRun_t function )
{
for ( int i = 0 ; i < numRegisteredJobs ; i + + )
{
if ( registeredJobs [ i ] . function = = function )
{
2012-11-26 18:58:24 +00:00
return registeredJobs [ i ] . name ;
}
}
return " unknown " ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobRegistration : : idParallelJobRegistration
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idParallelJobRegistration : : idParallelJobRegistration ( jobRun_t function , const char * name )
{
2012-11-26 18:58:24 +00:00
RegisterJob ( function , name ) ;
}
int globalSpuLocalStoreActive ;
2012-11-28 15:47:07 +00:00
void * globalSpuLocalStoreStart ;
void * globalSpuLocalStoreEnd ;
2012-11-26 18:58:24 +00:00
idSysMutex globalSpuLocalStoreMutex ;
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
PS3
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
static idCVar jobs_longJobMicroSec ( " jobs_longJobMicroSec " , " 10000 " , CVAR_INTEGER , " print a warning for jobs that take more than this number of microseconds " ) ;
const static int MAX_THREADS = 32 ;
2012-11-28 15:47:07 +00:00
struct threadJobListState_t
{
threadJobListState_t ( ) :
jobList ( NULL ) ,
version ( 0xFFFFFFFF ) ,
signalIndex ( 0 ) ,
lastJobIndex ( 0 ) ,
nextJobIndex ( - 1 ) { }
threadJobListState_t ( int _version ) :
jobList ( NULL ) ,
version ( _version ) ,
signalIndex ( 0 ) ,
lastJobIndex ( 0 ) ,
nextJobIndex ( - 1 ) { }
idParallelJobList_Threads * jobList ;
2012-11-26 18:58:24 +00:00
int version ;
int signalIndex ;
int lastJobIndex ;
int nextJobIndex ;
} ;
2012-11-28 15:47:07 +00:00
struct threadStats_t
{
2012-11-26 18:58:24 +00:00
unsigned int numExecutedJobs ;
unsigned int numExecutedSyncs ;
uint64 submitTime ;
uint64 startTime ;
uint64 endTime ;
uint64 waitTime ;
uint64 threadExecTime [ MAX_THREADS ] ;
uint64 threadTotalTime [ MAX_THREADS ] ;
} ;
2012-11-28 15:47:07 +00:00
class idParallelJobList_Threads
{
2012-11-26 18:58:24 +00:00
public :
2012-11-28 15:47:07 +00:00
idParallelJobList_Threads ( jobListId_t id , jobListPriority_t priority , unsigned int maxJobs , unsigned int maxSyncs ) ;
~ idParallelJobList_Threads ( ) ;
2012-11-26 18:58:24 +00:00
//------------------------
// These are called from the one thread that manages this list.
//------------------------
2012-11-28 15:47:07 +00:00
ID_INLINE void AddJob ( jobRun_t function , void * data ) ;
2012-11-26 18:58:24 +00:00
ID_INLINE void InsertSyncPoint ( jobSyncType_t syncType ) ;
2012-11-28 15:47:07 +00:00
void Submit ( idParallelJobList_Threads * waitForJobList_ , int parallelism ) ;
2012-11-26 18:58:24 +00:00
void Wait ( ) ;
bool TryWait ( ) ;
bool IsSubmitted ( ) const ;
2012-11-28 15:47:07 +00:00
unsigned int GetNumExecutedJobs ( ) const
{
return threadStats . numExecutedJobs ;
}
unsigned int GetNumSyncs ( ) const
{
return threadStats . numExecutedSyncs ;
}
uint64 GetSubmitTimeMicroSec ( ) const
{
return threadStats . submitTime ;
}
uint64 GetStartTimeMicroSec ( ) const
{
return threadStats . startTime ;
}
uint64 GetFinishTimeMicroSec ( ) const
{
return threadStats . endTime ;
}
uint64 GetWaitTimeMicroSec ( ) const
{
return threadStats . waitTime ;
}
2012-11-26 18:58:24 +00:00
uint64 GetTotalProcessingTimeMicroSec ( ) const ;
uint64 GetTotalWastedTimeMicroSec ( ) const ;
uint64 GetUnitProcessingTimeMicroSec ( int unit ) const ;
uint64 GetUnitWastedTimeMicroSec ( int unit ) const ;
2012-11-28 15:47:07 +00:00
jobListId_t GetId ( ) const
{
return listId ;
}
jobListPriority_t GetPriority ( ) const
{
return listPriority ;
}
int GetVersion ( )
{
return version . GetValue ( ) ;
}
2012-11-26 18:58:24 +00:00
bool WaitForOtherJobList ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
//------------------------
// This is thread safe and called from the job threads.
//------------------------
2012-11-28 15:47:07 +00:00
enum runResult_t
{
2012-11-26 18:58:24 +00:00
RUN_OK = 0 ,
RUN_PROGRESS = BIT ( 0 ) ,
RUN_DONE = BIT ( 1 ) ,
RUN_STALLED = BIT ( 2 )
} ;
2012-11-28 15:47:07 +00:00
int RunJobs ( unsigned int threadNum , threadJobListState_t & state , bool singleJob ) ;
2012-11-26 18:58:24 +00:00
private :
static const int NUM_DONE_GUARDS = 4 ; // cycle through 4 guards so we can cyclicly chain job lists
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
bool threaded ;
bool done ;
bool hasSignal ;
jobListId_t listId ;
jobListPriority_t listPriority ;
unsigned int maxJobs ;
unsigned int maxSyncs ;
unsigned int numSyncs ;
int lastSignalJob ;
2012-11-28 15:47:07 +00:00
idSysInterlockedInteger * waitForGuard ;
2012-11-26 18:58:24 +00:00
idSysInterlockedInteger doneGuards [ NUM_DONE_GUARDS ] ;
int currentDoneGuard ;
idSysInterlockedInteger version ;
2012-11-28 15:47:07 +00:00
struct job_t
{
2012-11-26 18:58:24 +00:00
jobRun_t function ;
2012-11-28 15:47:07 +00:00
void * data ;
2012-11-26 18:58:24 +00:00
int executed ;
} ;
idList < job_t , TAG_JOBLIST > jobList ;
idList < idSysInterlockedInteger , TAG_JOBLIST > signalJobCount ;
idSysInterlockedInteger currentJob ;
idSysInterlockedInteger fetchLock ;
idSysInterlockedInteger numThreadsExecuting ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
threadStats_t deferredThreadStats ;
threadStats_t threadStats ;
2012-11-28 15:47:07 +00:00
int RunJobsInternal ( unsigned int threadNum , threadJobListState_t & state , bool singleJob ) ;
static void Nop ( void * data ) { }
2012-11-26 18:58:24 +00:00
static int JOB_SIGNAL ;
static int JOB_SYNCHRONIZE ;
static int JOB_LIST_DONE ;
} ;
int idParallelJobList_Threads : : JOB_SIGNAL ;
int idParallelJobList_Threads : : JOB_SYNCHRONIZE ;
int idParallelJobList_Threads : : JOB_LIST_DONE ;
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : idParallelJobList_Threads
= = = = = = = = = = = = = = = = = = = = = = = =
*/
idParallelJobList_Threads : : idParallelJobList_Threads ( jobListId_t id , jobListPriority_t priority , unsigned int maxJobs , unsigned int maxSyncs ) :
threaded ( true ) ,
done ( true ) ,
hasSignal ( false ) ,
listId ( id ) ,
listPriority ( priority ) ,
numSyncs ( 0 ) ,
lastSignalJob ( 0 ) ,
waitForGuard ( NULL ) ,
currentDoneGuard ( 0 ) ,
2012-11-28 15:47:07 +00:00
jobList ( )
{
2012-11-26 18:58:24 +00:00
assert ( listPriority ! = JOBLIST_PRIORITY_NONE ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
this - > maxJobs = maxJobs ;
this - > maxSyncs = maxSyncs ;
jobList . AssureSize ( maxJobs + maxSyncs * 2 + 1 ) ; // syncs go in as dummy jobs and one more to update the doneCount
jobList . SetNum ( 0 ) ;
signalJobCount . AssureSize ( maxSyncs + 1 ) ; // need one extra for submit
signalJobCount . SetNum ( 0 ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
memset ( & deferredThreadStats , 0 , sizeof ( threadStats_t ) ) ;
memset ( & threadStats , 0 , sizeof ( threadStats_t ) ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : ~ idParallelJobList_Threads
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idParallelJobList_Threads : : ~ idParallelJobList_Threads ( )
{
2012-11-26 18:58:24 +00:00
Wait ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : AddJob
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
ID_INLINE void idParallelJobList_Threads : : AddJob ( jobRun_t function , void * data )
{
2012-11-26 18:58:24 +00:00
assert ( done ) ;
# if defined( _DEBUG )
// make sure there isn't already a job with the same function and data in the list
2012-11-28 15:47:07 +00:00
if ( jobList . Num ( ) < 1000 ) // don't do this N^2 slow check on big lists
{
for ( int i = 0 ; i < jobList . Num ( ) ; i + + )
{
2012-11-26 18:58:24 +00:00
assert ( jobList [ i ] . function ! = function | | jobList [ i ] . data ! = data ) ;
}
}
# endif
2012-11-28 15:47:07 +00:00
if ( 1 ) // JDC: this never worked in tech5! !jobList.IsFull() ) {
{
job_t & job = jobList . Alloc ( ) ;
2012-11-26 18:58:24 +00:00
job . function = function ;
job . data = data ;
job . executed = 0 ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
// debug output to show us what is overflowing
int currentJobCount [ MAX_REGISTERED_JOBS ] = { } ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < jobList . Num ( ) ; + + i )
{
const char * jobName = GetJobName ( jobList [ i ] . function ) ;
for ( int j = 0 ; j < numRegisteredJobs ; + + j )
{
if ( jobName = = registeredJobs [ j ] . name )
{
2012-11-26 18:58:24 +00:00
currentJobCount [ j ] + + ;
break ;
}
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// print the quantity of each job type
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < numRegisteredJobs ; + + i )
{
if ( currentJobCount [ i ] > 0 )
{
2012-11-26 18:58:24 +00:00
idLib : : Printf ( " Job: %s, # %d " , registeredJobs [ i ] . name , currentJobCount [ i ] ) ;
}
}
idLib : : Error ( " Can't add job '%s', too many jobs %d " , GetJobName ( function ) , jobList . Num ( ) ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : InsertSyncPoint
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
ID_INLINE void idParallelJobList_Threads : : InsertSyncPoint ( jobSyncType_t syncType )
{
2012-11-26 18:58:24 +00:00
assert ( done ) ;
2012-11-28 15:47:07 +00:00
switch ( syncType )
{
case SYNC_SIGNAL :
{
2012-11-26 18:58:24 +00:00
assert ( ! hasSignal ) ;
2012-11-28 15:47:07 +00:00
if ( jobList . Num ( ) )
{
2012-11-26 18:58:24 +00:00
assert ( ! hasSignal ) ;
signalJobCount . Alloc ( ) ;
signalJobCount [ signalJobCount . Num ( ) - 1 ] . SetValue ( jobList . Num ( ) - lastSignalJob ) ;
lastSignalJob = jobList . Num ( ) ;
2012-11-28 15:47:07 +00:00
job_t & job = jobList . Alloc ( ) ;
2012-11-26 18:58:24 +00:00
job . function = Nop ;
job . data = & JOB_SIGNAL ;
hasSignal = true ;
}
break ;
}
2012-11-28 15:47:07 +00:00
case SYNC_SYNCHRONIZE :
{
if ( hasSignal )
{
job_t & job = jobList . Alloc ( ) ;
2012-11-26 18:58:24 +00:00
job . function = Nop ;
job . data = & JOB_SYNCHRONIZE ;
hasSignal = false ;
numSyncs + + ;
}
break ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : Submit
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idParallelJobList_Threads : : Submit ( idParallelJobList_Threads * waitForJobList , int parallelism )
{
2012-11-26 18:58:24 +00:00
assert ( done ) ;
assert ( numSyncs < = maxSyncs ) ;
2012-11-28 15:47:07 +00:00
assert ( ( unsigned int ) jobList . Num ( ) < = maxJobs + numSyncs * 2 ) ;
2012-11-26 18:58:24 +00:00
assert ( fetchLock . GetValue ( ) = = 0 ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
done = false ;
currentJob . SetValue ( 0 ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
memset ( & deferredThreadStats , 0 , sizeof ( deferredThreadStats ) ) ;
deferredThreadStats . numExecutedJobs = jobList . Num ( ) - numSyncs * 2 ;
deferredThreadStats . numExecutedSyncs = numSyncs ;
deferredThreadStats . submitTime = Sys_Microseconds ( ) ;
deferredThreadStats . startTime = 0 ;
deferredThreadStats . endTime = 0 ;
deferredThreadStats . waitTime = 0 ;
2012-11-28 15:47:07 +00:00
if ( jobList . Num ( ) = = 0 )
{
2012-11-26 18:58:24 +00:00
return ;
}
2012-11-28 15:47:07 +00:00
if ( waitForJobList ! = NULL )
{
2012-11-26 18:58:24 +00:00
waitForGuard = & waitForJobList - > doneGuards [ waitForJobList - > currentDoneGuard ] ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
waitForGuard = NULL ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
currentDoneGuard = ( currentDoneGuard + 1 ) & ( NUM_DONE_GUARDS - 1 ) ;
doneGuards [ currentDoneGuard ] . SetValue ( 1 ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
signalJobCount . Alloc ( ) ;
signalJobCount [ signalJobCount . Num ( ) - 1 ] . SetValue ( jobList . Num ( ) - lastSignalJob ) ;
2012-11-28 15:47:07 +00:00
job_t & job = jobList . Alloc ( ) ;
2012-11-26 18:58:24 +00:00
job . function = Nop ;
job . data = & JOB_LIST_DONE ;
2012-11-28 15:47:07 +00:00
if ( threaded )
{
2012-11-26 18:58:24 +00:00
// hand over to the manager
void SubmitJobList ( idParallelJobList_Threads * jobList , int parallelism ) ;
SubmitJobList ( this , parallelism ) ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
// run all the jobs right here
threadJobListState_t state ( GetVersion ( ) ) ;
RunJobs ( 0 , state , false ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : Wait
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idParallelJobList_Threads : : Wait ( )
{
if ( jobList . Num ( ) > 0 )
{
2012-11-26 18:58:24 +00:00
// don't lock up but return if the job list was never properly submitted
2012-11-28 15:47:07 +00:00
if ( ! verify ( ! done & & signalJobCount . Num ( ) > 0 ) )
{
2012-11-26 18:58:24 +00:00
return ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
bool waited = false ;
uint64 waitStart = Sys_Microseconds ( ) ;
2012-11-28 15:47:07 +00:00
while ( signalJobCount [ signalJobCount . Num ( ) - 1 ] . GetValue ( ) > 0 )
{
2012-11-26 18:58:24 +00:00
Sys_Yield ( ) ;
waited = true ;
}
version . Increment ( ) ;
2012-11-28 15:47:07 +00:00
while ( numThreadsExecuting . GetValue ( ) > 0 )
{
2012-11-26 18:58:24 +00:00
Sys_Yield ( ) ;
waited = true ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
jobList . SetNum ( 0 ) ;
signalJobCount . SetNum ( 0 ) ;
numSyncs = 0 ;
lastSignalJob = 0 ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
uint64 waitEnd = Sys_Microseconds ( ) ;
deferredThreadStats . waitTime = waited ? ( waitEnd - waitStart ) : 0 ;
}
memcpy ( & threadStats , & deferredThreadStats , sizeof ( threadStats ) ) ;
done = true ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : TryWait
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idParallelJobList_Threads : : TryWait ( )
{
if ( jobList . Num ( ) = = 0 | | signalJobCount [ signalJobCount . Num ( ) - 1 ] . GetValue ( ) < = 0 )
{
2012-11-26 18:58:24 +00:00
Wait ( ) ;
return true ;
}
return false ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : IsSubmitted
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idParallelJobList_Threads : : IsSubmitted ( ) const
{
2012-11-26 18:58:24 +00:00
return ! done ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : GetTotalProcessingTimeMicroSec
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
uint64 idParallelJobList_Threads : : GetTotalProcessingTimeMicroSec ( ) const
{
2012-11-26 18:58:24 +00:00
uint64 total = 0 ;
2012-11-28 15:47:07 +00:00
for ( int unit = 0 ; unit < MAX_THREADS ; unit + + )
{
2012-11-26 18:58:24 +00:00
total + = threadStats . threadExecTime [ unit ] ;
}
return total ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : GetTotalWastedTimeMicroSec
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
uint64 idParallelJobList_Threads : : GetTotalWastedTimeMicroSec ( ) const
{
2012-11-26 18:58:24 +00:00
uint64 total = 0 ;
2012-11-28 15:47:07 +00:00
for ( int unit = 0 ; unit < MAX_THREADS ; unit + + )
{
2012-11-26 18:58:24 +00:00
total + = threadStats . threadTotalTime [ unit ] - threadStats . threadExecTime [ unit ] ;
}
return total ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : GetUnitProcessingTimeMicroSec
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
uint64 idParallelJobList_Threads : : GetUnitProcessingTimeMicroSec ( int unit ) const
{
if ( unit < 0 | | unit > = MAX_THREADS )
{
2012-11-26 18:58:24 +00:00
return 0 ;
}
return threadStats . threadExecTime [ unit ] ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : GetUnitWastedTimeMicroSec
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
uint64 idParallelJobList_Threads : : GetUnitWastedTimeMicroSec ( int unit ) const
{
if ( unit < 0 | | unit > = MAX_THREADS )
{
2012-11-26 18:58:24 +00:00
return 0 ;
}
return threadStats . threadTotalTime [ unit ] - threadStats . threadExecTime [ unit ] ;
}
# ifndef _DEBUG
volatile float longJobTime ;
volatile jobRun_t longJobFunc ;
2012-11-28 15:47:07 +00:00
volatile void * longJobData ;
2012-11-26 18:58:24 +00:00
# endif
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : RunJobsInternal
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
int idParallelJobList_Threads : : RunJobsInternal ( unsigned int threadNum , threadJobListState_t & state , bool singleJob )
{
if ( state . version ! = version . GetValue ( ) )
{
2012-11-26 18:58:24 +00:00
// trying to run an old version of this list that is already done
return RUN_DONE ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
assert ( threadNum < MAX_THREADS ) ;
2012-11-28 15:47:07 +00:00
if ( deferredThreadStats . startTime = = 0 )
{
2012-11-26 18:58:24 +00:00
deferredThreadStats . startTime = Sys_Microseconds ( ) ; // first time any thread is running jobs from this list
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
int result = RUN_OK ;
2012-11-28 15:47:07 +00:00
do
{
2012-11-26 18:58:24 +00:00
// run through all signals and syncs before the last job that has been or is being executed
// this loop is really an optimization to minimize the time spent in the fetchLock section below
2012-11-28 15:47:07 +00:00
for ( ; state . lastJobIndex < ( int ) currentJob . GetValue ( ) & & state . lastJobIndex < jobList . Num ( ) ; state . lastJobIndex + + )
{
if ( jobList [ state . lastJobIndex ] . data = = & JOB_SIGNAL )
{
2012-11-26 18:58:24 +00:00
state . signalIndex + + ;
assert ( state . signalIndex < signalJobCount . Num ( ) ) ;
2012-11-28 15:47:07 +00:00
}
else if ( jobList [ state . lastJobIndex ] . data = = & JOB_SYNCHRONIZE )
{
2012-11-26 18:58:24 +00:00
assert ( state . signalIndex > 0 ) ;
2012-11-28 15:47:07 +00:00
if ( signalJobCount [ state . signalIndex - 1 ] . GetValue ( ) > 0 )
{
2012-11-26 18:58:24 +00:00
// stalled on a synchronization point
return ( result | RUN_STALLED ) ;
}
2012-11-28 15:47:07 +00:00
}
else if ( jobList [ state . lastJobIndex ] . data = = & JOB_LIST_DONE )
{
if ( signalJobCount [ signalJobCount . Num ( ) - 1 ] . GetValue ( ) > 0 )
{
2012-11-26 18:58:24 +00:00
// stalled on a synchronization point
return ( result | RUN_STALLED ) ;
}
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// try to lock to fetch a new job
2012-11-28 15:47:07 +00:00
if ( fetchLock . Increment ( ) = = 1 )
{
2012-11-26 18:58:24 +00:00
// grab a new job
state . nextJobIndex = currentJob . Increment ( ) - 1 ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// run through any remaining signals and syncs (this should rarely iterate more than once)
2012-11-28 15:47:07 +00:00
for ( ; state . lastJobIndex < = state . nextJobIndex & & state . lastJobIndex < jobList . Num ( ) ; state . lastJobIndex + + )
{
if ( jobList [ state . lastJobIndex ] . data = = & JOB_SIGNAL )
{
2012-11-26 18:58:24 +00:00
state . signalIndex + + ;
assert ( state . signalIndex < signalJobCount . Num ( ) ) ;
2012-11-28 15:47:07 +00:00
}
else if ( jobList [ state . lastJobIndex ] . data = = & JOB_SYNCHRONIZE )
{
2012-11-26 18:58:24 +00:00
assert ( state . signalIndex > 0 ) ;
2012-11-28 15:47:07 +00:00
if ( signalJobCount [ state . signalIndex - 1 ] . GetValue ( ) > 0 )
{
2012-11-26 18:58:24 +00:00
// return this job to the list
currentJob . Decrement ( ) ;
// release the fetch lock
fetchLock . Decrement ( ) ;
// stalled on a synchronization point
return ( result | RUN_STALLED ) ;
}
2012-11-28 15:47:07 +00:00
}
else if ( jobList [ state . lastJobIndex ] . data = = & JOB_LIST_DONE )
{
if ( signalJobCount [ signalJobCount . Num ( ) - 1 ] . GetValue ( ) > 0 )
{
2012-11-26 18:58:24 +00:00
// return this job to the list
currentJob . Decrement ( ) ;
// release the fetch lock
fetchLock . Decrement ( ) ;
// stalled on a synchronization point
return ( result | RUN_STALLED ) ;
}
// decrement the done count
doneGuards [ currentDoneGuard ] . Decrement ( ) ;
}
}
// release the fetch lock
fetchLock . Decrement ( ) ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
// release the fetch lock
fetchLock . Decrement ( ) ;
// another thread is fetching right now so consider stalled
return ( result | RUN_STALLED ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// if at the end of the job list we're done
2012-11-28 15:47:07 +00:00
if ( state . nextJobIndex > = jobList . Num ( ) )
{
2012-11-26 18:58:24 +00:00
return ( result | RUN_DONE ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// execute the next job
{
uint64 jobStart = Sys_Microseconds ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
jobList [ state . nextJobIndex ] . function ( jobList [ state . nextJobIndex ] . data ) ;
jobList [ state . nextJobIndex ] . executed = 1 ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
uint64 jobEnd = Sys_Microseconds ( ) ;
deferredThreadStats . threadExecTime [ threadNum ] + = jobEnd - jobStart ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
# ifndef _DEBUG
2012-11-28 15:47:07 +00:00
if ( jobs_longJobMicroSec . GetInteger ( ) > 0 )
{
if ( jobEnd - jobStart > jobs_longJobMicroSec . GetInteger ( )
& & GetId ( ) ! = JOBLIST_UTILITY )
{
2012-11-26 18:58:24 +00:00
longJobTime = ( jobEnd - jobStart ) * ( 1.0f / 1000.0f ) ;
longJobFunc = jobList [ state . nextJobIndex ] . function ;
longJobData = jobList [ state . nextJobIndex ] . data ;
2012-11-28 15:47:07 +00:00
const char * jobName = GetJobName ( jobList [ state . nextJobIndex ] . function ) ;
const char * jobListName = GetJobListName ( GetId ( ) ) ;
2012-11-26 18:58:24 +00:00
idLib : : Printf ( " %1.1f milliseconds for a single '%s' job from job list %s on thread %d \n " , longJobTime , jobName , jobListName , threadNum ) ;
}
}
# endif
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
result | = RUN_PROGRESS ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// decrease the job count for the current signal
2012-11-28 15:47:07 +00:00
if ( signalJobCount [ state . signalIndex ] . Decrement ( ) = = 0 )
{
2012-11-26 18:58:24 +00:00
// if this was the very last job of the job list
2012-11-28 15:47:07 +00:00
if ( state . signalIndex = = signalJobCount . Num ( ) - 1 )
{
2012-11-26 18:58:24 +00:00
deferredThreadStats . endTime = Sys_Microseconds ( ) ;
return ( result | RUN_DONE ) ;
}
}
2012-11-28 15:47:07 +00:00
}
while ( ! singleJob ) ;
2012-11-26 18:58:24 +00:00
return result ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : RunJobs
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
int idParallelJobList_Threads : : RunJobs ( unsigned int threadNum , threadJobListState_t & state , bool singleJob )
{
2012-11-26 18:58:24 +00:00
uint64 start = Sys_Microseconds ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
numThreadsExecuting . Increment ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
int result = RunJobsInternal ( threadNum , state , singleJob ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
numThreadsExecuting . Decrement ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
deferredThreadStats . threadTotalTime [ threadNum ] + = Sys_Microseconds ( ) - start ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
return result ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList_Threads : : WaitForOtherJobList
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idParallelJobList_Threads : : WaitForOtherJobList ( )
{
if ( waitForGuard ! = NULL )
{
if ( waitForGuard - > GetValue ( ) > 0 )
{
2012-11-26 18:58:24 +00:00
return true ;
}
}
return false ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : idParallelJobList
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idParallelJobList : : idParallelJobList ( jobListId_t id , jobListPriority_t priority , unsigned int maxJobs , unsigned int maxSyncs , const idColor * color )
{
2012-11-26 18:58:24 +00:00
assert ( priority > JOBLIST_PRIORITY_NONE ) ;
2012-11-28 15:47:07 +00:00
this - > jobListThreads = new ( TAG_JOBLIST ) idParallelJobList_Threads ( id , priority , maxJobs , maxSyncs ) ;
2012-11-26 18:58:24 +00:00
this - > color = color ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : ~ idParallelJobList
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idParallelJobList : : ~ idParallelJobList ( )
{
2012-11-26 18:58:24 +00:00
delete jobListThreads ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : AddJob
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idParallelJobList : : AddJob ( jobRun_t function , void * data )
{
2012-11-26 18:58:24 +00:00
assert ( IsRegisteredJob ( function ) ) ;
jobListThreads - > AddJob ( function , data ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : AddJobSPURS
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
CellSpursJob128 * idParallelJobList : : AddJobSPURS ( )
{
2012-11-26 18:58:24 +00:00
return NULL ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : InsertSyncPoint
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idParallelJobList : : InsertSyncPoint ( jobSyncType_t syncType )
{
2012-11-26 18:58:24 +00:00
jobListThreads - > InsertSyncPoint ( syncType ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : Wait
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idParallelJobList : : Wait ( )
{
if ( jobListThreads ! = NULL )
{
2012-11-26 18:58:24 +00:00
jobListThreads - > Wait ( ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : TryWait
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idParallelJobList : : TryWait ( )
{
2012-11-26 18:58:24 +00:00
bool done = true ;
2012-11-28 15:47:07 +00:00
if ( jobListThreads ! = NULL )
{
2012-11-26 18:58:24 +00:00
done & = jobListThreads - > TryWait ( ) ;
}
return done ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : Submit
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idParallelJobList : : Submit ( idParallelJobList * waitForJobList , int parallelism )
{
2012-11-26 18:58:24 +00:00
assert ( waitForJobList ! = this ) ;
jobListThreads - > Submit ( ( waitForJobList ! = NULL ) ? waitForJobList - > jobListThreads : NULL , parallelism ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : IsSubmitted
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idParallelJobList : : IsSubmitted ( ) const
{
2012-11-26 18:58:24 +00:00
return jobListThreads - > IsSubmitted ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : GetNumExecutedJobs
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
unsigned int idParallelJobList : : GetNumExecutedJobs ( ) const
{
2012-11-26 18:58:24 +00:00
return jobListThreads - > GetNumExecutedJobs ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : GetNumSyncs
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
unsigned int idParallelJobList : : GetNumSyncs ( ) const
{
2012-11-26 18:58:24 +00:00
return jobListThreads - > GetNumSyncs ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : GetSubmitTimeMicroSec
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
uint64 idParallelJobList : : GetSubmitTimeMicroSec ( ) const
{
2012-11-26 18:58:24 +00:00
return jobListThreads - > GetSubmitTimeMicroSec ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : GetStartTimeMicroSec
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
uint64 idParallelJobList : : GetStartTimeMicroSec ( ) const
{
2012-11-26 18:58:24 +00:00
return jobListThreads - > GetStartTimeMicroSec ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : GetFinishTimeMicroSec
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
uint64 idParallelJobList : : GetFinishTimeMicroSec ( ) const
{
2012-11-26 18:58:24 +00:00
return jobListThreads - > GetFinishTimeMicroSec ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : GetWaitTimeMicroSec
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
uint64 idParallelJobList : : GetWaitTimeMicroSec ( ) const
{
2012-11-26 18:58:24 +00:00
return jobListThreads - > GetWaitTimeMicroSec ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : GetTotalProcessingTimeMicroSec
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
uint64 idParallelJobList : : GetTotalProcessingTimeMicroSec ( ) const
{
2012-11-26 18:58:24 +00:00
return jobListThreads - > GetTotalProcessingTimeMicroSec ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : GetTotalWastedTimeMicroSec
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
uint64 idParallelJobList : : GetTotalWastedTimeMicroSec ( ) const
{
2012-11-26 18:58:24 +00:00
return jobListThreads - > GetTotalWastedTimeMicroSec ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : GetUnitProcessingTimeMicroSec
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
uint64 idParallelJobList : : GetUnitProcessingTimeMicroSec ( int unit ) const
{
2012-11-26 18:58:24 +00:00
return jobListThreads - > GetUnitProcessingTimeMicroSec ( unit ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : GetUnitWastedTimeMicroSec
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
uint64 idParallelJobList : : GetUnitWastedTimeMicroSec ( int unit ) const
{
2012-11-26 18:58:24 +00:00
return jobListThreads - > GetUnitWastedTimeMicroSec ( unit ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobList : : GetId
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
jobListId_t idParallelJobList : : GetId ( ) const
{
2012-11-26 18:58:24 +00:00
return jobListThreads - > GetId ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
idJobThread
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
const int JOB_THREAD_STACK_SIZE = 256 * 1024 ; // same size as the SPU local store
2012-11-28 15:47:07 +00:00
struct threadJobList_t
{
idParallelJobList_Threads * jobList ;
2012-11-26 18:58:24 +00:00
int version ;
} ;
static idCVar jobs_prioritize ( " jobs_prioritize " , " 1 " , CVAR_BOOL | CVAR_NOCHEAT , " prioritize job lists " ) ;
2012-11-28 15:47:07 +00:00
class idJobThread : public idSysThread
{
2012-11-26 18:58:24 +00:00
public :
2012-11-28 15:47:07 +00:00
idJobThread ( ) ;
~ idJobThread ( ) ;
2012-11-26 18:58:24 +00:00
void Start ( core_t core , unsigned int threadNum ) ;
2012-11-28 15:47:07 +00:00
void AddJobList ( idParallelJobList_Threads * jobList ) ;
2012-11-26 18:58:24 +00:00
private :
threadJobList_t jobLists [ MAX_JOBLISTS ] ; // cyclic buffer with job lists
unsigned int firstJobList ; // index of the last job list the thread grabbed
unsigned int lastJobList ; // index where the next job list to work on will be added
idSysMutex addJobMutex ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
unsigned int threadNum ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
virtual int Run ( ) ;
} ;
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idJobThread : : idJobThread
= = = = = = = = = = = = = = = = = = = = = = = =
*/
idJobThread : : idJobThread ( ) :
2012-11-28 15:47:07 +00:00
firstJobList ( 0 ) ,
lastJobList ( 0 ) ,
threadNum ( 0 )
{
2012-11-26 18:58:24 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idJobThread : : ~ idJobThread
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idJobThread : : ~ idJobThread ( )
{
2012-11-26 18:58:24 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idJobThread : : Start
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idJobThread : : Start ( core_t core , unsigned int threadNum )
{
2012-11-26 18:58:24 +00:00
this - > threadNum = threadNum ;
2013-03-25 23:34:24 +00:00
// DG: change threadname from "JobListProcessor_%d" to "JLProc_%d", because Linux
// has a 15 (+ \0) char limit for threadnames.
// furthermore: va is not thread safe, use snPrintf instead
char name [ 16 ] ;
idStr : : snPrintf ( name , 16 , " JLProc_%d " , threadNum ) ;
StartWorkerThread ( name , core , THREAD_NORMAL , JOB_THREAD_STACK_SIZE ) ;
// DG end
2012-11-26 18:58:24 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idJobThread : : AddJobList
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idJobThread : : AddJobList ( idParallelJobList_Threads * jobList )
{
2012-11-26 18:58:24 +00:00
// must lock because multiple threads may try to add new job lists at the same time
addJobMutex . Lock ( ) ;
2012-11-28 15:47:07 +00:00
// wait until there is space available because in rare cases multiple versions of the same job lists may still be queued
while ( lastJobList - firstJobList > = MAX_JOBLISTS )
{
2012-11-26 18:58:24 +00:00
Sys_Yield ( ) ;
}
assert ( lastJobList - firstJobList < MAX_JOBLISTS ) ;
jobLists [ lastJobList & ( MAX_JOBLISTS - 1 ) ] . jobList = jobList ;
jobLists [ lastJobList & ( MAX_JOBLISTS - 1 ) ] . version = jobList - > GetVersion ( ) ;
lastJobList + + ;
addJobMutex . Unlock ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idJobThread : : Run
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
int idJobThread : : Run ( )
{
2012-11-26 18:58:24 +00:00
threadJobListState_t threadJobListState [ MAX_JOBLISTS ] ;
int numJobLists = 0 ;
int lastStalledJobList = - 1 ;
2012-11-28 15:47:07 +00:00
while ( ! IsTerminating ( ) )
{
2012-11-26 18:58:24 +00:00
// fetch any new job lists and add them to the local list
2012-11-28 15:47:07 +00:00
if ( numJobLists < MAX_JOBLISTS & & firstJobList < lastJobList )
{
2012-11-26 18:58:24 +00:00
threadJobListState [ numJobLists ] . jobList = jobLists [ firstJobList & ( MAX_JOBLISTS - 1 ) ] . jobList ;
threadJobListState [ numJobLists ] . version = jobLists [ firstJobList & ( MAX_JOBLISTS - 1 ) ] . version ;
threadJobListState [ numJobLists ] . signalIndex = 0 ;
threadJobListState [ numJobLists ] . lastJobIndex = 0 ;
threadJobListState [ numJobLists ] . nextJobIndex = - 1 ;
numJobLists + + ;
firstJobList + + ;
}
2012-11-28 15:47:07 +00:00
if ( numJobLists = = 0 )
{
2012-11-26 18:58:24 +00:00
break ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
int currentJobList = 0 ;
jobListPriority_t priority = JOBLIST_PRIORITY_NONE ;
2012-11-28 15:47:07 +00:00
if ( lastStalledJobList < 0 )
{
2012-11-26 18:58:24 +00:00
// find the job list with the highest priority
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < numJobLists ; i + + )
{
if ( threadJobListState [ i ] . jobList - > GetPriority ( ) > priority & & ! threadJobListState [ i ] . jobList - > WaitForOtherJobList ( ) )
{
2012-11-26 18:58:24 +00:00
priority = threadJobListState [ i ] . jobList - > GetPriority ( ) ;
currentJobList = i ;
}
}
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
// try to hide the stall with a job from a list that has equal or higher priority
currentJobList = lastStalledJobList ;
priority = threadJobListState [ lastStalledJobList ] . jobList - > GetPriority ( ) ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < numJobLists ; i + + )
{
if ( i ! = lastStalledJobList & & threadJobListState [ i ] . jobList - > GetPriority ( ) > = priority & & ! threadJobListState [ i ] . jobList - > WaitForOtherJobList ( ) )
{
2012-11-26 18:58:24 +00:00
priority = threadJobListState [ i ] . jobList - > GetPriority ( ) ;
currentJobList = i ;
}
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// if the priority is high then try to run through the whole list to reduce the overhead
// otherwise run a single job and re-evaluate priorities for the next job
bool singleJob = ( priority = = JOBLIST_PRIORITY_HIGH ) ? false : jobs_prioritize . GetBool ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// try running one or more jobs from the current job list
int result = threadJobListState [ currentJobList ] . jobList - > RunJobs ( threadNum , threadJobListState [ currentJobList ] , singleJob ) ;
2012-11-28 15:47:07 +00:00
if ( ( result & idParallelJobList_Threads : : RUN_DONE ) ! = 0 )
{
2012-11-26 18:58:24 +00:00
// done with this job list so remove it from the local list
2012-11-28 15:47:07 +00:00
for ( int i = currentJobList ; i < numJobLists - 1 ; i + + )
{
2012-11-26 18:58:24 +00:00
threadJobListState [ i ] = threadJobListState [ i + 1 ] ;
}
numJobLists - - ;
lastStalledJobList = - 1 ;
2012-11-28 15:47:07 +00:00
}
else if ( ( result & idParallelJobList_Threads : : RUN_STALLED ) ! = 0 )
{
2012-11-26 18:58:24 +00:00
// yield when stalled on the same job list again without making any progress
2012-11-28 15:47:07 +00:00
if ( currentJobList = = lastStalledJobList )
{
if ( ( result & idParallelJobList_Threads : : RUN_PROGRESS ) = = 0 )
{
2012-11-26 18:58:24 +00:00
Sys_Yield ( ) ;
}
}
lastStalledJobList = currentJobList ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
lastStalledJobList = - 1 ;
}
}
return 0 ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobManagerLocal
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
extern void Sys_CPUCount ( int & logicalNum , int & coreNum , int & packageNum ) ;
2012-11-26 18:58:24 +00:00
// WINDOWS LOGICAL PROCESSOR LIMITS:
//
// http://download.microsoft.com/download/5/7/7/577a5684-8a83-43ae-9272-ff260a9c20e2/Hyper-thread_Windows.doc
//
// Physical Logical (Cores + HT)
2012-11-28 15:47:07 +00:00
// Windows XP Home Edition 1 2
// Windows XP Professional 2 4
// Windows Server 2003, Standard Edition 4 8
// Windows Server 2003, Enterprise Edition 8 16
2012-11-26 18:58:24 +00:00
// Windows Server 2003, Datacenter Edition 32 32
//
// Windows Vista ? ?
//
// Windows 7 Starter 1 32/64
// Windows 7 Home Basic 1 32/64
// Windows 7 Professional 2 32/64
//
//
// Hyperthreading is not dead yet. Intel's Core i7 Processor is quad-core with HT for 8 logicals.
// DOOM3: We don't have that many jobs, so just set this fairly low so we don't spin up a ton of idle threads
# define MAX_JOB_THREADS 2
# define NUM_JOB_THREADS "2"
# define JOB_THREAD_CORES { CORE_ANY, CORE_ANY, CORE_ANY, CORE_ANY, \
CORE_ANY , CORE_ANY , CORE_ANY , CORE_ANY , \
CORE_ANY , CORE_ANY , CORE_ANY , CORE_ANY , \
CORE_ANY , CORE_ANY , CORE_ANY , CORE_ANY , \
CORE_ANY , CORE_ANY , CORE_ANY , CORE_ANY , \
CORE_ANY , CORE_ANY , CORE_ANY , CORE_ANY , \
CORE_ANY , CORE_ANY , CORE_ANY , CORE_ANY , \
CORE_ANY , CORE_ANY , CORE_ANY , CORE_ANY }
idCVar jobs_numThreads ( " jobs_numThreads " , NUM_JOB_THREADS , CVAR_INTEGER | CVAR_NOCHEAT , " number of threads used to crunch through jobs " , 0 , MAX_JOB_THREADS ) ;
2012-11-28 15:47:07 +00:00
class idParallelJobManagerLocal : public idParallelJobManager
{
2012-11-26 18:58:24 +00:00
public :
virtual ~ idParallelJobManagerLocal ( ) { }
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
virtual void Init ( ) ;
virtual void Shutdown ( ) ;
2012-11-28 15:47:07 +00:00
virtual idParallelJobList * AllocJobList ( jobListId_t id , jobListPriority_t priority , unsigned int maxJobs , unsigned int maxSyncs , const idColor * color ) ;
virtual void FreeJobList ( idParallelJobList * jobList ) ;
2012-11-26 18:58:24 +00:00
virtual int GetNumJobLists ( ) const ;
virtual int GetNumFreeJobLists ( ) const ;
2012-11-28 15:47:07 +00:00
virtual idParallelJobList * GetJobList ( int index ) ;
2012-11-26 18:58:24 +00:00
virtual int GetNumProcessingUnits ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
virtual void WaitForAllJobLists ( ) ;
2012-11-28 15:47:07 +00:00
void Submit ( idParallelJobList_Threads * jobList , int parallelism ) ;
2012-11-26 18:58:24 +00:00
private :
idJobThread threads [ MAX_JOB_THREADS ] ;
unsigned int maxThreads ;
int numPhysicalCpuCores ;
int numLogicalCpuCores ;
int numCpuPackages ;
2012-11-28 15:47:07 +00:00
idStaticList < idParallelJobList * , MAX_JOBLISTS > jobLists ;
2012-11-26 18:58:24 +00:00
} ;
idParallelJobManagerLocal parallelJobManagerLocal ;
2012-11-28 15:47:07 +00:00
idParallelJobManager * parallelJobManager = & parallelJobManagerLocal ;
2012-11-26 18:58:24 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = =
SubmitJobList
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void SubmitJobList ( idParallelJobList_Threads * jobList , int parallelism )
{
2012-11-26 18:58:24 +00:00
parallelJobManagerLocal . Submit ( jobList , parallelism ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobManagerLocal : : Init
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idParallelJobManagerLocal : : Init ( )
{
2012-11-26 18:58:24 +00:00
// on consoles this will have specific cores for the threads, but on PC they will all be CORE_ANY
core_t cores [ ] = JOB_THREAD_CORES ;
assert ( sizeof ( cores ) / sizeof ( cores [ 0 ] ) > = MAX_JOB_THREADS ) ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < MAX_JOB_THREADS ; i + + )
{
2012-11-26 18:58:24 +00:00
threads [ i ] . Start ( cores [ i ] , i ) ;
}
maxThreads = jobs_numThreads . GetInteger ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
Sys_CPUCount ( numPhysicalCpuCores , numLogicalCpuCores , numCpuPackages ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobManagerLocal : : Shutdown
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idParallelJobManagerLocal : : Shutdown ( )
{
for ( int i = 0 ; i < MAX_JOB_THREADS ; i + + )
{
2012-11-26 18:58:24 +00:00
threads [ i ] . StopThread ( ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobManagerLocal : : AllocJobList
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idParallelJobList * idParallelJobManagerLocal : : AllocJobList ( jobListId_t id , jobListPriority_t priority , unsigned int maxJobs , unsigned int maxSyncs , const idColor * color )
{
for ( int i = 0 ; i < jobLists . Num ( ) ; i + + )
{
if ( jobLists [ i ] - > GetId ( ) = = id )
{
2012-11-26 18:58:24 +00:00
// idStudio may cause job lists to be allocated multiple times
}
}
2012-11-28 15:47:07 +00:00
idParallelJobList * jobList = new ( TAG_JOBLIST ) idParallelJobList ( id , priority , maxJobs , maxSyncs , color ) ;
2012-11-26 18:58:24 +00:00
jobLists . Append ( jobList ) ;
return jobList ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobManagerLocal : : FreeJobList
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idParallelJobManagerLocal : : FreeJobList ( idParallelJobList * jobList )
{
if ( jobList = = NULL )
{
2012-11-26 18:58:24 +00:00
return ;
}
// wait for all job threads to finish because job list deletion is not thread safe
2012-11-28 15:47:07 +00:00
for ( unsigned int i = 0 ; i < maxThreads ; i + + )
{
2012-11-26 18:58:24 +00:00
threads [ i ] . WaitForThread ( ) ;
}
int index = jobLists . FindIndex ( jobList ) ;
assert ( index > = 0 & & jobLists [ index ] = = jobList ) ;
jobLists [ index ] - > Wait ( ) ;
delete jobLists [ index ] ;
jobLists . RemoveIndexFast ( index ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobManagerLocal : : GetNumJobLists
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
int idParallelJobManagerLocal : : GetNumJobLists ( ) const
{
2012-11-26 18:58:24 +00:00
return jobLists . Num ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobManagerLocal : : GetNumFreeJobLists
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
int idParallelJobManagerLocal : : GetNumFreeJobLists ( ) const
{
2012-11-26 18:58:24 +00:00
return MAX_JOBLISTS - jobLists . Num ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobManagerLocal : : GetJobList
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idParallelJobList * idParallelJobManagerLocal : : GetJobList ( int index )
{
2012-11-26 18:58:24 +00:00
return jobLists [ index ] ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobManagerLocal : : GetNumProcessingUnits
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
int idParallelJobManagerLocal : : GetNumProcessingUnits ( )
{
2012-11-26 18:58:24 +00:00
return maxThreads ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobManagerLocal : : WaitForAllJobLists
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idParallelJobManagerLocal : : WaitForAllJobLists ( )
{
2012-11-26 18:58:24 +00:00
// wait for all job lists to complete
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < jobLists . Num ( ) ; i + + )
{
2012-11-26 18:58:24 +00:00
jobLists [ i ] - > Wait ( ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idParallelJobManagerLocal : : Submit
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idParallelJobManagerLocal : : Submit ( idParallelJobList_Threads * jobList , int parallelism )
{
if ( jobs_numThreads . IsModified ( ) )
{
2012-11-26 18:58:24 +00:00
maxThreads = idMath : : ClampInt ( 0 , MAX_JOB_THREADS , jobs_numThreads . GetInteger ( ) ) ;
jobs_numThreads . ClearModified ( ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// determine the number of threads to use
int numThreads = maxThreads ;
2012-11-28 15:47:07 +00:00
if ( parallelism = = JOBLIST_PARALLELISM_DEFAULT )
{
2012-11-26 18:58:24 +00:00
numThreads = maxThreads ;
2012-11-28 15:47:07 +00:00
}
else if ( parallelism = = JOBLIST_PARALLELISM_MAX_CORES )
{
2012-11-26 18:58:24 +00:00
numThreads = numLogicalCpuCores ;
2012-11-28 15:47:07 +00:00
}
else if ( parallelism = = JOBLIST_PARALLELISM_MAX_THREADS )
{
2012-11-26 18:58:24 +00:00
numThreads = MAX_JOB_THREADS ;
2012-11-28 15:47:07 +00:00
}
else if ( parallelism > MAX_JOB_THREADS )
{
2012-11-26 18:58:24 +00:00
numThreads = MAX_JOB_THREADS ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
numThreads = parallelism ;
}
2012-11-28 15:47:07 +00:00
if ( numThreads < = 0 )
{
2012-11-26 18:58:24 +00:00
threadJobListState_t state ( jobList - > GetVersion ( ) ) ;
jobList - > RunJobs ( 0 , state , false ) ;
return ;
}
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < numThreads ; i + + )
{
2012-11-26 18:58:24 +00:00
threads [ i ] . AddJobList ( jobList ) ;
threads [ i ] . SignalWork ( ) ;
}
2013-03-25 23:34:24 +00:00
}