Commit 568fad12 authored by Byurrrer's avatar Byurrrer

Merge branch 'dev' of https://dev.ds-servers.com/sip/engine into dev

parents ce1dd336 a1d573e0
......@@ -174,6 +174,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sxaigrid", "..\..\sxaigrid\
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sxlevel", "..\..\sxlevel\vs2013\sxlevel.vcxproj", "{6FE14C5C-6052-4D96-A89F-0843D91F89AD}"
ProjectSection(ProjectDependencies) = postProject
{7C0C8205-BDD3-44A3-AA3A-7855C7EFC88E} = {7C0C8205-BDD3-44A3-AA3A-7855C7EFC88E}
{AEECC2DB-C7C7-4089-9262-A69794DF834D} = {AEECC2DB-C7C7-4089-9262-A69794DF834D}
{56A8D7F7-B73C-4206-8038-83D8A169AA2F} = {56A8D7F7-B73C-4206-8038-83D8A169AA2F}
EndProjectSection
......
......@@ -36,6 +36,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release Singlethreaded|Win32'">false</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\source\core\file.cpp" />
<ClCompile Include="..\..\..\source\core\PerfMon.cpp" />
<ClCompile Include="..\..\..\source\core\sxcore.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Singlethreaded|Win32'">false</ExcludedFromBuild>
......@@ -56,6 +57,7 @@
<ClInclude Include="..\..\..\source\core\Config.h" />
<ClInclude Include="..\..\..\source\core\cvars.h" />
<ClInclude Include="..\..\..\source\core\File.h" />
<ClInclude Include="..\..\..\source\core\PerfMon.h" />
<ClInclude Include="..\..\..\source\core\sxcore.h" />
<ClInclude Include="..\..\..\source\core\Task.h" />
<ClInclude Include="..\..\..\source\core\TaskManager.h" />
......
......@@ -37,6 +37,9 @@
<ClCompile Include="..\..\..\source\common\string_utils.cpp">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="..\..\..\source\core\PerfMon.cpp">
<Filter>Source</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="Source">
......@@ -83,5 +86,8 @@
<ClInclude Include="..\..\..\source\common\ConcurrentQueue.h">
<Filter>Header</Filter>
</ClInclude>
<ClInclude Include="..\..\..\source\core\PerfMon.h">
<Filter>Header</Filter>
</ClInclude>
</ItemGroup>
</Project>
\ No newline at end of file
bullet3 @ b8b67cb6
Subproject commit bfe26aafbe2ce7d3800bb30381674c7e6aea8ef0
Subproject commit b8b67cb64146de7400ea969d4082ee1d6f976715
#include "PerfMon.h"
CPerfMon::CPerfMon()
{
memset(m_aiRecStackPtr, 0, sizeof(m_aiRecStackPtr));
}
void CPerfMon::startSection(ID idSection)
{
assert(idSection >= 0 && "IDs below 0 are reserved!");
ID idThread = Core_MGetThreadID();
CPerfRecord rec;
rec.m_iDepth = m_aiRecStackPtr[idThread]++;
rec.m_idSection = idSection;
rec.m_isEntry = true;
rec.m_time = std::chrono::high_resolution_clock::now();
m_aaRecords[m_iWriteIdx][idThread].push_back(rec);
}
void CPerfMon::endSection(ID idSection)
{
assert(idSection >= 0 && "IDs below 0 are reserved!");
ID idThread = Core_MGetThreadID();
CPerfRecord rec;
rec.m_iDepth = --m_aiRecStackPtr[idThread];
rec.m_idSection = idSection;
rec.m_isEntry = false;
rec.m_time = std::chrono::high_resolution_clock::now();
m_aaRecords[m_iWriteIdx][idThread].push_back(rec);
}
void CPerfMon::endFrame()
{
ID idThread = Core_MGetThreadID();
assert(idThread == 0);
CPerfRecord rec;
rec.m_iDepth = 0;
rec.m_idSection = -2;
rec.m_isEntry = false;
rec.m_time = std::chrono::high_resolution_clock::now();
m_aaRecords[m_iWriteIdx][idThread].push_back(rec);
std::swap(m_iReadIdx, m_iWriteIdx);
for(int i = 0; i < SX_MAX_THREAD_COUNT; ++i)
{
m_aaRecords[m_iWriteIdx][i].clearFast();
assert(m_aiRecStackPtr[i] == 0);
}
rec.m_isEntry = true;
m_aaRecords[m_iWriteIdx][idThread].push_back(rec);
}
void CPerfMon::syncBegin()
{
ID idThread = Core_MGetThreadID();
assert(idThread == 0);
CPerfRecord rec;
rec.m_iDepth = 0;
rec.m_idSection = -1;
rec.m_isEntry = true;
rec.m_time = std::chrono::high_resolution_clock::now();
m_aaRecords[m_iWriteIdx][idThread].push_back(rec);
}
const CPerfRecord *CPerfMon::getRecords(ID idThread, int *piRecordCount) const
{
assert(piRecordCount);
auto &arr = m_aaRecords[m_iReadIdx][idThread];
*piRecordCount = arr.size();
if(!*piRecordCount)
{
return(NULL);
}
return(&arr[0]);
}
#ifndef __PERFMON_H
#define __PERFMON_H
#include <gdefines.h>
#include <common/array.h>
#include <chrono>
#include "sxcore.h"
class CPerfMon
{
typedef std::chrono::system_clock::time_point time_point;
public:
CPerfMon();
void startSection(ID idSection);
void endSection(ID idSection);
void syncBegin();
void endFrame();
const CPerfRecord *getRecords(ID idThread, int *piRecordCount) const;
protected:
int m_iReadIdx = 0;
int m_iWriteIdx = 1;
Array<CPerfRecord> m_aaRecords[2][SX_MAX_THREAD_COUNT];
int m_aiRecStackPtr[SX_MAX_THREAD_COUNT];
};
#endif
......@@ -6,17 +6,28 @@ See the license in LICENSE
#include "task.h"
CTask::CTask(THREAD_UPDATE_FUNCTION func, UINT flags):
ITask::ITask(UINT flags):
m_iFlags(flags)
{
m_fnUpdateFunc = func;
}
CTask::~CTask()
UINT ITask::getFlags() const
{
return(m_iFlags);
}
//##########################################################################
CTask::CTask(THREAD_UPDATE_FUNCTION func, UINT flags):
ITask(flags),
m_fnUpdateFunc(func)
{
}
UINT CTask::getFlags() const
void CTask::run()
{
return(m_iFlags);
if(m_fnUpdateFunc)
{
m_fnUpdateFunc();
}
}
......@@ -11,22 +11,15 @@ See the license in LICENSE
#include <memory>
#define _NTASKMANAGER
// Ѕазовый класс задачи
class CTask
class ITask
{
public:
typedef std::shared_ptr<CTask> TaskPtr;
typedef std::shared_ptr<ITask> TaskPtr;
CTask(THREAD_UPDATE_FUNCTION fnFunc, UINT iFlags = CORE_TASK_FLAG_MAINTHREAD_REPEATING);
~CTask();
ITask(UINT iFlags = CORE_TASK_FLAG_MAINTHREAD_REPEATING);
virtual ~ITask(){}
void run()
{
if(m_fnUpdateFunc)
{
m_fnUpdateFunc();
}
}
virtual void run() = 0;
void stopRepeating()
{
......@@ -37,6 +30,18 @@ public:
private:
UINT m_iFlags;
};
// Ѕазовый класс задачи
class CTask: public ITask
{
public:
CTask(THREAD_UPDATE_FUNCTION fnFunc, UINT iFlags = CORE_TASK_FLAG_MAINTHREAD_REPEATING);
void run();
private:
THREAD_UPDATE_FUNCTION m_fnUpdateFunc;
};
......
......@@ -5,6 +5,9 @@ See the license in LICENSE
***********************************************************/
#include "TaskManager.h"
#include "PerfMon.h"
extern CPerfMon *g_pPerfMon;
#if defined(_WINDOWS)
static void SetThreadName(DWORD dwThreadID, const char *threadName)
......@@ -27,6 +30,35 @@ static void SetThreadName(DWORD dwThreadID, const char *threadName)
}
#endif
class CTaskForLoop: public ITask
{
public:
CTaskForLoop(ID id, const IParallelForBody *pBody, int iStart, int iEnd, UINT iFlags = CORE_TASK_FLAG_FOR_LOOP | CORE_TASK_FLAG_THREADSAFE):
ITask(iFlags),
m_id(id),
m_pBody(pBody),
m_iStart(iStart),
m_iEnd(iEnd)
{
}
void run()
{
m_pBody->forLoop(m_iStart, m_iEnd);
}
ID getID() const
{
return(m_id);
}
protected:
ID m_id;
const IParallelForBody *m_pBody;
int m_iStart;
int m_iEnd;
};
CTaskManager::CTaskManager(unsigned int numThreads):
m_isSingleThreaded(false),
m_isRunning(false)
......@@ -105,6 +137,9 @@ void CTaskManager::start()
char name[64];
ID idThread = Core_MGetThreadID();
assert(idThread == 0);
//< Инициализируем пул рабочих потоков
for(unsigned int i = 0; i < m_iNumThreads; ++i)
{
......@@ -133,6 +168,8 @@ void CTaskManager::start()
}
synchronize();
std::swap(m_iReadList, m_iWriteList);
if(m_isSingleThreaded)
......@@ -140,7 +177,10 @@ void CTaskManager::start()
worker(true);
}
m_aiNumWaitFor.clearFast();
sheduleNextBunch();
}
std::this_thread::yield();
......@@ -156,8 +196,8 @@ void CTaskManager::synchronize()
m_Condition.wait(lock);
}
//m_iNumTasksToWaitFor = m_OnSyncTasks.size();
g_pPerfMon->syncBegin();
while(!m_OnSyncTasks.empty())
{
m_BackgroundTasks.push(m_OnSyncTasks.pop());
......@@ -174,7 +214,7 @@ void CTaskManager::sheduleNextBunch()
m_Condition.wait(lock);
}
//m_iNumTasksToWaitFor = m_SyncTasks.size();
g_pPerfMon->endFrame();
while(!m_SyncTasks.empty())
{
......@@ -206,6 +246,7 @@ void CTaskManager::execute(TaskPtr t)
void CTaskManager::workerMain()
{
srand((UINT)time(0));
worker(false);
}
......@@ -229,6 +270,16 @@ void CTaskManager::worker(bool bOneRun)
m_Condition.notify_one();
}
if(task->getFlags() & CORE_TASK_FLAG_FOR_LOOP)
{
{
std::lock_guard<std::mutex> lock(m_mutexFor);
m_aiNumWaitFor[std::static_pointer_cast<CTaskForLoop, ITask>(task)->getID()] -= 1;
}
m_ConditionFor.notify_one();
}
std::this_thread::yield();
}
else
......@@ -237,8 +288,99 @@ void CTaskManager::worker(bool bOneRun)
{
return;
}
// тут делать нечего, спим 1.667 мс (1/10 кадра при 60 FPS)
std::this_thread::sleep_for(std::chrono::microseconds(166));
std::this_thread::yield();
//std::this_thread::sleep_for(std::chrono::microseconds(166));
}
}
}
ID CTaskManager::forLoop(int iStart, int iEnd, const IParallelForBody *pBody, int iMaxChunkSize)
{
int iTotal = iEnd - iStart;
int iChunkSize = (int)(ceilf((float)iTotal / (float)m_iNumThreads) + 0.5f);
if(iMaxChunkSize > 0 && iChunkSize > iMaxChunkSize)
{
iChunkSize = iMaxChunkSize;
}
int iTaskCount = (int)(ceilf((float)iTotal / (float)iChunkSize) + 0.5f);
ID id = -1;
{
std::lock_guard<std::mutex> lock(m_mutexFor);
id = m_aiNumWaitFor.size();
m_aiNumWaitFor.push_back(iTaskCount);
}
int iCur;
while(iTotal > 0)
{
iCur = (std::min)(iTotal, iChunkSize);
addTask(TaskPtr(new CTaskForLoop(id, pBody, iStart, iStart + iCur)));
iTotal -= iCur;
iStart += iCur;
}
return(id);
}
void CTaskManager::waitFor(ID id)
{
assert(ID_VALID(id) && (UINT)id < m_aiNumWaitFor.size());
std::unique_lock<std::mutex> lock(m_mutexFor);
while(m_aiNumWaitFor[id] > 0)
{
m_ConditionFor.wait(lock);
}
}
//##########################################################################
class CThreadsafeCounter
{
ID m_idCounter;
std::mutex m_mutex;
public:
CThreadsafeCounter()
{
m_idCounter = -1;
}
ID getNext()
{
m_mutex.lock();
++m_idCounter;
if(m_idCounter >= SX_MAX_THREAD_COUNT)
{
assert(!"thread counter exceeded");
// wrap back to the first worker index
m_idCounter = 1;
}
ID val = m_idCounter;
m_mutex.unlock();
return(val);
}
};
ID Core_MGetThreadID()
{
static CThreadsafeCounter s_threadCounter;
const ID c_idNullIndex = -1;
__declspec(thread) static ID s_idThreadIndex = c_idNullIndex;
if(s_idThreadIndex == c_idNullIndex)
{
s_idThreadIndex = s_threadCounter.getNext();
assert(s_idThreadIndex < SX_MAX_THREAD_COUNT);
}
return(s_idThreadIndex);
}
......@@ -29,24 +29,31 @@ typedef struct tagTHREADNAME_INFO
#pragma pack(pop)
#endif
// Внимание, при использовании должна быть создана хотя бы одна фоновая задача!
class CTaskManager
{
public:
typedef std::shared_ptr<CTask> TaskPtr;
typedef std::shared_ptr<ITask> TaskPtr;
typedef CConcurrentQueue<TaskPtr> TaskList;
CTaskManager(unsigned int numThreads = 0); //< Количество рабочих потоков, 0 для автоопределения
~CTaskManager();
void addTask(TaskPtr task); //< Добавляет задачу в планировщик
void add(THREAD_UPDATE_FUNCTION fnFunc, DWORD dwFlag = CORE_TASK_FLAG_MAINTHREAD_REPEATING); //< Добавляет задачу в планировщик
void addTask(TaskPtr task); //< Добавляет задачу в планировщик
void add(THREAD_UPDATE_FUNCTION fnFunc, DWORD dwFlag = CORE_TASK_FLAG_MAINTHREAD_REPEATING); //< Добавляет задачу в планировщик
void forceSinglethreaded();
void start(); //< Запускает выполнение планировщика
void stop(); //< Останавливает все
ID forLoop(int iStart, int iEnd, const IParallelForBody *pBody, int iMaxChunkSize = 0);
void waitFor(ID id);
int getThreadCount()
{
return(m_iNumThreads);
}
private:
void workerMain();
......@@ -72,10 +79,14 @@ private:
typedef std::lock_guard<std::mutex> ScopedLock;
mutable std::mutex m_mutexSync;
mutable std::mutex m_mutexFor;
Condition m_Condition;
Condition m_ConditionFor;
int m_iNumTasksToWaitFor;
bool m_isSingleThreaded;
Array<int> m_aiNumWaitFor;
};
#endif
/***********************************************************
Copyright Vitaliy Buturlin, Evgeny Danilovich, 2017, 2018
Copyright © Vitaliy Buturlin, Evgeny Danilovich, 2017, 2018
See the license in LICENSE
***********************************************************/
......@@ -23,6 +23,8 @@ See the license in LICENSE
#include "cvars.h"
#include "PerfMon.h"
#pragma comment (lib, "Ws2_32.lib")
AssotiativeArray<String, ConCmd> g_mCmds;
......@@ -30,8 +32,8 @@ AssotiativeArray<String, ConCmd> g_mCmds;
SOCKET ConnectSocket = INVALID_SOCKET;
SOCKET CommandSocket = INVALID_SOCKET;
#define CONSOLE_PORT g_szServerPort /*!< */
#define COMMAND_PORT g_szClientPort /*!< */
#define CONSOLE_PORT g_szServerPort /*!< Стандартный порт для подключения консоли */
#define COMMAND_PORT g_szClientPort /*!< Стандартный порт для команд */
bool g_bRunning = false;
bool g_bRunningCmd = false;
......@@ -426,6 +428,150 @@ void cmd_help(int argc, const char ** argv)
}
}
void cmd_perf_dump()
{
static const int *piConWidth = GET_PCVAR_INT("con_width");
//static const int *piConHeight = GET_PCVAR_INT("con_height");
extern CPerfMon *g_pPerfMon;
static const char *s_aszColors[] = {
COLOR_RED,
COLOR_GREEN,
COLOR_BLUE,
COLOR_OLIVE,
COLOR_MAGENTA,
COLOR_CYAN,
COLOR_GRAY,
COLOR_LRED,
COLOR_LGREEN,
COLOR_LBLUE,
COLOR_YELLOW,
COLOR_LMAGENTA,
COLOR_LCYAN,
COLOR_WHITE
};
std::chrono::system_clock::time_point tStart, tEnd, tSync;
Array<Array<const CPerfRecord*>> aaRecords;
Array<const CPerfRecord*> aRecords;
for(ID i = 0; i < SX_MAX_THREAD_COUNT; ++i)
{
int iRecordCount;
const CPerfRecord *pRecords = g_pPerfMon->getRecords(i, &iRecordCount);
if(pRecords)
{
aRecords.reserve(iRecordCount);
for(int j = 0; j < iRecordCount; ++j)
{
if(pRecords[j].m_idSection == -2)
{
(pRecords[j].m_isEntry ? tStart : tEnd) = pRecords[j].m_time;
}
else if(pRecords[j].m_idSection == -1)
{
tSync = pRecords[j].m_time;
}
else
{
aRecords.push_back(&pRecords[j]);
}
}
aaRecords.push_back(aRecords);
aRecords.clearFast();
}
}
int iMeterWidth = *piConWidth - 7;
float llOverallTime = (float)std::chrono::duration_cast<std::chrono::microseconds>(tEnd - tStart).count();
float llPreSyncTime = (float)std::chrono::duration_cast<std::chrono::microseconds>(tSync - tStart).count();
float llPostSyncTime = (float)std::chrono::duration_cast<std::chrono::microseconds>(tEnd - tSync).count();
int iSyncPos = (int)((llPreSyncTime / llOverallTime) * (float)iMeterWidth);
char *buf = (char*)alloca(sizeof(char) * (iMeterWidth + 1));
Array<float> afTimes;
for(int i = 0, l = aaRecords.size(); i < l; ++i)
{
for(int j = 0; j < iMeterWidth; ++j)
{
buf[j] = -1;
}
Array<const CPerfRecord*> &aRecords = aaRecords[i];
for(UINT j = 0; j < aRecords.size(); ++j)
{
if(aRecords[j]->m_isEntry)
{
const CPerfRecord *pStart = aRecords[j], *pEnd = NULL;
for(UINT k = j + 1; k < aRecords.size(); ++k)
{
if(!aRecords[k]->m_isEntry && aRecords[k]->m_idSection == pStart->m_idSection)
{
pEnd = aRecords[k];
break;
}
}
assert(pEnd);
float fDuration = (float)std::chrono::duration_cast<std::chrono::microseconds>(pEnd->m_time - pStart->m_time).count();
float fStartTime = (float)std::chrono::duration_cast<std::chrono::microseconds>(pStart->m_time - tStart).count();
afTimes[pStart->m_idSection] = fDuration * 0.001f;
int iBarLen = (int)((fDuration / llOverallTime) * (float)iMeterWidth);
int iStartPos = (int)((fStartTime / llOverallTime) * (float)iMeterWidth);
for(int k = 0; k < iBarLen; ++k)
{
buf[k + iStartPos] = pStart->m_idSection/* + (pStart->m_idSection < 10 ? '0' : 'A' - 10)*/;
}
}
}
buf[iSyncPos] = -2;
buf[iMeterWidth] = 0;
printf(" %2d " COLOR_DARKGRAY "[", i);
for(int j = 0; j < iMeterWidth; ++j)
{
if(buf[j] == -1)
{
printf(" ");
}
else if(buf[j] == -2)
{
printf(COLOR_DARKGRAY "|");
}
else
{
printf("%s%c", s_aszColors[((unsigned char)buf[j]) % ARRAYSIZE(s_aszColors)], buf[j] + (buf[j] < 10 ? '0' : 'A' - 10));
}
}
printf(COLOR_DARKGRAY "]" COLOR_RESET "\n");
}
printf("\n");
for(int i = 0, l = afTimes.size(); i < l; ++i)
{
assert(i < ARRAYSIZE(g_szPerfSectionName));
printf("%s%c: %.2f: %s" COLOR_RESET "\n",