-
Notifications
You must be signed in to change notification settings - Fork 6.3k
8344009: Improve compiler memory statistics #23530
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
tstuefe
wants to merge
20
commits into
openjdk:master
from
tstuefe:JDK-8344009-Improve-Compiler-memstat
Closed
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
551f2f9
Improved compiler memory statistics
tstuefe b4cf3f4
wip
tstuefe 19ca02f
grooming
tstuefe 89fce6a
more code grooming and comments
tstuefe 4f42616
revert unnecessary copyright change
tstuefe 9e6c566
Merge branch 'master' into JDK-8344009-Improve-Compiler-memstat
tstuefe 64f6375
move TracePhase counter initialization into conditional path
tstuefe dd7a06a
avoid Thread::current in high traffic chunk alloc path
tstuefe 2c56b21
Update src/hotspot/share/opto/chaitin.cpp
tstuefe 438fa6f
remove test code
tstuefe 8abf65d
performance fix
tstuefe b6636ed
final-statistics-switch
tstuefe 444417e
feedback roberto
tstuefe 3052ddf
feedback ashu
tstuefe 48160fd
feedback ashu
tstuefe 9c45cb3
NULL in comment
tstuefe 8d5ed22
Roberto Arenas
tstuefe d1dbba6
Feedback Roberto cont.
tstuefe a974a23
remove unnecessary changes in osream.hpp
tstuefe f82d37c
Merge branch 'master' into JDK-8344009-Improve-Compiler-memstat
tstuefe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
287 changes: 287 additions & 0 deletions
287
src/hotspot/share/compiler/compilationMemStatInternals.hpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,287 @@ | ||
/* | ||
* Copyright (c) 2024, 2025, Red Hat, Inc. and/or its affiliates. | ||
* Copyright (c) 2024, 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 | ||
* under the terms of the GNU General Public License version 2 only, as | ||
* published by the Free Software Foundation. | ||
* | ||
* This code is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* version 2 for more details (a copy is included in the LICENSE file that | ||
* accompanied this code). | ||
* | ||
* You should have received a copy of the GNU General Public License version | ||
* 2 along with this work; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
* | ||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
* or visit www.oracle.com if you need additional information or have any | ||
* questions. | ||
* | ||
*/ | ||
|
||
#ifndef SHARE_COMPILER_COMPILATIONMEMSTATINTERNALS_HPP | ||
#define SHARE_COMPILER_COMPILATIONMEMSTATINTERNALS_HPP | ||
|
||
#include "compiler/compilationMemoryStatistic.hpp" | ||
#include "compiler/compilerDefinitions.hpp" | ||
#include "memory/allocation.hpp" | ||
#include "memory/arena.hpp" | ||
#include "utilities/globalDefinitions.hpp" | ||
|
||
#ifdef COMPILER2 | ||
#include "opto/phase.hpp" | ||
#endif | ||
|
||
class CompileTask; | ||
class Method; | ||
class Symbol; | ||
class outputStream; | ||
|
||
#ifdef COMPILER2 | ||
constexpr int phase_trc_id_max = (int)Phase::PhaseTraceId::max_phase_timers; | ||
constexpr int phase_trc_id_none = (int)Phase::PhaseTraceId::_t_none; | ||
#else | ||
// In minimal builds, the ArenaCounterTable is just a single-dimension vector of arena tags (see below) | ||
constexpr int phase_trc_id_max = 1; | ||
constexpr int phase_trc_id_none = 0; | ||
#endif | ||
inline void check_phase_trace_id(int v) { assert(v >= 0 && v < phase_trc_id_max, "OOB (%d)", v); } | ||
|
||
constexpr int arena_tag_max = (int)Arena::Tag::tag_count; | ||
inline void check_arena_tag(int v) { assert(v >= 0 && v < arena_tag_max, "OOB (%d)", v); } | ||
|
||
// A two-dimensional table, containing byte counters per arena type and | ||
// per compilation phase. | ||
class ArenaCounterTable { | ||
size_t _v[phase_trc_id_max][arena_tag_max]; | ||
public: | ||
ArenaCounterTable(); | ||
void copy_from(const ArenaCounterTable& other); | ||
inline size_t at(int phase_trc_id, int arena_tag) const; | ||
inline void add(size_t size, int phase_trc_id, int arena_tag); | ||
inline void sub(size_t size, int phase_trc_id, int arena_tag); | ||
void print_on(outputStream* ss) const; | ||
void summarize(size_t out[arena_tag_max]) const; | ||
}; | ||
|
||
struct PhaseInfo { | ||
int id, num; | ||
const char* text; | ||
}; | ||
|
||
// A stack keeping track of the current compilation phase. Fixed-width for simplicity | ||
// (we should never go beyond 5 or so in depth). | ||
class PhaseInfoStack { | ||
static constexpr int max_depth = 16; | ||
int _depth; | ||
PhaseInfo _stack[max_depth]; | ||
public: | ||
inline PhaseInfoStack(); | ||
inline bool empty() const { return _depth == 0; } | ||
inline void push(PhaseInfo info); | ||
inline void pop(); | ||
inline const PhaseInfo& top() const; | ||
inline int depth() const { return _depth; } | ||
}; | ||
|
||
// A very simple fixed-width FIFO buffer, used for the phase timeline | ||
template <typename T, int size> | ||
class SimpleFifo { | ||
STATIC_ASSERT((size * 2) < INT_MAX); | ||
T _v[size]; | ||
int _pos; | ||
int _oldest; | ||
uint64_t _lost; | ||
|
||
int current_pos() const { return _pos; } | ||
static int pos_to_index(int pos) { return pos % size; } | ||
T& at(int pos) { return *(_v + pos_to_index(pos)); } | ||
|
||
public: | ||
SimpleFifo() : _pos(0), _oldest(0), _lost(0UL) {} | ||
T& current() { return at(current_pos()); } | ||
T& last() { assert(!empty(), "sanity"); return at(current_pos() - 1); } | ||
bool empty() const { return _pos == _oldest; } | ||
uint64_t lost() const { return _lost; } | ||
|
||
void advance() { | ||
_pos ++; | ||
if (_pos >= size) { | ||
_oldest ++; | ||
_lost ++; | ||
} | ||
if (_pos == INT_MAX) { | ||
_pos -= size; | ||
_oldest -= size; | ||
} | ||
} | ||
|
||
void revert() { | ||
assert(!empty(), "sanity"); | ||
_pos--; | ||
} | ||
|
||
template<typename F> | ||
void iterate_all(F f) const { | ||
for (int i = _oldest; i < _pos; i++) { | ||
const int index = pos_to_index(i); | ||
f(_v[index]); | ||
} | ||
} | ||
|
||
void copy_from(const SimpleFifo& other) { | ||
memcpy(_v, other._v, sizeof(_v)); | ||
_pos = other._pos; | ||
_lost = other._lost; | ||
_oldest = other._oldest; | ||
} | ||
}; | ||
|
||
// Holds a table of n entries; each entry keeping start->end footprints when | ||
// a phase started and ended; each entry also keeping the phase-local peak (if | ||
// a phase caused a temporary spike in footprint that vanished before the phase | ||
// ended). | ||
// Handling nested phases: for this structure, there is always a phase active; | ||
// if a phase ends, we "restart" the parent phase (which often is the | ||
// "outside any phase" phase). | ||
class FootprintTimeline { | ||
public: | ||
static constexpr unsigned max_num_phases = 256; // beyond that we wrap, keeping just the last n phases | ||
private: | ||
template <typename T, typename dT> | ||
struct C { | ||
T start, peak, cur; | ||
void init(T v) { start = cur = peak = v; } | ||
void update(T v) { cur = v; if (v > peak) peak = v; } | ||
dT end_delta() const { return (dT)cur - (dT)start; } | ||
// Returns the peak size during this phase: how high usage rose above either | ||
// start or end of phase. The background is that we want to know the max. memory | ||
// consumption during this phase, but that may not be reflected by the start or the | ||
// end counters if an Arena was created during the phase and only lived temporarily. | ||
size_t temporary_peak_size() const { return MIN2(peak - cur, peak - start); } | ||
tstuefe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
struct Entry { | ||
PhaseInfo info; | ||
int level; | ||
C<size_t, ssize_t> _bytes; | ||
C<unsigned, signed int> _live_nodes; | ||
}; | ||
SimpleFifo<Entry, max_num_phases> _fifo; | ||
DEBUG_ONLY(bool _inbetween_phases;) | ||
public: | ||
FootprintTimeline(); | ||
void copy_from(const FootprintTimeline& other); | ||
inline void on_footprint_change(size_t cur_abs, unsigned cur_nodes); | ||
void on_phase_end(size_t cur_abs, unsigned cur_nodes); | ||
void on_phase_start(PhaseInfo info, size_t cur_abs, unsigned cur_nodes, int level); | ||
void print_on(outputStream* st) const; | ||
}; | ||
|
||
// We keep the name of the involved symbols in Symbol (made permanent) instead of resolving them to string and | ||
// storing those. That significantly reduces footprint for the result store and delays resolving until printing | ||
// time, which may be never. | ||
class FullMethodName { | ||
Symbol* _k; | ||
Symbol* _m; | ||
Symbol* _s; | ||
public: | ||
FullMethodName(); | ||
FullMethodName(const Method* m); | ||
FullMethodName(const FullMethodName& o); | ||
FullMethodName& operator=(const FullMethodName& o); | ||
void make_permanent(); | ||
void print_on(outputStream* st) const; | ||
char* as_C_string(char* buf, size_t len) const; | ||
bool operator== (const FullMethodName& b) const; | ||
DEBUG_ONLY(bool is_test_class() const;) | ||
}; | ||
|
||
// ArenaState is the central data structure holding all statistics and temp data during | ||
// a single compilation. It is created on demand (if memstat is active) and tied to the | ||
// CompilerThread. | ||
class ArenaStatCounter : public CHeapObj<mtCompiler> { | ||
|
||
FullMethodName _fmn; | ||
|
||
// from directives | ||
const bool _should_print_memstat; | ||
const bool _should_crash_on_memlimit; | ||
|
||
// Bytes total now | ||
size_t _current; | ||
// Bytes total at last global peak | ||
size_t _peak; | ||
// Bytes per arena/phase, now | ||
ArenaCounterTable _counters_current; | ||
// Bytes per arena/phase when we last reached the global peak | ||
ArenaCounterTable _counters_at_global_peak; | ||
|
||
// Number of live nodes now (C2 only) | ||
unsigned _live_nodes_current; | ||
// Number of live nodes at global peak (C2 only) | ||
unsigned _live_nodes_at_global_peak; | ||
|
||
// MemLimit handling | ||
const size_t _limit; | ||
bool _hit_limit; | ||
bool _limit_in_process; | ||
|
||
// Keep track of current C2 phase | ||
int _phase_counter; | ||
PhaseInfoStack _phase_info_stack; | ||
|
||
// Keep track of C2 phase allocations over time | ||
FootprintTimeline _timeline; | ||
|
||
const CompilerType _comp_type; | ||
const int _comp_id; | ||
|
||
DEBUG_ONLY(bool _is_test_class;) | ||
|
||
int retrieve_live_node_count() const; | ||
|
||
public: | ||
ArenaStatCounter(const CompileTask* task, size_t limit); | ||
|
||
void on_phase_start(PhaseInfo info); | ||
void on_phase_end(); | ||
|
||
// Account an arena allocation. Returns true if new peak reached. | ||
bool on_arena_chunk_allocation(size_t size, int arena_tag, uint64_t* stamp); | ||
|
||
// Account an arena deallocation. | ||
void on_arena_chunk_deallocation(size_t size, uint64_t stamp); | ||
|
||
void print_peak_state_on(outputStream* st) const; | ||
void print_error_state_on(outputStream* st) const; | ||
|
||
size_t limit() const { return _limit; } | ||
bool hit_limit() const { return _hit_limit; } | ||
bool limit_in_process() const { return _limit_in_process; } | ||
void set_limit_in_process(bool v) { _limit_in_process = v; } | ||
|
||
const FullMethodName& fmn() const { return _fmn; } | ||
bool should_print_memstat() { return _should_print_memstat; }; | ||
bool should_crash_on_memlimit() const { return _should_crash_on_memlimit; }; | ||
|
||
CompilerType comp_type() const { return _comp_type; } | ||
int comp_id() const { return _comp_id; } | ||
DEBUG_ONLY(bool is_test_class() const { return _is_test_class; }) | ||
|
||
// Bytes total at last global peak | ||
size_t peak() const { return _peak; } | ||
|
||
// Bytes per arena/phase when we last reached the global peak | ||
const ArenaCounterTable& counters_at_global_peak() const { return _counters_at_global_peak; } | ||
const FootprintTimeline& timeline() const { return _timeline; } | ||
// Number of live nodes at global peak (C2 only) | ||
unsigned live_nodes_at_global_peak() const { return _live_nodes_at_global_peak; } | ||
|
||
int advance_phase_counter() { return ++_phase_counter; } | ||
}; | ||
|
||
#endif // SHARE_COMPILER_COMPILATIONMEMSTATINTERNALS_HPP |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.