diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index 4c9d2eb008b..9d92a00bf47 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -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 @@ -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; diff --git a/runtime/vm/j9vm.tdf b/runtime/vm/j9vm.tdf index c24ef9beafd..32403a26fb0 100644 --- a/runtime/vm/j9vm.tdf +++ b/runtime/vm/j9vm.tdf @@ -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" diff --git a/runtime/vm/threadhelp.cpp b/runtime/vm/threadhelp.cpp index 3adc40da463..9276903d105 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,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. diff --git a/runtime/vm/threadpark.cpp b/runtime/vm/threadpark.cpp index dbd9da4ba5d..040bc51cdff 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,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);*/ 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 3a019c0ac39..438a0a4bd06 100644 --- a/runtime/vm/vmthread.cpp +++ b/runtime/vm/vmthread.cpp @@ -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); @@ -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) @@ -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;