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 5, 2024
1 parent cee0ad8 commit 5e2916b
Show file tree
Hide file tree
Showing 6 changed files with 109 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 @@ -5593,6 +5593,11 @@ typedef struct J9VMThread {
j9object_t closeScopeObj;
#endif /* JAVA_SPEC_VERSION >= 22 */
UDATA unsafeIndexableHeaderSize;
U_64 parkWaitTime;
U_64 parkCount;
U_64 parkWaitSlidingWindowIndex;
U_64 *parkWaitSlidingWindow;
UDATA prePark;
} J9VMThread;

#define J9VMTHREAD_ALIGNMENT 0x100
Expand Down Expand Up @@ -5867,6 +5872,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
2 changes: 2 additions & 0 deletions runtime/vm/j9vm.tdf
Original file line number Diff line number Diff line change
Expand Up @@ -1003,3 +1003,5 @@ TraceException=Trc_VM_loadJFRMetadataBlob_blobDir_OOM NoEnv Overhead=1 Level=1 T
TraceException=Trc_VM_loadJFRMetadataBlob_fileLength_too_large NoEnv Overhead=1 Level=1 Template="JFR loadJFRMetadataBlob fileLength(%llx) not less than 2G"
TraceException=Trc_VM_loadJFRMetadataBlob_metaDataBlobFile_OOM NoEnv Overhead=1 Level=1 Template="JFR loadJFRMetadataBlob j9mem_allocate_memory failed for metaDataBlobFile"
TraceException=Trc_VM_loadJFRMetadataBlob_bad_fileDescriptor NoEnv Overhead=1 Level=1 Template="JFR loadJFRMetadataBlob j9file_open returns fileDescriptor(-1)"

TraceEvent=Trc_VM_ThreadHelp_timeCompensationHelper_parkWait Env Overhead=1 Level=5 Template="Park wait average=%zu spinTime=%zu spinCount1=%zu parkWait=%zu vmthread=%p"
50 changes: 47 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,52 @@ 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;

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];
}

U_64 avgWait = totalWait/slidingWindowSize;

//Trc_VM_ThreadHelp_timeCompensationHelper_parkWait(vmThread, avgWait, vmThread->parkCount, vmThread);
if (avgWait < vm->parkSpinWaitThreshold) {
VM_AtomicSupport::dropSMTThreadPriority();
for (IDATA outerSpin = 0; ; outerSpin++) {
for (IDATA spinCount = vm->thrParkSpinCount1; spinCount > 0; --spinCount)
{
VM_AtomicSupport::yieldCPU();
}

U_64 spinTime = j9time_nano_time() - parkStart;
if ((vmThread->prePark == 0) || spinTime > (avgWait*vm->parkSpinRatioOfAvgWait)) {
Trc_VM_ThreadHelp_timeCompensationHelper_parkWait(vmThread, avgWait, spinTime, outerSpin, vmThread->prePark, vmThread);
VM_AtomicSupport::readWriteBarrier();
break;
}
}
VM_AtomicSupport::restoreSMTThreadPriority();
}
}
rc = omrthread_park(millis, nanos);
U_64 parkEnd = j9time_nano_time();
vmThread->parkWaitSlidingWindow[vmThread->parkWaitSlidingWindowIndex % slidingWindowSize] = parkEnd - parkStart;
vmThread->parkWaitSlidingWindowIndex += 1;
vmThread->prePark = 0;
} 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
3 changes: 3 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,8 @@ 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);
VM_AtomicSupport::writeBarrier();
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
45 changes: 45 additions & 0 deletions runtime/vm/vmthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,11 @@ allocateVMThread(J9JavaVM *vm, omrthread_t osThread, UDATA privateFlags, void *m
newThread->scopedValueCache = NULL;
#endif /* JAVA_SPEC_VERSION >= 19 */

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 */

omrthread_monitor_enter(vm->exclusiveAccessMutex);
Expand Down Expand Up @@ -566,6 +571,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 @@ -823,6 +833,41 @@ 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 5e2916b

Please sign in to comment.