From 00adbbe5538ec5c26dc5bd17ca94cc29db9bc478 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 30 Jun 2025 11:22:46 +0000 Subject: [PATCH] 8274051: Remove supports_vtime()/elapsedVTime() Reviewed-by: kbarrett, iwalulya --- src/hotspot/os/aix/os_aix.cpp | 11 --- src/hotspot/os/bsd/os_bsd.cpp | 4 - src/hotspot/os/linux/os_linux.cpp | 10 --- src/hotspot/os/posix/os_posix.cpp | 2 - src/hotspot/os/windows/os_windows.cpp | 15 ---- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 9 ++- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 2 +- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 78 ++++++++++--------- src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 18 +---- .../share/gc/g1/g1ConcurrentMarkThread.cpp | 18 ++--- .../share/gc/g1/g1ConcurrentMarkThread.hpp | 16 ++-- .../gc/g1/g1ConcurrentMarkThread.inline.hpp | 11 +-- .../share/gc/g1/g1ConcurrentRefineThread.cpp | 20 ++--- .../share/gc/g1/g1ConcurrentRefineThread.hpp | 20 ++--- src/hotspot/share/gc/g1/g1RemSetSummary.cpp | 40 +++++----- src/hotspot/share/gc/g1/g1RemSetSummary.hpp | 19 +++-- src/hotspot/share/gc/g1/g1ServiceThread.cpp | 10 +-- src/hotspot/share/gc/g1/g1ServiceThread.hpp | 2 +- src/hotspot/share/runtime/os.hpp | 9 +-- 19 files changed, 122 insertions(+), 192 deletions(-) diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 9c6218aee16..22636fc5cae 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -879,17 +879,6 @@ void os::free_thread(OSThread* osthread) { //////////////////////////////////////////////////////////////////////////////// // time support -double os::elapsedVTime() { - struct rusage usage; - int retval = getrusage(RUSAGE_THREAD, &usage); - if (retval == 0) { - return usage.ru_utime.tv_sec + usage.ru_stime.tv_sec + (usage.ru_utime.tv_usec + usage.ru_stime.tv_usec) / (1000.0 * 1000); - } else { - // better than nothing, but not much - return elapsedTime(); - } -} - // We use mread_real_time here. // On AIX: If the CPU has a time register, the result will be RTC_POWER and // it has to be converted to real time. AIX documentations suggests to do diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 6f7d9a6de37..4b74e7c00f3 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -782,10 +782,6 @@ void os::free_thread(OSThread* osthread) { //////////////////////////////////////////////////////////////////////////////// // time support -double os::elapsedVTime() { - // better than nothing, but not much - return elapsedTime(); -} #ifdef __APPLE__ void os::Bsd::clock_init() { diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 1a23c956f35..b747fe4d88f 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -1487,16 +1487,6 @@ void os::Linux::capture_initial_stack(size_t max_size) { //////////////////////////////////////////////////////////////////////////////// // time support -double os::elapsedVTime() { - struct rusage usage; - int retval = getrusage(RUSAGE_THREAD, &usage); - if (retval == 0) { - return (double) (usage.ru_utime.tv_sec + usage.ru_stime.tv_sec) + (double) (usage.ru_utime.tv_usec + usage.ru_stime.tv_usec) / (1000 * 1000); - } else { - // better than nothing, but not much - return elapsedTime(); - } -} void os::Linux::fast_thread_clock_init() { clockid_t clockid; diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 303e44eadcb..1444a4f1882 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -1599,8 +1599,6 @@ jlong os::elapsed_frequency() { return NANOSECS_PER_SEC; // nanosecond resolution } -bool os::supports_vtime() { return true; } - // Return the real, user, and system times in seconds from an // arbitrary fixed point in the past. bool os::getTimesSecs(double* process_real_time, diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 24969683a1f..c1311579c5c 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -1194,21 +1194,6 @@ FILETIME java_to_windows_time(jlong l) { return result; } -bool os::supports_vtime() { return true; } - -double os::elapsedVTime() { - FILETIME created; - FILETIME exited; - FILETIME kernel; - FILETIME user; - if (GetThreadTimes(GetCurrentThread(), &created, &exited, &kernel, &user) != 0) { - // the resolution of windows_to_java_time() should be sufficient (ms) - return (double) (windows_to_java_time(kernel) + windows_to_java_time(user)) / MILLIUNITS; - } else { - return elapsedTime(); - } -} - jlong os::javaTimeMillis() { FILETIME wt; GetSystemTimeAsFileTime(&wt); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index b6c18420b82..a7f147611a6 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -1269,6 +1269,9 @@ jint G1CollectedHeap::initialize_service_thread() { jint G1CollectedHeap::initialize() { + if (!os::is_thread_cpu_time_supported()) { + vm_exit_during_initialization("G1 requires cpu time gathering support"); + } // Necessary to satisfy locking discipline assertions. MutexLocker x(Heap_lock); @@ -2234,7 +2237,7 @@ void G1CollectedHeap::gc_epilogue(bool full) { _free_arena_memory_task->notify_new_stats(&_young_gen_card_set_stats, &_collection_set_candidates_card_set_stats); - update_parallel_gc_threads_cpu_time(); + update_perf_counter_cpu_time(); } uint G1CollectedHeap::uncommit_regions(uint region_limit) { @@ -2318,10 +2321,10 @@ void G1CollectedHeap::verify_region_attr_remset_is_tracked() { } #endif -void G1CollectedHeap::update_parallel_gc_threads_cpu_time() { +void G1CollectedHeap::update_perf_counter_cpu_time() { assert(Thread::current()->is_VM_thread(), "Must be called from VM thread to avoid races"); - if (!UsePerfData || !os::is_thread_cpu_time_supported()) { + if (!UsePerfData) { return; } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index ad440577f2d..5305ef475b6 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -262,7 +262,7 @@ public: void set_collection_set_candidates_stats(G1MonotonicArenaMemoryStats& stats); void set_young_gen_card_set_stats(const G1MonotonicArenaMemoryStats& stats); - void update_parallel_gc_threads_cpu_time(); + void update_perf_counter_cpu_time(); private: // Return true if an explicit GC should start a concurrent cycle instead diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 25a9b80093c..ff291cab622 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -71,6 +71,7 @@ #include "runtime/handles.inline.hpp" #include "runtime/java.hpp" #include "runtime/orderAccess.hpp" +#include "runtime/os.hpp" #include "runtime/prefetch.inline.hpp" #include "runtime/threads.hpp" #include "utilities/align.hpp" @@ -507,8 +508,6 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _remark_weak_ref_times(), _cleanup_times(), - _accum_task_vtime(nullptr), - _concurrent_workers(nullptr), _num_concurrent_workers(0), _max_concurrent_workers(0), @@ -542,7 +541,6 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, } _tasks = NEW_C_HEAP_ARRAY(G1CMTask*, _max_num_tasks, mtGC); - _accum_task_vtime = NEW_C_HEAP_ARRAY(double, _max_num_tasks, mtGC); // so that the assertion in MarkingTaskQueue::task_queue doesn't fail _num_active_tasks = _max_num_tasks; @@ -552,8 +550,6 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _task_queues->register_queue(i, task_queue); _tasks[i] = new G1CMTask(i, this, task_queue, _region_mark_stats); - - _accum_task_vtime[i] = 0.0; } reset_at_marking_complete(); @@ -980,30 +976,23 @@ public: void work(uint worker_id) { ResourceMark rm; - double start_vtime = os::elapsedVTime(); + SuspendibleThreadSetJoiner sts_join; - { - SuspendibleThreadSetJoiner sts_join; + assert(worker_id < _cm->active_tasks(), "invariant"); - assert(worker_id < _cm->active_tasks(), "invariant"); + G1CMTask* task = _cm->task(worker_id); + task->record_start_time(); + if (!_cm->has_aborted()) { + do { + task->do_marking_step(G1ConcMarkStepDurationMillis, + true /* do_termination */, + false /* is_serial*/); - G1CMTask* task = _cm->task(worker_id); - task->record_start_time(); - if (!_cm->has_aborted()) { - do { - task->do_marking_step(G1ConcMarkStepDurationMillis, - true /* do_termination */, - false /* is_serial*/); - - _cm->do_yield_check(); - } while (!_cm->has_aborted() && task->has_aborted()); - } - task->record_end_time(); - guarantee(!task->has_aborted() || _cm->has_aborted(), "invariant"); + _cm->do_yield_check(); + } while (!_cm->has_aborted() && task->has_aborted()); } - - double end_vtime = os::elapsedVTime(); - _cm->update_accum_task_vtime(worker_id, end_vtime - start_vtime); + task->record_end_time(); + guarantee(!task->has_aborted() || _cm->has_aborted(), "invariant"); } G1CMConcurrentMarkingTask(G1ConcurrentMark* cm) : @@ -1496,7 +1485,7 @@ void G1ConcurrentMark::remark() { _remark_weak_ref_times.add((now - mark_work_end) * 1000.0); _remark_times.add((now - start) * 1000.0); - _g1h->update_parallel_gc_threads_cpu_time(); + _g1h->update_perf_counter_cpu_time(); policy->record_concurrent_mark_remark_end(); } @@ -2090,6 +2079,23 @@ void G1ConcurrentMark::abort_marking_threads() { _second_overflow_barrier_sync.abort(); } +double G1ConcurrentMark::worker_threads_cpu_time_s() { + class CountCpuTimeThreadClosure : public ThreadClosure { + public: + jlong _total_cpu_time; + + CountCpuTimeThreadClosure() : ThreadClosure(), _total_cpu_time(0) { } + + void do_thread(Thread* t) { + _total_cpu_time += os::thread_cpu_time(t); + } + } cl; + + threads_do(&cl); + + return (double)cl._total_cpu_time / NANOSECS_PER_SEC; +} + static void print_ms_time_info(const char* prefix, const char* name, NumberSeq& ns) { log_trace(gc, marking)("%s%5d %12s: total time = %8.2f s (avg = %8.2f ms).", @@ -2119,7 +2125,7 @@ void G1ConcurrentMark::print_summary_info() { log.trace(" Total stop_world time = %8.2f s.", (_remark_times.sum() + _cleanup_times.sum())/1000.0); log.trace(" Total concurrent time = %8.2f s (%8.2f s marking).", - cm_thread()->vtime_accum(), cm_thread()->vtime_mark_accum()); + cm_thread()->total_mark_cpu_time_s(), cm_thread()->worker_threads_cpu_time_s()); } void G1ConcurrentMark::threads_do(ThreadClosure* tc) const { @@ -2263,8 +2269,6 @@ bool G1CMTask::regular_clock_call() { return false; } - double curr_time_ms = os::elapsedVTime() * 1000.0; - // (4) We check whether we should yield. If we have to, then we abort. if (SuspendibleThreadSet::should_yield()) { // We should yield. To do this we abort the task. The caller is @@ -2274,7 +2278,7 @@ bool G1CMTask::regular_clock_call() { // (5) We check whether we've reached our time quota. If we have, // then we abort. - double elapsed_time_ms = curr_time_ms - _start_time_ms; + double elapsed_time_ms = (double)(os::current_thread_cpu_time() - _start_cpu_time_ns) / NANOSECS_PER_MILLISEC; if (elapsed_time_ms > _time_target_ms) { _has_timed_out = true; return false; @@ -2789,9 +2793,9 @@ void G1CMTask::handle_abort(bool is_serial, double elapsed_time_ms) { phase has visited reach a given limit. Additional invocations to the method clock have been planted in a few other strategic places too. The initial reason for the clock method was to avoid calling - vtime too regularly, as it is quite expensive. So, once it was in - place, it was natural to piggy-back all the other conditions on it - too and not constantly check them throughout the code. + cpu time gathering too regularly, as it is quite expensive. So, + once it was in place, it was natural to piggy-back all the other + conditions on it too and not constantly check them throughout the code. If do_termination is true then do_marking_step will enter its termination protocol. @@ -2814,7 +2818,7 @@ void G1CMTask::do_marking_step(double time_target_ms, bool is_serial) { assert(time_target_ms >= 1.0, "minimum granularity is 1ms"); - _start_time_ms = os::elapsedVTime() * 1000.0; + _start_cpu_time_ns = os::current_thread_cpu_time(); // If do_stealing is true then do_marking_step will attempt to // steal work from the other G1CMTasks. It only makes sense to @@ -2908,8 +2912,8 @@ void G1CMTask::do_marking_step(double time_target_ms, // closure which was statically allocated in this frame doesn't // escape it by accident. set_cm_oop_closure(nullptr); - double end_time_ms = os::elapsedVTime() * 1000.0; - double elapsed_time_ms = end_time_ms - _start_time_ms; + jlong end_cpu_time_ns = os::current_thread_cpu_time(); + double elapsed_time_ms = (double)(end_cpu_time_ns - _start_cpu_time_ns) / NANOSECS_PER_MILLISEC; // Update the step history. _step_times_ms.add(elapsed_time_ms); @@ -2932,7 +2936,7 @@ G1CMTask::G1CMTask(uint worker_id, _mark_stats_cache(mark_stats, G1RegionMarkStatsCache::RegionMarkStatsCacheSize), _calls(0), _time_target_ms(0.0), - _start_time_ms(0.0), + _start_cpu_time_ns(0), _cm_oop_closure(nullptr), _curr_region(nullptr), _finger(nullptr), diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index a4c2e94b2b1..3c3416ebcad 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -446,8 +446,6 @@ class G1ConcurrentMark : public CHeapObj { NumberSeq _remark_weak_ref_times; NumberSeq _cleanup_times; - double* _accum_task_vtime; // Accumulated task vtime - WorkerThreads* _concurrent_workers; uint _num_concurrent_workers; // The number of marking worker threads we're using uint _max_concurrent_workers; // Maximum number of marking worker threads @@ -612,16 +610,8 @@ public: // running. void abort_marking_threads(); - void update_accum_task_vtime(uint i, double vtime) { - _accum_task_vtime[i] += vtime; - } - - double all_task_accum_vtime() { - double ret = 0.0; - for (uint i = 0; i < _max_num_tasks; ++i) - ret += _accum_task_vtime[i]; - return ret; - } + // Total cpu time spent in mark worker threads in seconds. + double worker_threads_cpu_time_s(); // Attempts to steal an object from the task queues of other tasks bool try_stealing(uint worker_id, G1TaskQueueEntry& task_entry); @@ -753,8 +743,8 @@ private: // When the virtual timer reaches this time, the marking step should exit double _time_target_ms; - // Start time of the current marking step - double _start_time_ms; + // Start cpu time of the current marking step + jlong _start_cpu_time_ns; // Oop closure used for iterations over oops G1CMOopClosure* _cm_oop_closure; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp index 83d547966ed..c05e7cc4be4 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp @@ -47,8 +47,6 @@ G1ConcurrentMarkThread::G1ConcurrentMarkThread(G1ConcurrentMark* cm) : ConcurrentGCThread(), - _vtime_start(0.0), - _vtime_accum(0.0), _cm(cm), _state(Idle) { @@ -113,8 +111,6 @@ class G1ConcPhaseTimer : public GCTraceConcTimeImplhas_aborted()); - _vtime_accum = (os::elapsedVTime() - _vtime_start); - - update_threads_cpu_time(); + update_perf_counter_cpu_time(); } _cm->root_regions()->cancel_scan(); } @@ -171,7 +165,7 @@ bool G1ConcurrentMarkThread::phase_clear_cld_claimed_marks() { bool G1ConcurrentMarkThread::phase_scan_root_regions() { G1ConcPhaseTimer p(_cm, "Concurrent Scan Root Regions"); _cm->scan_root_regions(); - update_threads_cpu_time(); + update_perf_counter_cpu_time(); return _cm->has_aborted(); } @@ -231,7 +225,7 @@ bool G1ConcurrentMarkThread::subphase_delay_to_keep_mmu_before_remark() { bool G1ConcurrentMarkThread::subphase_remark() { ConcurrentGCBreakpoints::at("BEFORE MARKING COMPLETED"); - update_threads_cpu_time(); + update_perf_counter_cpu_time(); VM_G1PauseRemark op; VMThread::execute(&op); return _cm->has_aborted(); @@ -241,7 +235,7 @@ bool G1ConcurrentMarkThread::phase_rebuild_and_scrub() { ConcurrentGCBreakpoints::at("AFTER REBUILD STARTED"); G1ConcPhaseTimer p(_cm, "Concurrent Rebuild Remembered Sets and Scrub Regions"); _cm->rebuild_and_scrub(); - update_threads_cpu_time(); + update_perf_counter_cpu_time(); return _cm->has_aborted(); } @@ -342,8 +336,8 @@ void G1ConcurrentMarkThread::concurrent_cycle_end(bool mark_cycle_completed) { ConcurrentGCBreakpoints::notify_active_to_idle(); } -void G1ConcurrentMarkThread::update_threads_cpu_time() { - if (!UsePerfData || !os::is_thread_cpu_time_supported()) { +void G1ConcurrentMarkThread::update_perf_counter_cpu_time() { + if (!UsePerfData) { return; } ThreadTotalCPUTimeClosure tttc(CPUTimeGroups::CPUTimeType::gc_conc_mark); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp index 55655ac2c14..5f9ec4ef404 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,9 +35,6 @@ class G1Policy; class G1ConcurrentMarkThread: public ConcurrentGCThread { friend class VMStructs; - double _vtime_start; // Initial virtual time. - double _vtime_accum; // Accumulated virtual time. - G1ConcurrentMark* _cm; enum ServiceState : uint { @@ -88,10 +85,11 @@ class G1ConcurrentMarkThread: public ConcurrentGCThread { // Constructor G1ConcurrentMarkThread(G1ConcurrentMark* cm); - // Total virtual time so far for this thread and concurrent marking tasks. - double vtime_accum(); - // Marking virtual time so far this thread and concurrent marking tasks. - double vtime_mark_accum(); + // Total cpu time used by all marking related threads (i.e. this thread and the + // marking worker threads) in seconds. + double total_mark_cpu_time_s(); + // Cpu time used by all marking worker threads in seconds. + double worker_threads_cpu_time_s(); G1ConcurrentMark* cm() { return _cm; } @@ -110,7 +108,7 @@ class G1ConcurrentMarkThread: public ConcurrentGCThread { bool in_undo_mark() const; // Update the perf data counter for concurrent mark. - void update_threads_cpu_time(); + void update_perf_counter_cpu_time(); }; #endif // SHARE_GC_G1_G1CONCURRENTMARKTHREAD_HPP diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp index 52ebf1c6e37..254eaf62bb2 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,15 +28,16 @@ #include "gc/g1/g1ConcurrentMarkThread.hpp" #include "gc/g1/g1ConcurrentMark.hpp" +#include "runtime/os.hpp" // Total virtual time so far. -inline double G1ConcurrentMarkThread::vtime_accum() { - return _vtime_accum + _cm->all_task_accum_vtime(); +inline double G1ConcurrentMarkThread::total_mark_cpu_time_s() { + return os::thread_cpu_time(this) + worker_threads_cpu_time_s(); } // Marking virtual time so far -inline double G1ConcurrentMarkThread::vtime_mark_accum() { - return _cm->all_task_accum_vtime(); +inline double G1ConcurrentMarkThread::worker_threads_cpu_time_s() { + return _cm->worker_threads_cpu_time_s(); } inline void G1ConcurrentMarkThread::set_idle() { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp index da1f85eba73..2fa19d46093 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp @@ -40,8 +40,6 @@ G1ConcurrentRefineThread::G1ConcurrentRefineThread(G1ConcurrentRefine* cr, uint worker_id) : ConcurrentGCThread(), - _vtime_start(0.0), - _vtime_accum(0.0), _notifier(Mutex::nosafepoint, FormatBuffer<>("G1 Refine#%d", worker_id), true), _requested_active(false), _refinement_stats(), @@ -53,8 +51,6 @@ G1ConcurrentRefineThread::G1ConcurrentRefineThread(G1ConcurrentRefine* cr, uint } void G1ConcurrentRefineThread::run_service() { - _vtime_start = os::elapsedVTime(); - while (wait_for_completed_buffers()) { SuspendibleThreadSetJoiner sts_join; G1ConcurrentRefineStats active_stats_start = _refinement_stats; @@ -74,7 +70,7 @@ void G1ConcurrentRefineThread::run_service() { } } report_inactive("Deactivated", _refinement_stats - active_stats_start); - track_usage(); + update_perf_counter_cpu_time(); } log_debug(gc, refine)("Stopping %d", _worker_id); @@ -128,12 +124,17 @@ void G1ConcurrentRefineThread::stop_service() { activate(); } +jlong G1ConcurrentRefineThread::cpu_time() { + return os::thread_cpu_time(this); +} + // The (single) primary thread drives the controller for the refinement threads. class G1PrimaryConcurrentRefineThread final : public G1ConcurrentRefineThread { bool wait_for_completed_buffers() override; bool maybe_deactivate() override; void do_refinement_step() override; - void track_usage() override; + // Updates jstat cpu usage for all refinement threads. + void update_perf_counter_cpu_time() override; public: G1PrimaryConcurrentRefineThread(G1ConcurrentRefine* cr) : @@ -179,10 +180,8 @@ void G1PrimaryConcurrentRefineThread::do_refinement_step() { } } -void G1PrimaryConcurrentRefineThread::track_usage() { - G1ConcurrentRefineThread::track_usage(); - // The primary thread is responsible for updating the CPU time for all workers. - if (UsePerfData && os::is_thread_cpu_time_supported()) { +void G1PrimaryConcurrentRefineThread::update_perf_counter_cpu_time() { + if (UsePerfData) { ThreadTotalCPUTimeClosure tttc(CPUTimeGroups::CPUTimeType::gc_conc_refine); cr()->threads_do(&tttc); } @@ -191,6 +190,7 @@ void G1PrimaryConcurrentRefineThread::track_usage() { class G1SecondaryConcurrentRefineThread final : public G1ConcurrentRefineThread { bool wait_for_completed_buffers() override; void do_refinement_step() override; + void update_perf_counter_cpu_time() override { /* Nothing to do. The primary thread does all the work. */ } public: G1SecondaryConcurrentRefineThread(G1ConcurrentRefine* cr, uint worker_id) : diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp index 0711b61b194..b1e34e4b78d 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,9 +39,6 @@ class G1ConcurrentRefineThread: public ConcurrentGCThread { friend class VMStructs; friend class G1CollectedHeap; - double _vtime_start; // Initial virtual time. - double _vtime_accum; // Accumulated virtual time. - Monitor _notifier; bool _requested_active; @@ -71,15 +68,8 @@ protected: // precondition: this is the current thread. virtual void do_refinement_step() = 0; - // Update concurrent refine threads stats. - // If we are in Primary thread, we additionally update CPU time tracking. - virtual void track_usage() { - if (os::supports_vtime()) { - _vtime_accum = (os::elapsedVTime() - _vtime_start); - } else { - _vtime_accum = 0.0; - } - }; + // Update concurrent refine threads cpu time stats. + virtual void update_perf_counter_cpu_time() = 0; // Helper for do_refinement_step implementations. Try to perform some // refinement work, limited by stop_at. Returns true if any refinement work @@ -113,8 +103,8 @@ public: return &_refinement_stats; } - // Total virtual time so far. - double vtime_accum() { return _vtime_accum; } + // Total cpu time spent in this thread so far. + jlong cpu_time(); }; #endif // SHARE_GC_G1_G1CONCURRENTREFINETHREAD_HPP diff --git a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp index 38f874d5359..9d7d10f2483 100644 --- a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp +++ b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp @@ -44,7 +44,7 @@ void G1RemSetSummary::update() { CollectData(G1RemSetSummary * summary) : _summary(summary), _counter(0) {} virtual void do_thread(Thread* t) { G1ConcurrentRefineThread* crt = static_cast(t); - _summary->set_rs_thread_vtime(_counter, crt->vtime_accum()); + _summary->set_refine_thread_cpu_time(_counter, crt->cpu_time()); _counter++; } } collector(this); @@ -53,23 +53,23 @@ void G1RemSetSummary::update() { g1h->concurrent_refine()->threads_do(&collector); } -void G1RemSetSummary::set_rs_thread_vtime(uint thread, double value) { - assert(_rs_threads_vtimes != nullptr, "just checking"); - assert(thread < _num_vtimes, "just checking"); - _rs_threads_vtimes[thread] = value; +void G1RemSetSummary::set_refine_thread_cpu_time(uint thread, jlong value) { + assert(_refine_threads_cpu_times != nullptr, "just checking"); + assert(thread < _num_refine_threads, "just checking"); + _refine_threads_cpu_times[thread] = value; } -double G1RemSetSummary::rs_thread_vtime(uint thread) const { - assert(_rs_threads_vtimes != nullptr, "just checking"); - assert(thread < _num_vtimes, "just checking"); - return _rs_threads_vtimes[thread]; +jlong G1RemSetSummary::refine_thread_cpu_time(uint thread) const { + assert(_refine_threads_cpu_times != nullptr, "just checking"); + assert(thread < _num_refine_threads, "just checking"); + return _refine_threads_cpu_times[thread]; } G1RemSetSummary::G1RemSetSummary(bool should_update) : - _num_vtimes(G1ConcRefinementThreads), - _rs_threads_vtimes(NEW_C_HEAP_ARRAY(double, _num_vtimes, mtGC)) { + _num_refine_threads(G1ConcRefinementThreads), + _refine_threads_cpu_times(NEW_C_HEAP_ARRAY(jlong, _num_refine_threads, mtGC)) { - memset(_rs_threads_vtimes, 0, sizeof(double) * _num_vtimes); + memset(_refine_threads_cpu_times, 0, sizeof(jlong) * _num_refine_threads); if (should_update) { update(); @@ -77,22 +77,22 @@ G1RemSetSummary::G1RemSetSummary(bool should_update) : } G1RemSetSummary::~G1RemSetSummary() { - FREE_C_HEAP_ARRAY(double, _rs_threads_vtimes); + FREE_C_HEAP_ARRAY(jlong, _refine_threads_cpu_times); } void G1RemSetSummary::set(G1RemSetSummary* other) { assert(other != nullptr, "just checking"); - assert(_num_vtimes == other->_num_vtimes, "just checking"); + assert(_num_refine_threads == other->_num_refine_threads, "just checking"); - memcpy(_rs_threads_vtimes, other->_rs_threads_vtimes, sizeof(double) * _num_vtimes); + memcpy(_refine_threads_cpu_times, other->_refine_threads_cpu_times, sizeof(jlong) * _num_refine_threads); } void G1RemSetSummary::subtract_from(G1RemSetSummary* other) { assert(other != nullptr, "just checking"); - assert(_num_vtimes == other->_num_vtimes, "just checking"); + assert(_num_refine_threads == other->_num_refine_threads, "just checking"); - for (uint i = 0; i < _num_vtimes; i++) { - set_rs_thread_vtime(i, other->rs_thread_vtime(i) - rs_thread_vtime(i)); + for (uint i = 0; i < _num_refine_threads; i++) { + set_refine_thread_cpu_time(i, other->refine_thread_cpu_time(i) - refine_thread_cpu_time(i)); } } @@ -383,8 +383,8 @@ void G1RemSetSummary::print_on(outputStream* out, bool show_thread_times) { if (show_thread_times) { out->print_cr(" Concurrent refinement threads times (s)"); out->print(" "); - for (uint i = 0; i < _num_vtimes; i++) { - out->print(" %5.2f", rs_thread_vtime(i)); + for (uint i = 0; i < _num_refine_threads; i++) { + out->print(" %5.2f", (double)refine_thread_cpu_time(i) / NANOSECS_PER_SEC); } out->cr(); } diff --git a/src/hotspot/share/gc/g1/g1RemSetSummary.hpp b/src/hotspot/share/gc/g1/g1RemSetSummary.hpp index f3bb7d3adcc..373f38952c8 100644 --- a/src/hotspot/share/gc/g1/g1RemSetSummary.hpp +++ b/src/hotspot/share/gc/g1/g1RemSetSummary.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,15 +31,14 @@ class G1RemSet; -// A G1RemSetSummary manages statistical information about the G1RemSet - +// A G1RemSetSummary manages statistical information about the remembered set. class G1RemSetSummary { - size_t _num_vtimes; - double* _rs_threads_vtimes; + size_t _num_refine_threads; + jlong* _refine_threads_cpu_times; - void set_rs_thread_vtime(uint thread, double value); + void set_refine_thread_cpu_time(uint thread, jlong value); - // update this summary with current data from various places + // Update this summary with current data from various places. void update(); public: @@ -47,14 +46,14 @@ public: ~G1RemSetSummary(); - // set the counters in this summary to the values of the others + // Set the counters in this summary to the values of the others. void set(G1RemSetSummary* other); - // subtract all counters from the other summary, and set them in the current + // Subtract all counters from the other summary, and set them in the current. void subtract_from(G1RemSetSummary* other); void print_on(outputStream* out, bool show_thread_times); - double rs_thread_vtime(uint thread) const; + jlong refine_thread_cpu_time(uint thread) const; }; #endif // SHARE_GC_G1_G1REMSETSUMMARY_HPP diff --git a/src/hotspot/share/gc/g1/g1ServiceThread.cpp b/src/hotspot/share/gc/g1/g1ServiceThread.cpp index 22675ec2a64..3c96123d14f 100644 --- a/src/hotspot/share/gc/g1/g1ServiceThread.cpp +++ b/src/hotspot/share/gc/g1/g1ServiceThread.cpp @@ -119,7 +119,7 @@ G1ServiceTask* G1ServiceThread::wait_for_task() { void G1ServiceThread::run_task(G1ServiceTask* task) { jlong start = os::elapsed_counter(); - double vstart = os::elapsedVTime(); + jlong start_cpu_time_ns = os::thread_cpu_time(this); assert(task->time() <= start, "task run early: " JLONG_FORMAT " > " JLONG_FORMAT, @@ -130,12 +130,12 @@ void G1ServiceThread::run_task(G1ServiceTask* task) { task->execute(); - update_thread_cpu_time(); + update_perf_counter_cpu_time(); log_debug(gc, task)("G1 Service Thread (%s) (run: %1.3fms) (cpu: %1.3fms)", task->name(), TimeHelper::counter_to_millis(os::elapsed_counter() - start), - (os::elapsedVTime() - vstart) * MILLIUNITS); + (double)(os::thread_cpu_time(this) - start_cpu_time_ns) / NANOSECS_PER_MILLISEC); } void G1ServiceThread::run_service() { @@ -153,8 +153,8 @@ void G1ServiceThread::stop_service() { ml.notify(); } -void G1ServiceThread::update_thread_cpu_time() { - if (UsePerfData && os::is_thread_cpu_time_supported()) { +void G1ServiceThread::update_perf_counter_cpu_time() { + if (UsePerfData) { ThreadTotalCPUTimeClosure tttc(CPUTimeGroups::CPUTimeType::gc_service); tttc.do_thread(this); } diff --git a/src/hotspot/share/gc/g1/g1ServiceThread.hpp b/src/hotspot/share/gc/g1/g1ServiceThread.hpp index cfa7abb6552..4ed9c241562 100644 --- a/src/hotspot/share/gc/g1/g1ServiceThread.hpp +++ b/src/hotspot/share/gc/g1/g1ServiceThread.hpp @@ -121,7 +121,7 @@ class G1ServiceThread: public ConcurrentGCThread { void schedule(G1ServiceTask* task, jlong delay, bool notify); // Update the perf data counter for service thread. - void update_thread_cpu_time(); + void update_perf_counter_cpu_time(); public: G1ServiceThread(); diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index b26ec280e72..a2ff2ad9eca 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -293,13 +293,6 @@ class os: AllStatic { static jlong elapsed_counter(); static jlong elapsed_frequency(); - // The "virtual time" of a thread is the amount of time a thread has - // actually run. The first function indicates whether the OS supports - // this functionality for the current thread, and if so the second - // returns the elapsed virtual time for the current thread. - static bool supports_vtime(); - static double elapsedVTime(); - // Return current local time in a string (YYYY-MM-DD HH:MM:SS). // It is MT safe, but not async-safe, as reading time zone // information may require a lock on some platforms.