22 static const int HIGH_PRIORITY_SCALE = 4;
23 static const int LOW_PRIORITY_RANGE = 25;
29 AdaptiveLru(
const std::string &name,
size_t max_size) :
35 _current_frame_identifier = 0;
36 _weight = adaptive_lru_weight;
37 _max_updates_per_frame = adaptive_lru_max_updates_per_frame;
40 _static_list._next = &_static_list;
41 _static_list._prev = &_static_list;
44 for (index = 0; index < LPP_TotalPriorities; ++index) {
45 _page_array[index]._next = &_page_array[index];
46 _page_array[index]._prev = &_page_array[index];
60 while (_static_list._next != &_static_list) {
61 nassertv(_static_list._next !=
nullptr);
82 if (start_node == &_static_list) {
90 if (--num_updates <= 0) {
95 node->remove_from_list();
96 node->insert_before(&_static_list);
98 }
while (node != start_node && node != &_static_list);
112 int target_priority = page->_priority;
113 unsigned int lifetime_frames = _current_frame_identifier - page->_first_frame_identifier;
114 if (lifetime_frames > 0) {
115 if (page->_update_frame_identifier) {
116 unsigned int update_frames;
118 update_frames = (_current_frame_identifier - page->_update_frame_identifier);
119 if (update_frames > 0) {
120 if (page->_update_total_usage > 0) {
121 PN_stdfloat update_average_frame_utilization =
122 (PN_stdfloat) (page->_update_total_usage) / (PN_stdfloat)update_frames;
124 page->_average_frame_utilization =
125 calculate_exponential_moving_average(update_average_frame_utilization,
126 page->_average_frame_utilization);
128 page->_average_frame_utilization *= 1.0f - _weight;
131 target_priority = page->_priority;
132 if (page->_average_frame_utilization >= 1.0f) {
133 int integer_average_frame_utilization;
135 integer_average_frame_utilization =
136 (int) ((page->_average_frame_utilization - 1.0f) *
137 (PN_stdfloat) HIGH_PRIORITY_SCALE);
138 if (integer_average_frame_utilization >= LPP_New) {
139 integer_average_frame_utilization = LPP_New;
141 integer_average_frame_utilization = LPP_New -
142 integer_average_frame_utilization;
143 target_priority = integer_average_frame_utilization;
145 int integer_average_frame_utilization;
147 integer_average_frame_utilization = (int)
148 (page->_average_frame_utilization *
149 (PN_stdfloat) LOW_PRIORITY_RANGE);
150 integer_average_frame_utilization = LOW_PRIORITY_RANGE -
151 integer_average_frame_utilization;
152 target_priority = LPP_New + integer_average_frame_utilization;
157 page->_update_frame_identifier = _current_frame_identifier;
158 page->_update_total_usage = 0;
161 if (target_priority != page->_priority) {
162 page->_priority = std::min(std::max(target_priority, 0), LPP_TotalPriorities - 1);
176 if (lru != _lru && _lru !=
nullptr) {
183 if (_lru !=
nullptr) {
188 nassertv(lru !=
nullptr);
192 _priority = AdaptiveLru::LPP_New;
193 _first_frame_identifier = _lru->_current_frame_identifier;
194 _current_frame_identifier = _lru->_current_frame_identifier;
205 size_t counted_size = 0;
208 while (node != &_static_list) {
210 if (page->_current_frame_identifier + 1 >= _current_frame_identifier) {
211 counted_size += page->_lru_size;
228 if (_total_size > _max_size) {
239 output(ostream &out)
const {
241 out <<
"AdaptiveLru " << get_name()
242 <<
", " << _total_size <<
" of " << _max_size;
249 write(ostream &out,
int indent_level)
const {
250 indent(out, indent_level) << *
this <<
":\n";
259 for (index = 0; index < LPP_TotalPriorities; ++index) {
261 if (node != &_page_array[index]) {
262 indent(out, indent_level + 2) <<
"Priority " << index <<
":\n";
263 while (node != &_page_array[index]) {
265 indent(out, indent_level + 4) << *page;
267 if (page->_current_frame_identifier + 1 >= _current_frame_identifier) {
287 nassertv(page !=
nullptr && page->_lru ==
this);
290 _total_size += page->_lru_size;
300 nassertv(page !=
nullptr && page->_lru ==
this);
303 _total_size -= page->_lru_size;
313 nassertv(page !=
nullptr && page->_lru ==
this);
316 if (page->_current_frame_identifier == _current_frame_identifier) {
318 ++(page->_current_frame_usage);
322 page->_current_frame_identifier = _current_frame_identifier;
323 page->_last_frame_usage = page->_current_frame_usage;
324 page->_current_frame_usage = 1;
331 ++(page->_update_total_usage);
347 for (index = LPP_TotalPriorities - 1; index >= 0; index--) {
355 while (node != &_page_array[index]) {
360 (page->_current_frame_identifier + 1 >= _current_frame_identifier)) {
370 if (_total_size <= target_size) {
384 }
while (hard_evict && attempts < 2);
397 size_t counted_size = 0;
399 for (index = 0; index < LPP_TotalPriorities; ++index) {
401 while (node != &_page_array[index]) {
403 counted_size += page->_lru_size;
404 if (page->_priority != index) {
405 nout <<
"page " << page <<
" has priority " << page->_priority
406 <<
" but is in queue " << index <<
"\n";
410 bool inserted_ok = pages.insert(page).second;
412 nout <<
"page " << page <<
" appears more than once in the dynamic index\n";
419 if (counted_size != _total_size) {
420 nout <<
"count " << counted_size <<
" bytes in dynamic index, but have " << _total_size <<
" on record\n";
427 while (node != &_static_list) {
429 counted_size += page->_lru_size;
431 if (pages.find(page) == pages.end()) {
432 nout <<
"page " << page <<
" appears in dynamic index, but not in static index (or multiple times in static index)\n";
440 if (counted_size != _total_size) {
441 nout <<
"count " << counted_size <<
" bytes in static index, but have " << _total_size <<
" on record\n";
452 AdaptiveLruPage(
size_t lru_size) :
456 _first_frame_identifier(0),
457 _current_frame_identifier(0),
458 _update_frame_identifier(0),
459 _current_frame_usage(0),
460 _last_frame_usage(0),
461 _update_total_usage(0),
462 _average_frame_utilization(1.0f)
472 _lru_size(copy._lru_size),
474 _first_frame_identifier(0),
475 _current_frame_identifier(0),
476 _update_frame_identifier(0),
477 _current_frame_usage(0),
478 _last_frame_usage(0),
479 _update_total_usage(0),
480 _average_frame_utilization(1.0f)
487 void AdaptiveLruPage::
497 if (_lru !=
nullptr) {
520 void AdaptiveLruPage::
521 output(ostream &out)
const {
522 out <<
"page " <<
this <<
", " << _lru_size;
528 void AdaptiveLruPage::
529 write(ostream &out,
int indent_level)
const {
530 indent(out, indent_level) << *
this <<
"\n";
539 if (_lru ==
nullptr) {
542 return _lru->_current_frame_identifier - _first_frame_identifier;
551 if (_lru ==
nullptr) {
554 return _lru->_current_frame_identifier - _current_frame_identifier;
564 test_adaptive_lru() {
565 int maximum_memory = 3000000;
576 cerr <<
"created lru_page_0: " << lru_page_0 <<
"\n";
580 cerr <<
"created lru_page_1: " << lru_page_1 <<
"\n";
584 cerr <<
"created lru_page_2: " << lru_page_2 <<
"\n";
588 cerr <<
"created lru_page_3: " << lru_page_3 <<
"\n";
592 cerr <<
"created lru_page_4: " << lru_page_4 <<
"\n";
596 cerr <<
"created lru_page_5: " << lru_page_5 <<
"\n";
599 int total_frames = 300;
601 for (index = 0; index < total_frames; index++) {
602 cerr <<
"FRAME " << index <<
"\n";
607 lru_page_0->mark_used_lru(lru);
610 lru_page_1->mark_used_lru(lru);
611 lru_page_1->mark_used_lru(lru);
614 lru_page_2->mark_used_lru(lru);
617 if ((index % 10) == 0) {
618 lru_page_3->mark_used_lru(lru);
622 lru_page_4->mark_used_lru(lru);
626 lru_page_5->mark_used_lru(lru);
630 cerr <<
"Failed validation\n";
644 #endif // test_adaptive_lru size_t count_active_size() const
Returns the total size of the pages that were enqueued since the last call to begin_epoch().
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
unsigned int get_num_frames() const
Returns the number of frames since the page was first added to its LRU.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void do_remove_page(AdaptiveLruPage *page)
Removes a page from the LRU.
void set_lru_size(size_t lru_size)
Specifies the size of this page, presumably in bytes, although any unit is possible.
size_t get_lru_size() const
Returns the size of this page as reported to the LRU, presumably in bytes.
virtual void evict_lru()
Evicts the page from the LRU.
bool do_validate()
Checks that the LRU is internally consistent.
void dequeue_lru()
Removes the page from its AdaptiveLru.
void enqueue_lru(AdaptiveLru *lru)
Adds the page to the LRU for the first time, or marks it recently-accessed if it has already been add...
bool validate()
Checks that the LRU is internally self-consistent.
A base class for all things which can have a name.
get_frame_count
Returns the number of times tick() has been called since the ClockObject was created,...
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Similar to MutexHolder, but for a light mutex.
void begin_epoch()
Marks the end of the previous epoch and the beginning of the next one.
void update_page(AdaptiveLruPage *page)
This updates the page's average utilization.
One atomic piece that may be managed by a AdaptiveLru chain.
void do_add_page(AdaptiveLruPage *page)
Adds a new page the the LRU.
A basic LRU-type algorithm, except that it is adaptive and attempts to avoid evicting pages that have...
void do_access_page(AdaptiveLruPage *page)
Marks a page accessed.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void do_partial_lru_update(int num_updates)
This only updates a number of pages up to the specified maximum_updates.
void do_evict_to(size_t target_size, bool hard_evict)
Evicts pages until the LRU is within the indicated size.
This is our own Panda specialization on the default STL set.
unsigned int get_num_inactive_frames() const
Returns the number of frames since the page was last accessed on its LRU.
void unlock()
Alias for release() to match C++11 semantics.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void lock()
Alias for acquire() to match C++11 semantics.