Skip to content

Commit

Permalink
Added: Support for mutex profiling
Browse files Browse the repository at this point in the history
  • Loading branch information
richardbiely committed Mar 12, 2024
1 parent dd6c809 commit 278e757
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 62 deletions.
49 changes: 40 additions & 9 deletions include/gaia/config/profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,27 +118,58 @@ namespace tracy {
#if !defined(GAIA_PROF_STOP)
#define GAIA_PROF_STOP() GAIA_PROF_STOP_IMPL()
#endif
//! Profiling zone for mutex
#if !defined(GAIA_PROF_MUTEX_BASE)
//! Extracts reference to m_lockable from tracy::Lockable<T> because
//! they don't provide a getter for the internal mutex object.
//! Therefore, we would not be able to use conditional variables easily.
template <class TLockable, class T>
inline T& gaia_extract_lock_from_tracy_lockable(TLockable& lockable) {
// Assuming TLockable is non-virtual and the memory layout is as follows:
// T m_lockable;
// LockableCtx m_ctx;
// ...
//
// m_lockable is the first member. We simply cast to it.
T* ptr_lockable = reinterpret_cast<T*>(&lockable);
return (T&)*(ptr_lockable + 0);
// return (T&)lockable;
}
#define GAIA_PROF_EXTRACT_MUTEX(type, name) gaia_extract_lock_from_tracy_lockable<decltype(name), type>(name)
#define GAIA_PROF_MUTEX_BASE(type) LockableBase<type>
#endif
#if !defined(GAIA_PROF_MUTEX)
#define GAIA_PROF_MUTEX(type, name) TracyLockable(type, name)
#endif
#else
//! Marks the end of frame
//! Marks the end of frame
#if !defined(GAIA_PROF_FRAME)
#define GAIA_PROF_FRAME()
#endif
//! Profiling zone bounded by the scope. The zone is named after a unique compile-time string
//! Profiling zone bounded by the scope. The zone is named after a unique compile-time string
#if !defined(GAIA_PROF_SCOPE)
#define GAIA_PROF_SCOPE(zoneName)
#endif
//! Profiling zone bounded by the scope. The zone is named after a run-time string
//! Profiling zone bounded by the scope. The zone is named after a run-time string
#if !defined(GAIA_PROF_SCOPE2)
#define GAIA_PROF_SCOPE2(zoneName)
#endif
//! Profiling zone with user-defined scope - start. The zone is named after a unique compile-time string
//! Profiling zone with user-defined scope - start. The zone is named after a unique compile-time string
#if !defined(GAIA_PROF_START)
#define GAIA_PROF_START(zoneName)
#endif
//! Profiling zone with user-defined scope - stop.
//! Profiling zone with user-defined scope - stop.
#if !defined(GAIA_PROF_STOP)
#define GAIA_PROF_STOP()
#endif
//! Profiling zone for mutex
#if !defined(GAIA_PROF_MUTEX_BASE)
#define GAIA_PROF_MUTEX_BASE(type) type
#endif
#if !defined(GAIA_PROF_MUTEX)
#define GAIA_PROF_EXTRACT_MUTEX(type, name) name
#define GAIA_PROF_MUTEX(type, name) GAIA_PROF_MUTEX_BASE(type) name
#endif
#endif

#if GAIA_PROFILER_MEM
Expand All @@ -159,19 +190,19 @@ namespace tracy {
#define GAIA_PROF_FREE2(ptr, name) TracyFreeN(ptr, name)
#endif
#else
//! Marks a memory allocation event. The event is named after a unique compile-time string
//! Marks a memory allocation event. The event is named after a unique compile-time string
#if !defined(GAIA_PROF_ALLOC)
#define GAIA_PROF_ALLOC(ptr, size)
#endif
//! Marks a memory allocation event. The event is named after a run-time string
//! Marks a memory allocation event. The event is named after a run-time string
#if !defined(GAIA_PROF_ALLOC2)
#define GAIA_PROF_ALLOC2(ptr, size, name)
#endif
//! Marks a memory release event. The event is named after a unique compile-time string
//! Marks a memory release event. The event is named after a unique compile-time string
#if !defined(GAIA_PROF_FREE)
#define GAIA_PROF_FREE(ptr)
#endif
//! Marks a memory release event. The event is named after a run-time string
//! Marks a memory release event. The event is named after a run-time string
#if !defined(GAIA_PROF_FREE2)
#define GAIA_PROF_FREE2(ptr, name)
#endif
Expand Down
28 changes: 14 additions & 14 deletions include/gaia/mt/jobmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ namespace gaia {
};

class JobManager {
std::mutex m_jobsLock;
GAIA_PROF_MUTEX(std::mutex, m_jobsLock);
//! Implicit list of jobs
cnt::ilist<JobContainer, JobHandle> m_jobs;

std::mutex m_depsLock;
GAIA_PROF_MUTEX(std::mutex, m_depsLock);
//! List of job dependencies
cnt::ilist<JobDependency, DepHandle> m_deps;

Expand Down Expand Up @@ -123,7 +123,7 @@ namespace gaia {
//! \warning Must be used from the main thread.
GAIA_NODISCARD JobHandle alloc_job(const Job& job) {
JobAllocCtx ctx{job.priority};
std::scoped_lock<std::mutex> lock(m_jobsLock);
std::scoped_lock lock(m_jobsLock);
auto handle = m_jobs.alloc(&ctx);
auto& j = m_jobs[handle.id()];
GAIA_ASSERT(j.state == JobInternalState::Idle || j.state == JobInternalState::Released);
Expand All @@ -138,7 +138,7 @@ namespace gaia {
//! \warning Must be used from the main thread.
void free_job(JobHandle jobHandle) {
// No need to lock. Called from the main thread only when the job has finished already.
// --> std::scoped_lock<std::mutex> lock(m_jobsLock);
// --> std::scoped_lock lock(m_jobsLock);
auto& job = m_jobs.free(jobHandle);
job.state = JobInternalState::Released;
}
Expand All @@ -162,12 +162,12 @@ namespace gaia {
void reset() {
{
// No need to lock. Called from the main thread only when all jobs have finished already.
// --> std::scoped_lock<std::mutex> lock(m_jobsLock);
// --> std::scoped_lock lock(m_jobsLock);
m_jobs.clear();
}
{
// No need to lock. Called from the main thread only when all jobs must have ended already.
// --> std::scoped_lock<std::mutex> lock(m_depsLock);
// --> std::scoped_lock lock(m_depsLock);
m_deps.clear();
}
}
Expand All @@ -176,15 +176,15 @@ namespace gaia {
std::function<void()> func;

{
std::scoped_lock<std::mutex> lock(m_jobsLock);
std::scoped_lock lock(m_jobsLock);
auto& job = m_jobs[jobHandle.id()];
job.state = JobInternalState::Running;
func = job.func;
}
if (func.operator bool())
func();
{
std::scoped_lock<std::mutex> lock(m_jobsLock);
std::scoped_lock lock(m_jobsLock);
auto& job = m_jobs[jobHandle.id()];
job.state = JobInternalState::Done;
}
Expand All @@ -194,14 +194,14 @@ namespace gaia {
//! \return True if job dependencies are met. False otherwise
GAIA_NODISCARD bool handle_deps(JobHandle jobHandle) {
GAIA_PROF_SCOPE(JobManager::handle_deps);
std::scoped_lock<std::mutex> lockJobs(m_jobsLock);
std::scoped_lock lockJobs(m_jobsLock);
auto& job = m_jobs[jobHandle.id()];
if (job.dependencyIdx == BadIndex)
return true;

uint32_t depsId = job.dependencyIdx;
{
std::scoped_lock<std::mutex> lockDeps(m_depsLock);
std::scoped_lock lockDeps(m_depsLock);

// Iterate over all dependencies.
// The first busy dependency breaks the loop. At this point we also update
Expand All @@ -228,7 +228,7 @@ namespace gaia {
//! \warning Must be used from the main thread.
//! \warning Needs to be called before any of the listed jobs are scheduled.
void dep(JobHandle jobHandle, JobHandle dependsOn) {
std::scoped_lock<std::mutex> lockJobs(m_jobsLock);
std::scoped_lock lockJobs(m_jobsLock);
auto& job = m_jobs[jobHandle.id()];

#if GAIA_ASSERT_ENABLED
Expand All @@ -239,7 +239,7 @@ namespace gaia {

{
GAIA_PROF_SCOPE(JobManager::dep);
std::scoped_lock<std::mutex> lockDeps(m_depsLock);
std::scoped_lock lockDeps(m_depsLock);

auto depHandle = alloc_dep();
auto& dep = m_deps[depHandle.id()];
Expand Down Expand Up @@ -275,9 +275,9 @@ namespace gaia {
#endif

GAIA_PROF_SCOPE(JobManager::deps);
std::scoped_lock<std::mutex> lockJobs(m_jobsLock);
std::scoped_lock lockJobs(m_jobsLock);
{
std::scoped_lock<std::mutex> lockDeps(m_depsLock);
std::scoped_lock lockDeps(m_depsLock);

for (auto dependsOn: dependsOnSpan) {
auto depHandle = alloc_dep();
Expand Down
8 changes: 4 additions & 4 deletions include/gaia/mt/jobqueue.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace gaia {
#endif

#if JOB_QUEUE_USE_LOCKS
std::mutex m_bufferLock;
GAIA_PROF_MUTEX(std::mutex, m_bufferLock);
cnt::sringbuffer<JobHandle, N> m_buffer;
#else
cnt::sarray<JobHandle, N> m_buffer;
Expand All @@ -39,7 +39,7 @@ namespace gaia {
GAIA_PROF_SCOPE(JobQueue::try_push);

#if JOB_QUEUE_USE_LOCKS
std::scoped_lock<std::mutex> lock(m_bufferLock);
std::scoped_lock lock(m_bufferLock);
if (m_buffer.size() >= m_buffer.max_size())
return false;
m_buffer.push_back(jobHandle);
Expand All @@ -66,7 +66,7 @@ namespace gaia {
GAIA_PROF_SCOPE(JobQueue::try_pop);

#if JOB_QUEUE_USE_LOCKS
std::scoped_lock<std::mutex> lock(m_bufferLock);
std::scoped_lock lock(m_bufferLock);
if (m_buffer.empty())
return false;

Expand Down Expand Up @@ -117,7 +117,7 @@ namespace gaia {
GAIA_PROF_SCOPE(JobQueue::try_steal);

#if JOB_QUEUE_USE_LOCKS
std::scoped_lock<std::mutex> lock(m_bufferLock);
std::scoped_lock lock(m_bufferLock);
if (m_buffer.empty())
return false;

Expand Down
10 changes: 6 additions & 4 deletions include/gaia/mt/threadpool.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ namespace gaia {
//! How many jobs are currently being processed
std::atomic_uint32_t m_jobsPending[JobPriorityCnt]{};
//! Mutex protecting the access to a given queue
std::mutex m_cvLock[JobPriorityCnt];
GAIA_PROF_MUTEX(std::mutex, m_cvLock0);
GAIA_PROF_MUTEX(std::mutex, m_cvLock1);
//! Signals for given workers to wake up
std::condition_variable m_cv[JobPriorityCnt];
//! List of pending user jobs
Expand Down Expand Up @@ -697,14 +698,15 @@ namespace gaia {
//! \param prio Target worker queue defined by job priority
void worker_loop(JobPriority prio) {
auto& jobQueue = m_jobQueue[(uint32_t)prio];
auto& cv = m_cv[(uint32_t)prio];
auto& cvLock = m_cvLock[(uint32_t)prio];
auto& cv = m_cv[prio];
auto& cvLock = prio==0 ? m_cvLock0 : m_cvLock1;

while (!m_stop) {
JobHandle jobHandle;

if (!jobQueue.try_pop(jobHandle)) {
std::unique_lock<std::mutex> lock(cvLock);
auto& mtx = GAIA_PROF_EXTRACT_MUTEX(std::mutex, cvLock);
std::unique_lock lock(mtx);
cv.wait(lock);
continue;
}
Expand Down
Loading

0 comments on commit 278e757

Please sign in to comment.