Skip to content

Commit

Permalink
Add support or optional spin on park
Browse files Browse the repository at this point in the history
Introduce the ability to spin on park in order to address pathological
cases where application behaviour frequently parks/unparks.

Signed-off-by: tajila <[email protected]>
  • Loading branch information
tajila committed Nov 19, 2024
1 parent 22c662b commit 37bf334
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 3 deletions.
10 changes: 10 additions & 0 deletions runtime/oti/j9nonbuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -5622,6 +5622,11 @@ typedef struct J9VMThread {
#if defined(J9VM_OPT_JFR)
J9ThreadJFRState threadJfrState;
#endif /* defined(J9VM_OPT_JFR) */
U_64 parkWaitTime;
U_64 parkCount;
U_64 parkWaitSlidingWindowIndex;
U_64 *parkWaitSlidingWindow;
UDATA prePark;
} J9VMThread;

#define J9VMTHREAD_ALIGNMENT 0x100
Expand Down Expand Up @@ -5899,6 +5904,11 @@ typedef struct J9JavaVM {
UDATA thrNestedSpinning;
UDATA thrTryEnterNestedSpinning;
UDATA thrDeflationPolicy;
UDATA thrParkSpinCount1;
UDATA thrParkSpinCount2;
U_64 parkSpinWaitThreshold;
U_64 parkWaitSlidingWindowSize;
double parkSpinRatioOfAvgWait;
UDATA gcOptions;
UDATA ( *unhookVMEvent)(struct J9JavaVM *javaVM, UDATA eventNumber, void * currentHandler, void * oldHandler) ;
UDATA classLoadingMaxStack;
Expand Down
4 changes: 4 additions & 0 deletions runtime/vm/j9vm.tdf
Original file line number Diff line number Diff line change
Expand Up @@ -1012,3 +1012,7 @@ TraceEntry=Trc_VM_snapshot_subAllocateSnapshotMemory_Entry NoEnv Overhead=1 Leve
TraceExit=Trc_VM_snapshot_subAllocateSnapshotMemory_Exit NoEnv Overhead=1 Level=1 Template="subAllocateMemory() Memory allocated = %p."
TraceEvent=Trc_VM_snapshot_loadWarmClassFromSnapshot_ClassLoadHookFailed Overhead=1 Level=1 Template="loadWarmClassFromSnapshot() Warm class load hook failed class=%p, %s"
TraceEvent=Trc_VM_snapshot_loadWarmClassFromSnapshot_ClassInfo Overhead=1 Level=1 Template="loadWarmClassFromSnapshot() LoadClass clazz=%p, %s"

TraceEvent=Trc_VM_ThreadHelp_timeCompensationHelper_parkWait Env Overhead=1 Level=5 Template="Park early break average=%zu spinTime=%zu outerSpin=%zu parkWait=%zu vmthread=%p"
TraceEvent=Trc_VM_ThreadHelp_timeCompensationHelper_parkWaitSpinElapsed Env Overhead=1 Level=5 Template="Park spin elapsed average=%zu spinTime=%zu outerSpin=%zu parkWait=%zu vmthread=%p"
TraceEvent=Trc_VM_ThreadHelp_timeCompensationHelper_parkWaitNoSpin Env Overhead=1 Level=5 Template="After park no spin average=%zu waitTime=%zu outerSpin=%zu parkWait=%zu vmthread=%p"
64 changes: 61 additions & 3 deletions runtime/vm/threadhelp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,9 +427,9 @@ IDATA
timeCompensationHelper(J9VMThread *vmThread, U_8 threadHelperType, omrthread_monitor_t monitor, I_64 millis, I_32 nanos)
{
IDATA rc = 0;
J9JavaVM *vm = vmThread->javaVM;

#if defined(J9VM_OPT_CRIU_SUPPORT)
J9JavaVM *vm = vmThread->javaVM;
/* Time compensation only supports CRIURestoreNonPortableMode which is default mode. */
bool waitTimed = (millis > 0) || (nanos > 0);
bool compensationMightBeRequired = waitTimed && !J9_IS_CRIU_RESTORED(vm);
Expand All @@ -452,8 +452,66 @@ timeCompensationHelper(J9VMThread *vmThread, U_8 threadHelperType, omrthread_mon
rc = omrthread_monitor_wait_timed(monitor, millis, nanos);
break;
case HELPER_TYPE_THREAD_PARK:
rc = omrthread_park(millis, nanos);
break;
{
PORT_ACCESS_FROM_VMC(vmThread);
U_64 parkStart = j9time_nano_time();
U_64 slidingWindowSize = vm->parkWaitSlidingWindowSize;
U_64 avgWait = 0;
bool noSpin = true;
if (0 != slidingWindowSize) {
if (vmThread->parkWaitSlidingWindowIndex > slidingWindowSize) {
U_64 totalWait = 0;
vmThread->prePark = 1;

for (U_64 i = 0; i < slidingWindowSize; i++) {
totalWait += vmThread->parkWaitSlidingWindow[i];

}
avgWait = totalWait/slidingWindowSize;
bool spinElapsed = true;
//Trc_VM_ThreadHelp_timeCompensationHelper_parkWait(vmThread, avgWait, vmThread->parkCount, vmThread);

if (avgWait < vm->parkSpinWaitThreshold) {
noSpin = false;
for (IDATA spinCount2 = vm->thrParkSpinCount2; spinCount2 > 0; --spinCount2) {
U_64 spinTime = j9time_nano_time() - parkStart;

if ((vmThread->prePark == 0) || (spinTime > vm->parkSpinWaitThreshold)) {
Trc_VM_ThreadHelp_timeCompensationHelper_parkWait(vmThread, avgWait, spinTime, vm->thrParkSpinCount2 - spinCount2, vmThread->prePark, vmThread);
spinElapsed = false;
break;
}


VM_AtomicSupport::dropSMTThreadPriority();
for (IDATA spinCount1 = vm->thrParkSpinCount1; spinCount1 > 0; --spinCount1) {
VM_AtomicSupport::yieldCPU();
}
VM_AtomicSupport::restoreSMTThreadPriority();

omrthread_yield_new(vm->thrParkSpinCount2 - spinCount2);

}

if (spinElapsed) {
Trc_VM_ThreadHelp_timeCompensationHelper_parkWaitSpinElapsed(vmThread, avgWait, j9time_nano_time() - parkStart, vm->thrParkSpinCount2, vmThread->prePark, vmThread);
}
}
}
rc = omrthread_park(millis, nanos);
U_64 parkEnd = j9time_nano_time();
vmThread->parkWaitSlidingWindow[vmThread->parkWaitSlidingWindowIndex % slidingWindowSize] = parkEnd - parkStart;
vmThread->parkWaitSlidingWindowIndex += 1;
vmThread->prePark = 0;
if (noSpin) {
Trc_VM_ThreadHelp_timeCompensationHelper_parkWaitNoSpin(vmThread, avgWait, parkEnd - parkStart, 0, vmThread->prePark, vmThread);
}

} else {
rc = omrthread_park(millis, nanos);
}
break;
}
case HELPER_TYPE_THREAD_SLEEP:
/* Returns 0 when the timeout specified passed.
* A timeout of 0 (0ms, 0ns) indicates an immediate return.
Expand Down
2 changes: 2 additions & 0 deletions runtime/vm/threadpark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "omrthread.h"
#include "ut_j9vm.h"

#include "AtomicSupport.hpp"
#include "VMHelpers.hpp"

#include <string.h>
Expand Down Expand Up @@ -153,6 +154,7 @@ threadUnparkImpl(J9VMThread *vmThread, j9object_t threadObject)
if (NULL != otherVmThread) {
/* in this case the thread is already dead so we don't need to unpark */
omrthread_unpark(otherVmThread->osThread);
otherVmThread->prePark = 0;
}
objectMonitorExit(vmThread, threadLock);
/*Trc_JCL_unpark_Exit(vmThread);*/
Expand Down
2 changes: 2 additions & 0 deletions runtime/vm/vmthinit.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ void freeVMThread(J9JavaVM *vm, J9VMThread *vmThread)
/* Free J9RIParameters. */
j9mem_free_memory(vmThread->riParameters);
}
j9mem_free_memory(vmThread->parkWaitSlidingWindow);

#endif /* defined(J9VM_PORT_RUNTIME_INSTRUMENTATION) */
if (J9JAVAVM_COMPRESS_OBJECT_REFERENCES(vm)) {
j9mem_free_memory32(vmThread->startOfMemoryBlock);
Expand Down
40 changes: 40 additions & 0 deletions runtime/vm/vmthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,10 @@ allocateVMThread(J9JavaVM *vm, omrthread_t osThread, UDATA privateFlags, void *m
#if defined(J9VM_OPT_JFR)
newThread->threadJfrState.prevTimestamp = -1;
#endif /* defined(J9VM_OPT_JFR) */
newThread->parkWaitSlidingWindow = (U_64*) j9mem_allocate_memory(vm->parkWaitSlidingWindowSize * sizeof(U_64), OMRMEM_CATEGORY_VM);
if (NULL == newThread->parkWaitSlidingWindow) {
goto fail;
}

/* If an exclusive access request is in progress, mark this thread */

Expand Down Expand Up @@ -571,6 +575,11 @@ threadParseArguments(J9JavaVM *vm, char *optArg)
vm->thrNestedSpinning = 1;
vm->thrTryEnterNestedSpinning = 1;
vm->thrDeflationPolicy = J9VM_DEFLATION_POLICY_ASAP;
vm->thrParkSpinCount1 = 0;
vm->thrParkSpinCount2 = 0;
vm->parkSpinWaitThreshold = 0;
vm->parkWaitSlidingWindowSize = 0;
vm->parkSpinRatioOfAvgWait = 0.95;

if (cpus > 1) {
#if (defined(LINUXPPC)) && !defined(J9VM_ENV_LITTLE_ENDIAN)
Expand Down Expand Up @@ -828,6 +837,37 @@ threadParseArguments(J9JavaVM *vm, char *optArg)
continue;
}

if (try_scan(&scan_start, "parkSpinCount1=")) {
if (scan_udata(&scan_start, &vm->thrParkSpinCount1)) {
goto _error;
}
continue;
}
if (try_scan(&scan_start, "parkSpinCount2=")) {
if (scan_udata(&scan_start, &vm->thrParkSpinCount2)) {
goto _error;
}
continue;
}
if (try_scan(&scan_start, "parkSpinWaitThreshold=")) {
if (scan_udata(&scan_start, &vm->parkSpinWaitThreshold)) {
goto _error;
}
continue;
}
if (try_scan(&scan_start, "parkWaitSlidingWindowSize=")) {
if (scan_udata(&scan_start, &vm->parkWaitSlidingWindowSize)) {
goto _error;
}
continue;
}
if (try_scan(&scan_start, "parkSpinRatioOfAvgWait=")) {
if (scan_double(&scan_start, &vm->parkSpinRatioOfAvgWait)) {
goto _error;
}
continue;
}

#if !defined(WIN32) && defined(OMR_NOTIFY_POLICY_CONTROL)
if (try_scan(&scan_start, "notifyPolicy=")) {
char *oldScanStart = scan_start;
Expand Down

0 comments on commit 37bf334

Please sign in to comment.