Skip to content

Commit

Permalink
HPCC-30156 Instrument CriticalSection
Browse files Browse the repository at this point in the history
Moved into jmutex

Signed-off-by: Richard Chapman <[email protected]>
  • Loading branch information
richardkchapman committed Sep 6, 2023
1 parent c52bd06 commit b890cb7
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 234 deletions.
73 changes: 1 addition & 72 deletions system/jlib/jdebug.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,78 +23,7 @@
#include "jiface.hpp"
#include "jstats.h"
#include <atomic>

#define TIMING

__int64 jlib_decl cycle_to_nanosec(cycle_t cycles);
__int64 jlib_decl cycle_to_microsec(cycle_t cycles);
__int64 jlib_decl cycle_to_millisec(cycle_t cycles);
cycle_t jlib_decl nanosec_to_cycle(__int64 cycles);
cycle_t jlib_decl millisec_to_cycle(unsigned ms);
double jlib_decl getCycleToNanoScale();
void jlib_decl display_time(const char * title, cycle_t diff);

// X86 / X86_64
#if defined(_ARCH_X86_64_) || defined(_ARCH_X86_)

#define HAS_GOOD_CYCLE_COUNTER

#if defined(_WIN32) && defined (_ARCH_X86_)
#pragma warning(push)
#pragma warning(disable:4035)
inline cycle_t getTSC() { __asm { __asm _emit 0x0f __asm _emit 0x31 } }
#pragma warning(pop)
#elif !defined(_WIN32)
inline cycle_t getTSC() { return __builtin_ia32_rdtsc(); }
#else
#include <intrin.h>
inline cycle_t getTSC() { return __rdtsc(); }
#endif // WIN32

#elif defined(_ARCH_PPC)

#define HAS_GOOD_CYCLE_COUNTER

static inline cycle_t getTSC()
{
int64_t result;
#ifdef _ARCH_PPC64
/*
This reads timebase in one 64bit go. Does *not* include a workaround for the cell (see
http://ozlabs.org/pipermail/linuxppc-dev/2006-October/027052.html)
*/
__asm__ volatile(
"mftb %0"
: "=r" (result));
#else
/*
Read the high 32bits of the timer, then the lower, and repeat if high order has changed in the meantime. See
http://ozlabs.org/pipermail/linuxppc-dev/1999-October/003889.html
*/
unsigned long dummy;
__asm__ volatile(
"mfspr %1,269\n\t" /* mftbu */
"mfspr %L0,268\n\t" /* mftb */
"mfspr %0,269\n\t" /* mftbu */
"cmpw %0,%1\n\t" /* check if the high order word has chanegd */
"bne $-16"
: "=r" (result), "=r" (dummy));
#endif
return result;
}

#else
// ARMFIX: cycle-count is not always available in user mode
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0338g/Bihbeabc.html
// http://neocontra.blogspot.co.uk/2013/05/user-mode-performance-counters-for.html
inline cycle_t getTSC() { return 0; }
#endif // X86

#if defined(INLINE_GET_CYCLES_NOW) && defined(HAS_GOOD_CYCLE_COUNTER)
inline cycle_t get_cycles_now() { return getTSC(); }
#else
cycle_t jlib_decl get_cycles_now(); // equivalent to getTSC when available
#endif
#include <jtiming.hpp>

struct HardwareInfo
{
Expand Down
22 changes: 22 additions & 0 deletions system/jlib/jmutex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,28 @@ int Mutex::unlockAll()
return ret;
}

static SpinLock sl;
void InstrumentedCriticalSection::reportContended(bool destructor)
{
if (contended*2 > uncontended && contended % 1000 == 0)
{
SpinBlock block(sl);
DBGLOG("Heavily contended critsec %u/%u", contended, contended+uncontended);
printStackReport();
}
else if (destructor && contended > 1000)
{
SpinBlock block(sl);
DBGLOG("Contended critsec %u/%u", contended, contended+uncontended);
}
if (destructor && (waitCycles || holdCycles))
{
DBGLOG("Total wait time for CritSec: %" I64F "u ms", cycle_to_millisec(waitCycles));
DBGLOG("Total hold time for CritSec: %" I64F "u ms", cycle_to_millisec(holdCycles));
}
}





Expand Down
149 changes: 131 additions & 18 deletions system/jlib/jmutex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <functional>
#include "jiface.hpp"
#include "jsem.hpp"
#include "jtiming.hpp"

extern jlib_decl void ThreadYield();
extern jlib_decl void spinUntilReady(std::atomic_uint &value);
Expand All @@ -34,6 +35,9 @@ extern jlib_decl void spinUntilReady(std::atomic_uint &value);
//#define SPINLOCK_USE_MUTEX // for testing
#define SPINLOCK_RR_CHECK // checks for realtime threads
#define _ASSERT_LOCK_SUPPORT
#define USE_INSTRUMENTED_CRITSECS
#elif defined(_PROFILE)
#define USE_INSTRUMENTED_CRITSECS
#endif

#ifdef SPINLOCK_USE_MUTEX
Expand Down Expand Up @@ -195,7 +199,7 @@ TryEnterCriticalSection(
);
};

class jlib_decl CriticalSection
class jlib_decl UninstrumentedCriticalSection
{
// lightweight mutex within a single process
private:
Expand Down Expand Up @@ -255,22 +259,24 @@ class jlib_decl CriticalSection
bool wouldBlock() { if (TryEnterCriticalSection(&flags)) { leave(); return false; } return true; } // debug only
#endif
};

typedef UninstrumentedCriticalSection InstrumentedCriticalSection; // Not worth implementing for Windows
#else

/**
* Mutex locking wrapper. Use enter/leave to lock/unlock.
*/
class CriticalSection
class UninstrumentedCriticalSection
{
private:
MutexId mutex;
#ifdef _ASSERT_LOCK_SUPPORT
ThreadId owner;
unsigned depth;
#endif
CriticalSection (const CriticalSection &);
UninstrumentedCriticalSection (const UninstrumentedCriticalSection &) = delete;
public:
inline CriticalSection()
inline UninstrumentedCriticalSection()
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
Expand All @@ -287,7 +293,7 @@ class CriticalSection
#endif
}

inline ~CriticalSection()
inline ~UninstrumentedCriticalSection()
{
#ifdef _ASSERT_LOCK_SUPPORT
assert(owner==0 && depth==0);
Expand Down Expand Up @@ -327,49 +333,142 @@ class CriticalSection
#endif
}
};

class InstrumentedCriticalSection
{
private:
MutexId mutex;
#ifdef _ASSERT_LOCK_SUPPORT
ThreadId owner = 0;
#endif
unsigned depth = 0;
cycle_t waitCycles = 0;
cycle_t holdCycles = 0;
cycle_t holdStart = 0;
unsigned contended = 0;
unsigned uncontended = 0;
bool wasContended = false;
InstrumentedCriticalSection (const InstrumentedCriticalSection &) = delete;
public:
inline InstrumentedCriticalSection()
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
#ifdef _DEBUG
verifyex(pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE)==0); // verify supports attr
#else
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
#endif
pthread_mutex_init(&mutex, &attr);
pthread_mutexattr_destroy(&attr);
}

inline ~InstrumentedCriticalSection()
{
#ifdef _ASSERT_LOCK_SUPPORT
assert(owner==0);
#endif
assert(depth==0);
pthread_mutex_destroy(&mutex);
reportContended(true);
}

inline void enter()
{
cycle_t start = get_cycles_now();
#ifdef _ASSERT_LOCK_SUPPORT
ThreadId me = GetCurrentThreadId();
#endif
bool isContended = depth != 0;
pthread_mutex_lock(&mutex);
depth++;
holdStart = get_cycles_now();
waitCycles += holdStart-start;
if (isContended)
contended++;
else
uncontended++;
wasContended = isContended;
#ifdef _ASSERT_LOCK_SUPPORT
owner = me;
#endif
}

inline void leave()
{
bool isContended = false;
if (depth==1)
{
#ifdef _ASSERT_LOCK_SUPPORT
owner = 0;
#endif
isContended = wasContended;
holdCycles += get_cycles_now()-holdStart;
}
depth--;
pthread_mutex_unlock(&mutex);
if (isContended)
reportContended(false);
}
inline void assertLocked()
{
#ifdef _ASSERT_LOCK_SUPPORT
assertex(owner == GetCurrentThreadId());
#endif
}
void reportContended(bool destructor);
};

#endif

/**
* Critical section delimiter, using scope to define lifetime of
* the lock on a critical section (parameter).
* Blocks on construction, unblocks on destruction.
*/
class CriticalBlock

template<typename T> class CriticalBlockOf
{
CriticalSection &crit;
T &crit;
public:
inline CriticalBlock(CriticalSection &c) : crit(c) { crit.enter(); }
inline ~CriticalBlock() { crit.leave(); }
inline CriticalBlockOf(T &c) : crit(c) { crit.enter(); }
inline ~CriticalBlockOf() { crit.leave(); }
};

typedef CriticalBlockOf<UninstrumentedCriticalSection> UninstrumentedCriticalBlock;
typedef CriticalBlockOf<InstrumentedCriticalSection> InstrumentedCriticalBlock;

/**
* Critical section delimiter, using scope to define lifetime of
* the lock on a critical section (parameter).
* Unblocks on construction, blocks on destruction.
*/
class CriticalUnblock
template<typename T> class CriticalUnblockOf
{
CriticalSection &crit;
T &crit;
public:
inline CriticalUnblock(CriticalSection &c) : crit(c) { crit.leave(); }
inline ~CriticalUnblock() { crit.enter(); }
inline CriticalUnblockOf(T &c) : crit(c) { crit.leave(); }
inline ~CriticalUnblockOf() { crit.enter(); }
};

class CLeavableCriticalBlock
typedef CriticalUnblockOf<UninstrumentedCriticalSection> UninstrumentedCriticalUnblock;
typedef CriticalUnblockOf<InstrumentedCriticalSection> InstrumentedCriticalUnblock;

template<typename T> class CLeavableCriticalBlockOf
{
CriticalSection &crit;
T &crit;
bool locked = false;
public:
inline CLeavableCriticalBlock(CriticalSection &_crit) : crit(_crit)
inline CLeavableCriticalBlockOf(T &_crit) : crit(_crit)
{
enter();
}
inline CLeavableCriticalBlock(CriticalSection &_crit, bool lock) : crit(_crit)
inline CLeavableCriticalBlockOf(T &_crit, bool lock) : crit(_crit)
{
if (lock)
enter();
}
inline ~CLeavableCriticalBlock()
inline ~CLeavableCriticalBlockOf()
{
if (locked)
crit.leave();
Expand All @@ -391,6 +490,20 @@ class CLeavableCriticalBlock
}
};

typedef CLeavableCriticalBlockOf<UninstrumentedCriticalSection> UninstrumentedLeavableCriticalBlock;
typedef CLeavableCriticalBlockOf<InstrumentedCriticalSection> InstrumentedLeavableCriticalBlock;

#ifdef USE_INSTRUMENTED_CRITSECS
typedef InstrumentedCriticalSection CriticalSection;
typedef InstrumentedCriticalBlock CriticalBlock;
typedef InstrumentedCriticalBlock CriticalUnblock;
typedef InstrumentedLeavableCriticalBlock CLeavableCriticalBlock;
#else
typedef UninstrumentedCriticalSection CriticalSection;
typedef UninstrumentedCriticalBlock CriticalBlock;
typedef UninstrumentedCriticalBlock CriticalUnblock;
typedef UninstrumentedLeavableCriticalBlock CLeavableCriticalBlock;
#endif

#ifdef SPINLOCK_USE_MUTEX // for testing

Expand Down
Loading

0 comments on commit b890cb7

Please sign in to comment.