Auto tune lock for high CPU tension scenarios
Former-commit-id: 8edbae2e04538f82a146a6c2b459a6dfcacf99b2
This commit is contained in:
parent
399975721c
commit
1ac6e3ecc5
@ -40,6 +40,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <linux/futex.h>
|
#include <linux/futex.h>
|
||||||
|
#include <sys/sysinfo.h>
|
||||||
#endif
|
#endif
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
@ -69,6 +70,8 @@ __attribute__((weak)) void logStackTrace(ucontext_t *) {}
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern int g_fInCrash;
|
extern int g_fInCrash;
|
||||||
|
extern int g_fTestMode;
|
||||||
|
int g_fHighCpuPressure = false;
|
||||||
|
|
||||||
/****************************************************
|
/****************************************************
|
||||||
*
|
*
|
||||||
@ -332,6 +335,7 @@ extern "C" void fastlock_lock(struct fastlock *lock)
|
|||||||
unsigned mask = (1U << (myticket % 32));
|
unsigned mask = (1U << (myticket % 32));
|
||||||
unsigned cloops = 0;
|
unsigned cloops = 0;
|
||||||
ticket ticketT;
|
ticket ticketT;
|
||||||
|
unsigned loopLimit = g_fHighCpuPressure ? 0x10000 : 0x100000;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
@ -344,7 +348,7 @@ extern "C" void fastlock_lock(struct fastlock *lock)
|
|||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
__asm__ __volatile__ ("yield");
|
__asm__ __volatile__ ("yield");
|
||||||
#endif
|
#endif
|
||||||
if ((++cloops % 0x100000) == 0)
|
if ((++cloops % loopLimit) == 0)
|
||||||
{
|
{
|
||||||
fastlock_sleep(lock, tid, ticketT.u, mask);
|
fastlock_sleep(lock, tid, ticketT.u, mask);
|
||||||
}
|
}
|
||||||
@ -461,3 +465,24 @@ void fastlock_lock_recursive(struct fastlock *lock, int nesting)
|
|||||||
fastlock_lock(lock);
|
fastlock_lock(lock);
|
||||||
lock->m_depth = nesting;
|
lock->m_depth = nesting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fastlock_auto_adjust_waits()
|
||||||
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
struct sysinfo sysinf;
|
||||||
|
auto fHighPressurePrev = g_fHighCpuPressure;
|
||||||
|
memset(&sysinf, 0, sizeof sysinf);
|
||||||
|
if (!sysinfo(&sysinf)) {
|
||||||
|
auto avgCoreLoad = sysinf.loads[0] / get_nprocs();
|
||||||
|
g_fHighCpuPressure = (avgCoreLoad > ((1 << SI_LOAD_SHIFT) * 0.9));
|
||||||
|
if (g_fHighCpuPressure)
|
||||||
|
serverLog(!fHighPressurePrev ? 3 /*LL_WARNING*/ : 1 /* LL_VERBOSE */, "NOTICE: Detuning locks due to high load per core: %.2f%%", avgCoreLoad / (double)(1 << SI_LOAD_SHIFT)*100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_fHighCpuPressure && fHighPressurePrev) {
|
||||||
|
serverLog(3 /*LL_WARNING*/, "NOTICE: CPU pressure reduced");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
g_fHighCpuPressure = g_fTestMode;
|
||||||
|
#endif
|
||||||
|
}
|
@ -15,6 +15,7 @@ void fastlock_unlock(struct fastlock *lock);
|
|||||||
void fastlock_free(struct fastlock *lock);
|
void fastlock_free(struct fastlock *lock);
|
||||||
int fastlock_unlock_recursive(struct fastlock *lock);
|
int fastlock_unlock_recursive(struct fastlock *lock);
|
||||||
void fastlock_lock_recursive(struct fastlock *lock, int nesting);
|
void fastlock_lock_recursive(struct fastlock *lock, int nesting);
|
||||||
|
void fastlock_auto_adjust_waits();
|
||||||
|
|
||||||
uint64_t fastlock_getlongwaitcount(); // this is a global value
|
uint64_t fastlock_getlongwaitcount(); // this is a global value
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
.extern gettid
|
.extern gettid
|
||||||
.extern fastlock_sleep
|
.extern fastlock_sleep
|
||||||
|
.extern g_fHighCpuPressure
|
||||||
|
|
||||||
# This is the first use of assembly in this codebase, a valid question is WHY?
|
# This is the first use of assembly in this codebase, a valid question is WHY?
|
||||||
# The spinlock we implement here is performance critical, and simply put GCC
|
# The spinlock we implement here is performance critical, and simply put GCC
|
||||||
@ -33,6 +34,13 @@ fastlock_lock:
|
|||||||
cmp [rdi], esi # Is the TID we got back the owner of the lock?
|
cmp [rdi], esi # Is the TID we got back the owner of the lock?
|
||||||
je .LLocked # Don't spin in that case
|
je .LLocked # Don't spin in that case
|
||||||
|
|
||||||
|
mov r9d, 0x1000 # 1000h is set so we overflow on the 1024*1024'th iteration (like the C code)
|
||||||
|
mov eax, [rip+g_fHighCpuPressure]
|
||||||
|
test eax, eax
|
||||||
|
jz .LNoTestMode
|
||||||
|
mov r9d, 0x10000
|
||||||
|
.LNoTestMode:
|
||||||
|
|
||||||
xor eax, eax # eliminate partial register dependency
|
xor eax, eax # eliminate partial register dependency
|
||||||
inc eax # we want to add one
|
inc eax # we want to add one
|
||||||
lock xadd [rdi+66], ax # do the xadd, ax contains the value before the addition
|
lock xadd [rdi+66], ax # do the xadd, ax contains the value before the addition
|
||||||
@ -45,8 +53,7 @@ fastlock_lock:
|
|||||||
cmp dx, ax # is our ticket up?
|
cmp dx, ax # is our ticket up?
|
||||||
je .LLocked # leave the loop
|
je .LLocked # leave the loop
|
||||||
pause
|
pause
|
||||||
add ecx, 0x1000 # Have we been waiting a long time? (oflow if we have)
|
add ecx, r9d # Have we been waiting a long time? (oflow if we have)
|
||||||
# 1000h is set so we overflow on the 1024*1024'th iteration (like the C code)
|
|
||||||
jnc .LLoop # If so, give up our timeslice to someone who's doing real work
|
jnc .LLoop # If so, give up our timeslice to someone who's doing real work
|
||||||
# Like the compiler, you're probably thinking: "Hey! I should take these pushs out of the loop"
|
# Like the compiler, you're probably thinking: "Hey! I should take these pushs out of the loop"
|
||||||
# But the compiler doesn't know that we rarely hit this, and when we do we know the lock is
|
# But the compiler doesn't know that we rarely hit this, and when we do we know the lock is
|
||||||
|
@ -66,6 +66,8 @@ struct benchmarkThread;
|
|||||||
struct clusterNode;
|
struct clusterNode;
|
||||||
struct redisConfig;
|
struct redisConfig;
|
||||||
|
|
||||||
|
int g_fTestMode = false;
|
||||||
|
|
||||||
static struct config {
|
static struct config {
|
||||||
aeEventLoop *el;
|
aeEventLoop *el;
|
||||||
const char *hostip;
|
const char *hostip;
|
||||||
|
@ -69,6 +69,8 @@
|
|||||||
redisContext *context;
|
redisContext *context;
|
||||||
struct config config;
|
struct config config;
|
||||||
|
|
||||||
|
int g_fTestMode = 0;
|
||||||
|
|
||||||
/* User preferences. */
|
/* User preferences. */
|
||||||
static struct pref {
|
static struct pref {
|
||||||
int hints;
|
int hints;
|
||||||
|
@ -2110,6 +2110,11 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
|||||||
migrateCloseTimedoutSockets();
|
migrateCloseTimedoutSockets();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tune the fastlock to CPU load */
|
||||||
|
run_with_period(30000) {
|
||||||
|
fastlock_auto_adjust_waits();
|
||||||
|
}
|
||||||
|
|
||||||
/* Resize tracking keys table if needed. This is also done at every
|
/* Resize tracking keys table if needed. This is also done at every
|
||||||
* command execution, but we want to be sure that if the last command
|
* command execution, but we want to be sure that if the last command
|
||||||
* executed changes the value via CONFIG SET, the server will perform
|
* executed changes the value via CONFIG SET, the server will perform
|
||||||
|
Loading…
x
Reference in New Issue
Block a user