From a7757a2b0dd52c0dc408fdbd412bcc8a7fc27c4d Mon Sep 17 00:00:00 2001 From: pouillot Date: Tue, 13 Jul 2010 20:56:47 +0000 Subject: [PATCH] Re #132 : Added inhibitable thread affinity (OK under WinXP, WIP under Linux) git-svn-id: https://svn.code.sf.net/p/speed-dreams/code/trunk@2565 30fe4595-0a0c-4342-8851-515496e4dcbd Former-commit-id: e213dd158c71de0e304ae0256c3e8d6be540e805 Former-commit-id: fe3be73c8454a434f87f9df725ec2137ac4385d7 --- src/interfaces/raceman.h | 6 + src/libs/confscreens/simuconfig.cpp | 52 ++++++- src/libs/raceengineclient/raceengine.cpp | 30 +++- src/libs/raceengineclient/raceengine.xml | 3 +- src/libs/tgf/directory.cpp | 6 +- src/libs/tgf/os.cpp | 26 ++++ src/libs/tgf/os.h | 17 ++- src/libs/tgf/tgf.cpp | 74 +-------- src/libs/tgf/tgf.h | 10 +- src/linux/linuxspec.cpp | 173 ++++++++++++++++++++- src/windows/windowsspec.cpp | 187 +++++++++++++++-------- 11 files changed, 420 insertions(+), 164 deletions(-) diff --git a/src/interfaces/raceman.h b/src/interfaces/raceman.h index c8a7a229..e9662f60 100755 --- a/src/interfaces/raceman.h +++ b/src/interfaces/raceman.h @@ -348,6 +348,11 @@ typedef struct RmInfo #define RM_VAL_MORE_CLOUDS "more clouds" #define RM_VAL_OVERCAST_CLOUDS "overcast clouds" +#define RM_VAL_NO_CLOUDS "no clouds" +#define RM_VAL_SCARCE_CLOUDS "scarce clouds" +#define RM_VAL_MORE_CLOUDS "more clouds" +#define RM_VAL_OVERCAST_CLOUDS "overcast clouds" + /* Movie capture */ #define RM_SECT_MOVIE_CAPTURE "Movie Capture" @@ -393,6 +398,7 @@ typedef struct RmInfo #define RM_SECT_RACE_ENGINE "Race Engine" #define RM_ATTR_MULTI_THREADING "multi-threading" +#define RM_ATTR_THREAD_AFFINITY "thread affinity" #define RM_VAL_AUTO "auto" #define RM_VAL_ON "on" diff --git a/src/libs/confscreens/simuconfig.cpp b/src/libs/confscreens/simuconfig.cpp index 09c4cd90..d1bd53eb 100644 --- a/src/libs/confscreens/simuconfig.cpp +++ b/src/libs/confscreens/simuconfig.cpp @@ -44,25 +44,33 @@ static int CurSimuVersion = 0; static const char *MultiThreadSchemeList[] = {RM_VAL_AUTO, RM_VAL_ON, RM_VAL_OFF}; static const int NbMultiThreadSchemes = sizeof(MultiThreadSchemeList) / sizeof(MultiThreadSchemeList[0]); +/* list of available thread affinity schemes */ +static const char *ThreadAffinitySchemeList[] = {RM_VAL_ON, RM_VAL_OFF}; +static const int NbThreadAffinitySchemes = sizeof(ThreadAffinitySchemeList) / sizeof(ThreadAffinitySchemeList[0]); + #ifdef ReMultiThreaded -static int CurMultiThreadScheme = 0; +static int CurMultiThreadScheme = 0; // Auto +static int CurThreadAffinityScheme = 0; // On #else -static int CurMultiThreadScheme = 2; +static int CurMultiThreadScheme = 2; // Off +static int CurThreadAffinityScheme = 1; // Off #endif /* gui label ids */ static int SimuVersionId; static int MultiThreadSchemeId; +static int ThreadAffinitySchemeId; /* gui screen handles */ -static void *ScrHandle = NULL; -static void *PrevScrHandle = NULL; +static void *ScrHandle = NULL; +static void *PrevScrHandle = NULL; static void loadSimuCfg(void) { const char *simuVersionName; const char *multiThreadSchemeName; + const char *threadAffinitySchemeName; int i; char buf[1024]; @@ -88,12 +96,21 @@ static void loadSimuCfg(void) } } + threadAffinitySchemeName = GfParmGetStr(paramHandle, RM_SECT_RACE_ENGINE, RM_ATTR_THREAD_AFFINITY, ThreadAffinitySchemeList[0]); + for (i = 0; i < NbThreadAffinitySchemes; i++) { + if (strcmp(threadAffinitySchemeName, ThreadAffinitySchemeList[i]) == 0) { + CurThreadAffinityScheme = i; + break; + } + } + #endif GfParmReleaseHandle(paramHandle); GfuiLabelSetText(ScrHandle, SimuVersionId, SimuVersionList[CurSimuVersion]); GfuiLabelSetText(ScrHandle, MultiThreadSchemeId, MultiThreadSchemeList[CurMultiThreadScheme]); + GfuiLabelSetText(ScrHandle, ThreadAffinitySchemeId, ThreadAffinitySchemeList[CurThreadAffinityScheme]); } @@ -106,6 +123,7 @@ static void storeSimuCfg(void * /* dummy */) void *paramHandle = GfParmReadFile(buf, GFPARM_RMODE_REREAD | GFPARM_RMODE_CREAT); GfParmSetStr(paramHandle, RM_SECT_MODULES, RM_ATTR_MOD_SIMU, SimuVersionList[CurSimuVersion]); GfParmSetStr(paramHandle, RM_SECT_RACE_ENGINE, RM_ATTR_MULTI_THREADING, MultiThreadSchemeList[CurMultiThreadScheme]); + GfParmSetStr(paramHandle, RM_SECT_RACE_ENGINE, RM_ATTR_THREAD_AFFINITY, ThreadAffinitySchemeList[CurThreadAffinityScheme]); GfParmWriteFile(NULL, paramHandle, "raceengine"); GfParmReleaseHandle(paramHandle); @@ -114,24 +132,35 @@ static void storeSimuCfg(void * /* dummy */) return; } -/* change the simulation version */ +/* Change the simulation version */ static void onChangeSimuVersion(void *vp) { CurSimuVersion = (CurSimuVersion + NbSimuVersions + (int)(long)vp) % NbSimuVersions; - GfuiLabelSetText(ScrHandle, SimuVersionId, SimuVersionList[CurSimuVersion]); + GfuiLabelSetText(ScrHandle, SimuVersionId, SimuVersionList[CurSimuVersion]); } -/* change the multi-threading scheme */ +/* Change the multi-threading scheme */ static void onChangeMultiThreadScheme(void *vp) { CurMultiThreadScheme = (CurMultiThreadScheme + NbMultiThreadSchemes + (int)(long)vp) % NbMultiThreadSchemes; - GfuiLabelSetText(ScrHandle, MultiThreadSchemeId, MultiThreadSchemeList[CurMultiThreadScheme]); + GfuiLabelSetText(ScrHandle, MultiThreadSchemeId, MultiThreadSchemeList[CurMultiThreadScheme]); +} + + +/* Change the thread affinity scheme */ +static void +onChangeThreadAffinityScheme(void *vp) +{ + CurThreadAffinityScheme = + (CurThreadAffinityScheme + NbThreadAffinitySchemes + (int)(long)vp) % NbThreadAffinitySchemes; + + GfuiLabelSetText(ScrHandle, ThreadAffinitySchemeId, ThreadAffinitySchemeList[CurThreadAffinityScheme]); } @@ -166,6 +195,13 @@ SimuMenuInit(void *prevMenu) CreateButtonControl(ScrHandle, menuDescHdle, "mthreadleftarrow", (void*)-1, onChangeMultiThreadScheme); CreateButtonControl(ScrHandle, menuDescHdle, "mthreadrightarrow", (void*)1, onChangeMultiThreadScheme); #endif + + ThreadAffinitySchemeId = CreateLabelControl(ScrHandle, menuDescHdle, "threadafflabel"); + +#ifdef ReMultiThreaded + CreateButtonControl(ScrHandle, menuDescHdle, "threadaffleftarrow", (void*)-1, onChangeThreadAffinityScheme); + CreateButtonControl(ScrHandle, menuDescHdle, "threadaffrightarrow", (void*)1, onChangeThreadAffinityScheme); +#endif CreateButtonControl(ScrHandle, menuDescHdle, "accept", PrevScrHandle, storeSimuCfg); CreateButtonControl(ScrHandle, menuDescHdle, "cancel", PrevScrHandle, GfuiScreenActivate); diff --git a/src/libs/raceengineclient/raceengine.cpp b/src/libs/raceengineclient/raceengine.cpp index 94a2578d..49a948df 100644 --- a/src/libs/raceengineclient/raceengine.cpp +++ b/src/libs/raceengineclient/raceengine.cpp @@ -66,6 +66,11 @@ tRmInfo* ReGetSituation() #include "SDL/SDL.h" #include "SDL/SDL_thread.h" +// Index of the CPU to use for thread affinity if any and if there are at least 2 ones. +static const int NGraphicsCPUId = 0; +static const int NUpdaterCPUId = 1; + +// The situation updater thread class. class reSituationUpdater { public: @@ -134,6 +139,9 @@ private: //! True if the updater is actually threaded (may be not the case) bool _bThreaded; + //! True if thread affinity has to be applied (even in case !_bThreaded) + bool _bThreadAffinity; + //! Flag to set in order to terminate the updater. bool _bTerminate; }; @@ -1301,6 +1309,11 @@ int reSituationUpdater::threadLoop() // Current real time. double realTime; + // Apply thread affinity to the current = situation updater thread if specified. + // Note: No need to reset the affinity, as the thread is just born. + if (_bThreadAffinity) + GfSetThreadAffinity(NUpdaterCPUId); + GfOut("SituationUpdater thread is started.\n"); do @@ -1370,7 +1383,8 @@ reSituationUpdater::reSituationUpdater(tRmInfo* pReInfo) _pCurrReInfo = pReInfo; _nInitDrivers = _pCurrReInfo->s->_ncars; - // No dedicated thread if only 1 CPU/core. + // Determine if we have a dedicated separate thread or not + // (according to the user settings, and the actual number of CPUs). snprintf(buf, 1024, "%s%s", GetLocalDir(), RACE_ENG_CFG); void *paramHandle = GfParmReadFile(buf, GFPARM_RMODE_REREAD | GFPARM_RMODE_CREAT); const char* pszMultiThreadScheme = @@ -1383,8 +1397,19 @@ reSituationUpdater::reSituationUpdater(tRmInfo* pReInfo) else // Can't be anything else than RM_VAL_AUTO _bThreaded = GfGetNumberOfCPUs() > 1; + // Determine if we apply some thread affinity or not (according to the user settings). + const char* pszThreadAffinityScheme = + GfParmGetStr(paramHandle, RM_SECT_RACE_ENGINE, RM_ATTR_THREAD_AFFINITY, RM_VAL_OFF); + _bThreadAffinity = strcmp(pszThreadAffinityScheme, RM_VAL_ON) == 0; + GfParmReleaseHandle(paramHandle); + // Apply thread affinity to the current = main = graphics thread + // (and don't forget to reset it when specified : + // user settings may have changed since last race). + GfSetThreadAffinity(_bThreadAffinity ? NGraphicsCPUId : GfAffinityAnyCPU); + + // Initialize termination flag. _bTerminate = false; if (_bThreaded) @@ -1404,6 +1429,9 @@ reSituationUpdater::reSituationUpdater(tRmInfo* pReInfo) _pDataMutex = 0; _pUpdateThread = 0; } + + GfOut("SituationUpdater initialized (%sseparate thread, CPU affinity %s).\n", + (_bThreaded ? "" : "no "), (_bThreadAffinity ? "On" : "Off")); } reSituationUpdater::~reSituationUpdater() diff --git a/src/libs/raceengineclient/raceengine.xml b/src/libs/raceengineclient/raceengine.xml index 87ea1a60..7e6582c1 100644 --- a/src/libs/raceengineclient/raceengine.xml +++ b/src/libs/raceengineclient/raceengine.xml @@ -14,7 +14,7 @@ - +
@@ -30,6 +30,7 @@
+
diff --git a/src/libs/tgf/directory.cpp b/src/libs/tgf/directory.cpp index bea24d4c..8f5f8ae5 100644 --- a/src/libs/tgf/directory.cpp +++ b/src/libs/tgf/directory.cpp @@ -23,10 +23,8 @@ @ingroup dir */ -#include -#ifdef WIN32 -#include -#endif +#include + #include "tgf.h" #include "os.h" diff --git a/src/libs/tgf/os.cpp b/src/libs/tgf/os.cpp index 9b26a39f..9a9f2a30 100644 --- a/src/libs/tgf/os.cpp +++ b/src/libs/tgf/os.cpp @@ -52,3 +52,29 @@ GfTimeClock(void) return 0; } } + +/* Retrieve the actual number of CPUs in the system +* Note that a core is considered here as a "CPU", and an Intel hyper-threaded processor +* will report twice as many "CPUs" as actual cores ... +*/ +unsigned GfGetNumberOfCPUs() +{ + if (GfOs.sysGetNumberOfCPUs) { + return GfOs.sysGetNumberOfCPUs(); + } else { + return 0; + } +} + +/* Force the current thread to run on the specified CPU. +* @param nCPUId the index in [0, # of actual CPUs on the system [ (any other value will actually reset the thread affinity to the "system" affinity mask, meaning no special processor / core assignement) +* @return true if any error occured, false otherwise + */ +bool GfSetThreadAffinity(int nCPUId) +{ + if (GfOs.sysSetThreadAffinity) { + return GfOs.sysSetThreadAffinity(nCPUId); + } else { + return false; + } +} diff --git a/src/libs/tgf/os.h b/src/libs/tgf/os.h index 4aa9f1ee..0ba8aad5 100644 --- a/src/libs/tgf/os.h +++ b/src/libs/tgf/os.h @@ -28,12 +28,18 @@ typedef int (*tfModUnloadList)(tModList **); typedef int (*tfModGetInfo)(unsigned int, const char*, tModList **); typedef int (*tfModGetInfoDir)(unsigned int, const char*, int, tModList **); typedef int (*tfModFreeInfoList)(tModList **); + /* directory interface */ typedef tFList *(*tfDirGetList)(const char *); typedef tFList *(*tfDirGetListFiltered)(const char *, const char *); + /* time interface */ typedef double (*tfTimeClock)(void); +/* System interface */ +typedef unsigned (*tfSysGetNumberOfCPUs)(void); +typedef bool (*tfSysSetThreadAffinity)(int nCPUId); + typedef struct { tfModLoad modLoad; tfModLoadDir modLoadDir; @@ -44,15 +50,10 @@ typedef struct { tfDirGetList dirGetList; tfDirGetListFiltered dirGetListFiltered; tfTimeClock timeClock; + tfSysSetThreadAffinity sysSetThreadAffinity; + tfSysGetNumberOfCPUs sysGetNumberOfCPUs; } tGfOs; -#ifdef WIN32 -#ifdef tgf_EXPORTS -__declspec(dllexport) -#else // TGF_EXPORTS -__declspec(dllimport) -#endif // TGF_EXPORTS -#endif // WIN32 -extern tGfOs GfOs; +TGF_API extern tGfOs GfOs; #endif /* _OS__H_ */ diff --git a/src/libs/tgf/tgf.cpp b/src/libs/tgf/tgf.cpp index 9b223e42..0453fa11 100644 --- a/src/libs/tgf/tgf.cpp +++ b/src/libs/tgf/tgf.cpp @@ -22,16 +22,10 @@ #else #include #include -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) -#include -#include -#else -#include -#endif #endif #include -#include +#include #include #include "tgf.h" @@ -702,7 +696,11 @@ int GfNearestPow2 (int x) return (1 << r); } -// Create a directory +/** Create a directory and the parents if needed + @ingroup dir + @param dir full directory path-name + @return GF_DIR_CREATED on success, GF_DIR_CREATION_FAILED otherwise. + */ int GfCreateDir(const char *path) { if (path == NULL) { @@ -752,63 +750,3 @@ int GfCreateDir(const char *path) return (err == -1 && errno != EEXIST) ? GF_DIR_CREATION_FAILED : GF_DIR_CREATED; } - -/* Get the actual number of CPUs / cores - - TODO: Be careful about fake CPUs like those displayed by hyperthreaded processors ... - TODO: Test under pltaforms other than Windows, Linux, Solaris, AIX (mainly BDS and MacOS X). -*/ -int GfGetNumberOfCPUs() -{ - int nCPUs = 0; - -// Windows -#if defined(WIN32) - - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - - nCPUs = sysinfo.dwNumberOfProcessors; - -// MacOS X, FreeBSD, OpenBSD, NetBSD, etc ... -#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) - - nt mib[4]; - size_t len; - - // Set the mib for hw.ncpu - - // Get the number of CPUs from the system - // 1) Try HW_AVAILCPU first. - mib[0] = CTL_HW; - mib[1] = HW_AVAILCPU; // alternatively, try HW_NCPU; - sysctl(mib, 2, &nCPUs, &len, NULL, 0); - - if (nCPUs < 1) - { - // 2) Try alternatively HW_NCPU. - mib[1] = HW_NCPU; - sysctl(mib, 2, &nCPUs, &len, NULL, 0); - } - -// Linux, Solaris, AIX -#elif defined(linux) || defined(__linux__) - - nCPUs = sysconf(_SC_NPROCESSORS_ONLN); - -#else - -#warning "Unsupported OS" - -#endif // WIN32 - - if (nCPUs < 1) - { - GfOut("Could not get the number of CPUs here ; assuming only 1\n"); - nCPUs = 1; - } - else - GfOut("Detected %d CPUs\n", nCPUs); - - return nCPUs; -} diff --git a/src/libs/tgf/tgf.h b/src/libs/tgf/tgf.h index f2c701b0..3f88c919 100644 --- a/src/libs/tgf/tgf.h +++ b/src/libs/tgf/tgf.h @@ -432,14 +432,20 @@ typedef struct TGF_API tdble gfMean(tdble v, tMeanVal *pvt, int n, int w); TGF_API void gfMeanReset(tdble v, tMeanVal *pvt); -/* Get the actual number of CPUs / cores */ -TGF_API int GfGetNumberOfCPUs(); +/******************** + * System Interface * + ********************/ +TGF_API unsigned GfGetNumberOfCPUs(); + +enum { GfAffinityAnyCPU = -1 }; +TGF_API bool GfSetThreadAffinity(int nCPUId); /* Run-time dirs accessors */ TGF_API const char *GetLocalDir(void); TGF_API const char *SetLocalDir(const char *buf); TGF_API const char *GetLibDir(void); TGF_API const char *SetLibDir(const char *buf); + TGF_API const char *GetDataDir(void); TGF_API const char *SetDataDir(const char *buf); TGF_API const char *GetBinDir(void); diff --git a/src/linux/linuxspec.cpp b/src/linux/linuxspec.cpp index afcf9a83..282da371 100644 --- a/src/linux/linuxspec.cpp +++ b/src/linux/linuxspec.cpp @@ -18,7 +18,7 @@ ***************************************************************************/ -#include +#include #include #include #include @@ -26,6 +26,18 @@ #include #include +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#include +#include +#if defined(__APPLE__) +#include /* Carbon APIs for Multiprocessing */ +#endif +#else +// Define _GNU_SOURCE in order to have access to pthread_set/getaffinity_np +//#define _GNU_SOURCE +//#include +#endif + #include #include @@ -551,26 +563,171 @@ linuxTimeClock(void) struct timeval tv; gettimeofday(&tv, 0); - return (double)(tv.tv_sec + tv.tv_usec * 1e-6); + return (double)(tv.tv_sec + tv.tv_usec * 1e-6); } +/* +* Function +* linuxGetNumberOfCPUs +* +* Description +* Retrieve the actual number of CPUs in the system +* Note that a core is considered here as a "CPU", and an Intel hyper-threaded processor +* will report twice as many "CPUs" as actual cores ... +* +* Parameters +* None +* +* Return +* The number of CPUs in the system +* +* Remarks +* WARNING: Not tested under platforms other than Linux : Mac OS X, BSD, Solaris, AIX. +* +*/ +unsigned linuxGetNumberOfCPUs() +{ + static unsigned nCPUs = 0; + + if (nCPUs == 0) + { + +// MacOS X, FreeBSD, OpenBSD, NetBSD, etc ... +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + + nt mib[4]; + size_t len; + + // Set the mib for hw.ncpu + + // Get the number of CPUs from the system + // 1) Try HW_AVAILCPU first. + mib[0] = CTL_HW; + mib[1] = HW_AVAILCPU; // alternatively, try HW_NCPU; + sysctl(mib, 2, &nCPUs, &len, NULL, 0); + + if (nCPUs < 1) + { + // 2) Try alternatively HW_NCPU. + mib[1] = HW_NCPU; + sysctl(mib, 2, &nCPUs, &len, NULL, 0); + } + +// Linux, Solaris, AIX +#elif defined(linux) || defined(__linux__) + + nCPUs = (unsigned)sysconf(_SC_NPROCESSORS_ONLN); + +// Anything else ... not supported. +#else + +#warning "Unsupported Linux OS" + +#endif + + if (nCPUs < 1) + { + GfOut("Could not get the number of CPUs here ; assuming only 1\n"); + nCPUs = 1; + } + else + GfOut("Detected %d CPUs\n", nCPUs); + } + + return nCPUs; +} + +/* +* Function +* linuxSetThreadAffinity +* +* Description +* Force the current thread to run on the specified CPU. +* +* Parameters +* nCPUId : the index in [0, # of actual CPUs on the system [ (any other value will actually reset the thread affinity to the "system" affinity mask, meaning no special CPU assignement) +* +* Return +* true if any error occured, false otherwise +* +* Remarks +* +*/ +bool +linuxSetThreadAffinity(int nCPUId) +{ +#if 0 +// MacOS X, FreeBSD, OpenBSD, NetBSD, etc ... +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + + GfOut("Warning: Thread affinity not yet implemented on Mac OS X or BSD.\n"); + return false; + +// Linux, Solaris, AIX ... with NPTL (Native POSIX Threads Library) +#elif defined(linux) || defined(__linux__) + + // Get the handle for the current thread. + pthread_t hCurrThread = pthread_self(); + GfOut("Current pthread handle is 0x%X\n", hCurrThread); + + // Determine the affinity mask to set for the current thread. + cpu_set_t nThreadAffinityMask; + CPU_ZERO(&nThreadAffinityMask); + if (nCPUId == GfAffinityAnyCore) + { + // No special affinity on any CPU => set "system" affinity mask + // (1 bit for each installed CPU). + for (int nCPUIndex = 0; nCPUIndex < linuxGetNumberOfCPUs(); nCPUIndex++) + CPU_SET(nCPUIndex, &nThreadAffinityMask); + } + else + { + // Affinity on a specified CPU => compute its mask. + CPU_SET(nCPUId, &nThreadAffinityMask); + } + + // Set the affinity mask for the current thread ("stick" it to the target core). + if (pthread_setaffinity_np(hCurrThread, sizeof(nThreadAffinityMask), &nThreadAffinityMask)) + { + GfError("Failed to set current pthread (handle=0x%X) affinity mask to 0x%X)\n", + hCurrThread, nThreadAffinityMask); + return false; + } + else + GfOut("Affinity mask set to 0x%X for current pthread (handle=0x%X)\n", + nThreadAffinityMask, hCurrThread); + + return true; + +// Anything else ... not supported. +#else + +#warning "linuxspec.cpp::linuxSetThreadAffinity : Unsupported Linux OS" + GfOut("Warning: Thread affinity not yet implemented on this unknown Unix.\n"); + return false; + +#endif +#endif + return true; // Temporary empty and silent implementation. +} + /* * Function - * LinuxSpecInit + * LinuxSpecInit * * Description - * Init the specific linux functions + * Initialize the specific linux functions * * Parameters - * none + * none * * Return - * none + * none * * Remarks - * + * */ void LinuxSpecInit(void) @@ -585,5 +742,7 @@ LinuxSpecInit(void) GfOs.dirGetList = linuxDirGetList; GfOs.dirGetListFiltered = linuxDirGetListFiltered; GfOs.timeClock = linuxTimeClock; + GfOs.sysGetNumberOfCPUs = linuxGetNumberOfCPUs; + GfOs.sysSetThreadAffinity = linuxSetThreadAffinity; } diff --git a/src/windows/windowsspec.cpp b/src/windows/windowsspec.cpp index 801da860..bbfc864f 100644 --- a/src/windows/windowsspec.cpp +++ b/src/windows/windowsspec.cpp @@ -29,7 +29,6 @@ #include - /* * Function * windowsModLoad @@ -551,58 +550,6 @@ windowsTimeClock(void) return( D ); } -/* -* Function -* windowsSetAffinity -* -* Description -* Restrict game executable to one CPU core/processor. -* This avoids jerky rendering, especially under Vista. -* -* Parameters -* none -* -* Return -* none -*/ -static void -windowsSetAffinity(void) -{ -#ifndef ULONG_PTR - typedef unsigned long ULONG_PTR; -#endif - - #ifndef ULONG_PTR - typedef unsigned long ULONG_PTR; - #endif - - #ifndef PDWORD_PTR - #ifndef DWORD_PTR - typedef ULONG_PTR DWORD_PTR; - #endif - typedef DWORD_PTR *PDWORD_PTR; - #endif - -#ifndef PDWORD_PTR -#ifndef DWORD_PTR - typedef ULONG_PTR DWORD_PTR; -#endif - typedef DWORD_PTR *PDWORD_PTR; -#endif - - HANDLE hProcess = GetCurrentProcess(); - ULONG_PTR ProcAM, SysAM; - - GetProcessAffinityMask( hProcess, (PDWORD_PTR) &ProcAM, (PDWORD_PTR) &SysAM ); - if (ProcAM > 1) - { - ProcAM = 1; - GfOut("Setting process affinity mask to 1"); - SetProcessAffinityMask( hProcess, ProcAM ); - } -} - - /* * Function * windowsGetOSInfo @@ -771,25 +718,140 @@ windowsGetOSInfo(int* pnMajor, int* pnMinor, int* pnBits) /* * Function -* WindowsSpecInit +* windowsGetNumberOfCPUs * * Description -* Init the specific windows functions +* Retrieve the actual number of CPUs in the system +* Note that a core is considered here as a "CPU", and an Intel hyper-threaded processor +* will report twice as many "CPUs" as actual cores ... * * Parameters -* none +* None * * Return -* none +* The number of CPUs in the system * * Remarks * */ +static unsigned +windowsGetNumberOfCPUs() +{ + static unsigned nCPUs = 0; + + if (nCPUs == 0) + { + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + + nCPUs = sysinfo.dwNumberOfProcessors; + + if (nCPUs < 1) + { + GfOut("Could not get the number of CPUs here ; assuming only 1\n"); + nCPUs = 1; + } + else + GfOut("Detected %u CPUs\n", nCPUs); + } + + return nCPUs; +} + +/* +* Function +* windowsSetThreadAffinity +* +* Description +* Force the current thread to run on the specified CPU. +* +* Parameters +* nCPUId : the index in [0, # of actual CPUs on the system [ (any other value will actually reset the thread affinity to the "system" affinity mask, meaning no special CPU assignement) +* +* Return +* true if any error occured, false otherwise +* +* Remarks +* +*/ +static bool +windowsSetThreadAffinity(int nCPUId) +{ + // Get the system affinity mask : it is what we want for the thread + // (1 bit for all the cores available in the system) + // Note: We don't care about the process affinity mask here. + DWORD_PTR nProcessMask, nSystemMask; + GetProcessAffinityMask(GetCurrentProcess(), &nProcessMask, &nSystemMask); + + // Determine the affinity mask to set + ULONGLONG nThreadAffinityMask; + if (nCPUId == GfAffinityAnyCPU) + { + // No special affinity on any processor => set "system" affinity mask. + nThreadAffinityMask = nSystemMask; + } + else + { + // Affinity on a specified CPU => compute its mask (1 bit in the "system" mask). + int nCPUIndex = -1; + int nBitIndex = 0; + while (nBitIndex < sizeof(nSystemMask)*8 && nCPUIndex < nCPUId) + { + if (nSystemMask & 1) + nCPUIndex++; + nSystemMask >>= 1; + nBitIndex++; + } + nBitIndex--; + if (nCPUIndex != nCPUId) + { + GfError("Target CPU %d not found (erroneous id specified ?)\n", nCPUId); + return false; + } + + // We've got it. + nThreadAffinityMask = (1 << nBitIndex); + } + + // Get the handle for the current thread. + HANDLE hCurrThread = GetCurrentThread(); + GfOut("Current thread handle is 0x%X\n", hCurrThread); + + // Set the affinity mask for the current thread ("stick" it to the target core). + if (SetThreadAffinityMask(hCurrThread, (DWORD_PTR)nThreadAffinityMask) == 0) + { + GfError("Failed to set current thread (handle=0x%X) affinity mask to 0x%X)\n", + hCurrThread, nThreadAffinityMask); + return false; + } + else + GfOut("Affinity mask set to 0x%X for current thread (handle=0x%X)\n", + nThreadAffinityMask, hCurrThread); + + return true; +} + +/* +* Function +* WindowsSpecInit +* +* Description +* Init the specific windows functions +* +* Parameters +* none +* +* Return +* none +* +* Remarks +* +*/ void WindowsSpecInit(void) { memset(&GfOs, 0, sizeof(GfOs)); - + GfOs.modLoad = windowsModLoad; GfOs.modLoadDir = windowsModLoadDir; GfOs.modUnloadList = windowsModUnloadList; @@ -798,11 +860,6 @@ WindowsSpecInit(void) GfOs.dirGetList = windowsDirGetList; GfOs.dirGetListFiltered = windowsDirGetListFiltered; GfOs.timeClock = windowsTimeClock; -//>>> Multithreading-Issue: -// Windows XP/windows 7 and ATI Radenon: card no problems without this! - // Workaround for Vista jerky rendering on multicore CPUs. - int nMajor, nMinor, nBits; - if (windowsGetOSInfo(&nMajor, &nMinor, &nBits) && nMajor >= 6) - windowsSetAffinity(); -//<<< + GfOs.sysGetNumberOfCPUs = windowsGetNumberOfCPUs; + GfOs.sysSetThreadAffinity = windowsSetThreadAffinity; }