From 37bf334b867280471f32c4dc490c5580d8524350 Mon Sep 17 00:00:00 2001 From: tajila Date: Thu, 7 Nov 2024 17:07:37 -0500 Subject: [PATCH] Add support or optional spin on park Introduce the ability to spin on park in order to address pathological cases where application behaviour frequently parks/unparks. Signed-off-by: tajila --- runtime/oti/j9nonbuilder.h | 10 ++++++ runtime/vm/j9vm.tdf | 4 +++ runtime/vm/threadhelp.cpp | 64 ++++++++++++++++++++++++++++++++++++-- runtime/vm/threadpark.cpp | 2 ++ runtime/vm/vmthinit.c | 2 ++ runtime/vm/vmthread.cpp | 40 ++++++++++++++++++++++++ 6 files changed, 119 insertions(+), 3 deletions(-) diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index ccbc0ec905c..c91b919b103 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -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 @@ -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; diff --git a/runtime/vm/j9vm.tdf b/runtime/vm/j9vm.tdf index 319699919e0..4474eb161aa 100644 --- a/runtime/vm/j9vm.tdf +++ b/runtime/vm/j9vm.tdf @@ -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" diff --git a/runtime/vm/threadhelp.cpp b/runtime/vm/threadhelp.cpp index 3adc40da463..ea459955fd6 100644 --- a/runtime/vm/threadhelp.cpp +++ b/runtime/vm/threadhelp.cpp @@ -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); @@ -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. diff --git a/runtime/vm/threadpark.cpp b/runtime/vm/threadpark.cpp index dbd9da4ba5d..6145c8a8303 100644 --- a/runtime/vm/threadpark.cpp +++ b/runtime/vm/threadpark.cpp @@ -28,6 +28,7 @@ #include "omrthread.h" #include "ut_j9vm.h" +#include "AtomicSupport.hpp" #include "VMHelpers.hpp" #include @@ -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);*/ diff --git a/runtime/vm/vmthinit.c b/runtime/vm/vmthinit.c index e7ea6fe7db3..ab4df1a51f5 100644 --- a/runtime/vm/vmthinit.c +++ b/runtime/vm/vmthinit.c @@ -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); diff --git a/runtime/vm/vmthread.cpp b/runtime/vm/vmthread.cpp index c18d4980374..db4f3ba3910 100644 --- a/runtime/vm/vmthread.cpp +++ b/runtime/vm/vmthread.cpp @@ -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 */ @@ -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) @@ -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;