aignacio_sf 068ae5eed3 Add EXPCL_PANDADX to classes.
Update some documentation.
2006-01-05 19:14:20 +00:00

1345 lines
37 KiB
C++

// Filename: lru.cxx
// Created by: aignacio (12Dec05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2006, Disney Enterprises, Inc. All rights
// reserved.
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
//#include "stdafx.h"
#define LRU_UNIT_TEST 0
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "lru.h"
static const int HIGH_PRIORITY_SCALE = 4;
static const int LOW_PRIORITY_RANGE = 25;
////////////////////////////////////////////////////////////////////
// Function: Lru::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
Lru::Lru (int maximum_memory, int maximum_pages, int maximum_page_types)
{
if(this) {
int index;
memset(&this->_m, 0, sizeof (LruVariables));
this->_m.maximum_memory = maximum_memory;
this->_m.maximum_pages = maximum_pages;
this->_m.maximum_page_types = maximum_page_types;
this->_m.available_memory = maximum_memory;
this->_m.current_frame_identifier = 1;
this->_m.weight = 0.20f;
this->set_maximum_frame_bandwidth_utilization(2000000.0f);
for(index = 0; index < MAXIMUM_LRU_PAGE_TYPES; index++) {
this->_m.page_in_function_array[index] = default_page_in_function;
this->_m.page_out_function_array[index] = default_page_out_function;
}
if(maximum_pages > 0) {
this -> _m.lru_page_pool = new LruPage * [maximum_pages];
this -> _m.lru_page_free_pool = new LruPage * [maximum_pages];
for(index = 0; index < maximum_pages; index++) {
LruPage * lru_page;
lru_page = new LruPage ( );
if(lru_page) {
lru_page->_m.pre_allocated = true;
this->_m.lru_page_pool[index] = lru_page;
}
else {
// ERROR
}
}
}
if(maximum_page_types > 0) {
this -> _m.page_type_statistics_array =
new PageTypeStatistics [maximum_page_types];
}
#if ENABLE_MUTEX
this -> _m.mutex = new Mutex ( );
#endif
}
}
////////////////////////////////////////////////////////////////////
// Function: Lru::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
Lru::~Lru ( )
{
int index;
LruPage * lru_page;
// free pre-allocated LruPages
if(this->_m.maximum_pages > 0) {
if(this->_m.lru_page_free_pool) {
for(index = 0; index < this->_m.maximum_pages; index++) {
lru_page = this->_m.lru_page_pool[index];
if(lru_page->_m.in_lru) {
this->remove_page(lru_page);
}
delete lru_page;
}
delete this -> _m.lru_page_free_pool;
}
if(this->_m.lru_page_pool) {
delete this -> _m.lru_page_pool;
}
}
// free dynamically allocated LruPages
for(index = 0; index < LPP_TotalPriorities; index++) {
LruPage * next_lru_page;
lru_page = this->_m.lru_page_array[index];
while(lru_page) {
next_lru_page = lru_page->_m.next;
delete lru_page;
lru_page = next_lru_page;
}
}
if(this->_m.page_type_statistics_array) {
delete this -> _m.page_type_statistics_array;
}
#if ENABLE_MUTEX
if(this->_m.mutex) {
delete this -> _m.mutex;
}
#endif
}
////////////////////////////////////////////////////////////////////
// Function: LruPage::Constructor
// Access: Protected
// Description: Internal function only.
// Call Lru::allocate_page instead.
////////////////////////////////////////////////////////////////////
LruPage::LruPage ( )
{
if(this) {
memset(&this->_m, 0, sizeof (LruPageVariables));
}
}
////////////////////////////////////////////////////////////////////
// Function: LruPage::Destructor
// Access: Protected
// Description: Internal function only.
// Call Lru::free_page instead.
////////////////////////////////////////////////////////////////////
LruPage::~LruPage ( )
{
}
////////////////////////////////////////////////////////////////////
// Function: LruPage::change_priority
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
void LruPage::change_priority (int delta)
{
this->_m.priority_change += delta;
}
////////////////////////////////////////////////////////////////////
// Function: Lru::register_lru_page_type
// Access: Public
// Description: Registers a specific type of page and its
// required page in and out functions.
////////////////////////////////////////////////////////////////////
bool Lru::register_lru_page_type (int index,
LruPageTypeFunction page_in_function,
LruPageTypeFunction page_out_function)
{
bool state;
state = false;
if(index >= 0 && index < MAXIMUM_LRU_PAGE_TYPES) {
this->_m.page_in_function_array[index] = page_in_function;
this->_m.page_out_function_array[index] = page_out_function;
state = true;
}
return state;
}
////////////////////////////////////////////////////////////////////
// Function: Lru::allocate_page
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
LruPage *Lru::allocate_page (int size)
{
LruPage * lru_page;
lru_page = 0;
if(size <= this->_m.maximum_memory) {
if(this->_m.maximum_pages) {
if(this->_m.total_lru_pages_in_free_pool > 0) {
lru_page =
this->_m.lru_page_free_pool [this->_m.total_lru_pages_in_free_pool - 1];
this->_m.total_lru_pages_in_free_pool--;
memset (&lru_page -> _m, 0, sizeof (LruPage::LruPageVariables));
lru_page->_m.pre_allocated = true;
}
else {
if(this->_m.total_lru_pages_in_pool < this->_m.maximum_pages) {
lru_page = this->_m.lru_page_pool[this->_m.total_lru_pages_in_pool];
this->_m.total_lru_pages_in_pool++;
}
else {
// out of pre-allocated LruPages so dynamically allocate a page
lru_page = new LruPage ( );
}
}
}
else {
lru_page = new LruPage;
}
if(lru_page) {
lru_page->_m.lru = this;
lru_page->_m.size = size;
lru_page->_m.first_frame_identifier = this->_m.current_frame_identifier;
lru_page->_m.last_frame_identifier = this->_m.current_frame_identifier;
lru_page->_m.allocated = true;
lru_page->_m.identifier = this->_m.identifier;
lru_page->_m.average_frame_utilization = 1.0f;
this->_m.total_pages++;
this->_m.identifier++;
}
else {
// ERROR: could not allocate LruPage
}
}
else {
// ERROR: requested page size is larger than maximum memory size
}
return lru_page;
}
////////////////////////////////////////////////////////////////////
// Function: Lru::update_start_update_lru_page
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void Lru::update_start_update_lru_page (LruPage *lru_page)
{
if(lru_page) {
if(this->_m.start_update_lru_page == lru_page) {
if(lru_page->_m.next) {
this->_m.start_update_lru_page = lru_page->_m.next;
}
else {
if((this->_m.start_priority_index + 1) >= LPP_TotalPriorities) {
this->_m.start_priority_index = 0;
}
else {
this->_m.start_priority_index = this->_m.start_priority_index + 1;
}
this->_m.start_update_lru_page = 0;
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: Lru::free_page
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void Lru::free_page (LruPage *lru_page)
{
if(this->_m.total_pages > 0) {
if(lru_page) {
LruMutexHolder(this->_m.mutex);
this->update_start_update_lru_page(lru_page);
if(lru_page->_m.in_cache) {
this->_m.available_memory += lru_page->_m.size;
}
if(lru_page->_m.pre_allocated) {
if(this->_m.maximum_pages) {
lru_page->_m.allocated = false;
this->_m.lru_page_free_pool [this->_m.total_lru_pages_in_free_pool] =
lru_page;
this->_m.total_lru_pages_in_free_pool++;
}
else {
// ERROR: this case should not happen
}
}
else {
delete lru_page;
}
this->_m.total_pages--;
}
}
else {
// ERROR: tried to free a page when 0 pages allocated
}
}
////////////////////////////////////////////////////////////////////
// Function: Lru::add_page
// Access: Public
// Description: Adds a page to the LRU based on the given priority.
////////////////////////////////////////////////////////////////////
void Lru::add_page (LruPagePriority priority, LruPage *lru_page)
{
if(lru_page) {
LruMutexHolder(this->_m.mutex);
LruPage * first_lru_page;
lru_page->_m.priority = priority;
first_lru_page = this->_m.lru_page_array[lru_page->_m.priority];
if(first_lru_page) {
first_lru_page->_m.previous = lru_page;
lru_page->_m.next = first_lru_page;
}
this->_m.lru_page_array[lru_page->_m.priority] = lru_page;
lru_page->_m.in_lru = true;
}
}
////////////////////////////////////////////////////////////////////
// Function: Lru::add_cached_page
// Access: Public
// Description: Adds a page that is already paged in to the LRU
// based on the given priority.
////////////////////////////////////////////////////////////////////
void Lru::add_cached_page (LruPagePriority priority, LruPage *lru_page)
{
if(lru_page) {
LruMutexHolder(this->_m.mutex);
lru_page->_m.in_cache = true;
if(lru_page->_m.size > this->_m.available_memory) {
int memory_required;
memory_required = lru_page->_m.size - this->_m.available_memory;
// unload page(s)
this->page_out_lru(memory_required);
}
this->_m.available_memory -= lru_page->_m.size;
this->add_page(priority, lru_page);
}
}
////////////////////////////////////////////////////////////////////
// Function: Lru::remove_page
// Access: Public
// Description: Removes a page from the LRU.
////////////////////////////////////////////////////////////////////
void Lru::remove_page (LruPage *lru_page)
{
if(this) {
if(this->_m.total_pages > 0) {
if(lru_page) {
LruMutexHolder(this->_m.mutex);
this->update_start_update_lru_page(lru_page);
if(lru_page->_m.previous) {
lru_page->_m.previous->_m.next = lru_page->_m.next;
if(lru_page->_m.next) {
lru_page->_m.next->_m.previous = lru_page->_m.previous;
}
}
else {
this->_m.lru_page_array[lru_page->_m.priority] =
lru_page->_m.next;
if(lru_page->_m.next) {
lru_page->_m.next->_m.previous = 0;
}
}
lru_page->_m.next = 0;
lru_page->_m.previous = 0;
lru_page->_m.in_lru = false;
}
}
else {
// ERROR: tried to remove a page when 0 pages are allocated
}
}
else {
// ERROR: Lru == 0, this should not happen
}
}
////////////////////////////////////////////////////////////////////
// Function: Lru::lock_page
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void Lru::lock_page (LruPage *lru_page)
{
lru_page->_m.lock = true;
}
////////////////////////////////////////////////////////////////////
// Function: Lru::unlock_page
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void Lru::unlock_page (LruPage *lru_page)
{
lru_page->_m.lock = false;
}
////////////////////////////////////////////////////////////////////
// Function: Lru::access_page
// Access: Public
// Description: This must always be called before accessing or
// using a page's memory since it pages in the page
// if it is currently paged out.
////////////////////////////////////////////////////////////////////
void Lru::access_page (LruPage *lru_page)
{
if(lru_page) {
if(lru_page->_m.current_frame_identifier
== this->_m.current_frame_identifier) {
lru_page->_m.current_frame_usage++;
this->_m.total_page_all_access_size += lru_page->_m.size;
}
else {
// first update this frame
lru_page->_m.last_frame_identifier = lru_page->_m.current_frame_identifier;
lru_page->_m.current_frame_identifier = this->_m.current_frame_identifier;
lru_page->_m.last_frame_usage = lru_page->_m.current_frame_usage;
lru_page->_m.current_frame_usage = 1;
lru_page->_m.total_frame_page_faults = 0;
this->_m.total_page_access_size += lru_page->_m.size;
}
// check if the page is out
if(lru_page->_m.in_cache == false) {
bool state;
state = true;
LruMutexHolder(this->_m.mutex);
// check memory usage
if(lru_page->_m.size > this->_m.available_memory) {
int memory_required;
memory_required = lru_page->_m.size - this->_m.available_memory;
// unload page(s)
state = this->page_out_lru(memory_required);
}
// load the page in
if(state) {
// PAGE IN CALLBACK
if(this->_m.page_in_function_array[lru_page->_m.type](lru_page)) {
this->_m.available_memory -= lru_page->_m.size;
lru_page->_m.in_cache = true;
// CHANGE THE PAGE PRIORITY FROM LPP_PageOut TO LPP_New
this->remove_page(lru_page);
this->add_page(LPP_New, lru_page);
this->_m.total_lifetime_page_ins++;
}
}
lru_page->_m.total_frame_page_faults++;
lru_page->_m.total_page_faults++;
}
lru_page->_m.total_usage++;
lru_page->_m.update_total_usage++;
this->_m.total_page_access++;
}
}
////////////////////////////////////////////////////////////////////
// Function: Lru::set_maximum_frame_bandwidth_utilization
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void Lru::set_maximum_frame_bandwidth_utilization
(float maximum_frame_bandwidth_utilization)
{
this->_m.maximum_frame_bandwidth_utilization =
maximum_frame_bandwidth_utilization;
this->_m.frame_bandwidth_factor = (float) LPP_TotalPriorities
/ this->_m.maximum_frame_bandwidth_utilization;
}
////////////////////////////////////////////////////////////////////
// Function: Lru::begin_frame
// Access: Public
// Description: This must be called before each frame.
////////////////////////////////////////////////////////////////////
void Lru::begin_frame ( )
{
this->_m.current_frame_identifier++;
this->_m.total_page_ins_last_frame = this->_m.total_page_ins;
this->_m.total_page_outs = this->_m.total_page_outs;
this->_m.total_page_ins = 0;
this->_m.total_page_outs = 0;
}
////////////////////////////////////////////////////////////////////
// Function: Lru::update_page_priorities
// Access: Public
// Description: This updates the priority of a page that has a
// change in priority.
////////////////////////////////////////////////////////////////////
void Lru::update_page_priorities (void)
{
int index;
LruPage *lru_page;
for(index = 0; index < this->_m.total_lru_page_priority_changes; index++) {
int priority;
lru_page = this->_m.lru_page_priority_change_array[index];
this->remove_page(lru_page);
priority = (( int ) lru_page->_m.priority + lru_page->_m.priority_change);
if(priority < 0) {
priority = 0;
}
if(priority >= LPP_TotalPriorities) {
priority = LPP_TotalPriorities - 1;
}
this->add_page((LruPagePriority) priority, lru_page);
lru_page->_m.priority_change = 0;
}
this->_m.total_lru_page_priority_changes = 0;
}
////////////////////////////////////////////////////////////////////
// Function: Lru::update_lru_page
// Access: Public
// Description: This updates the page's average utilization.
// Priority LPP_New is considered to be average usage
// of 1.0 (which means the page is used once per frame
// on average). Priorities < LPP_New are for pages
// used more than once per frame and Priorities >
// LPP_New are for pages used less than once per frame.
// If there was a change in priority, then adds it to
// the array of lru pages with changed priorities
// which will be updated later.
////////////////////////////////////////////////////////////////////
void Lru::update_lru_page (LruPage *lru_page)
{
#if LRU_UNIT_TEST
if(false) {
char string[256];
sprintf(string, " UPDATE %d\n", lru_page->_m.identifier);
OutputDebugString(string);
}
#endif
if(lru_page->_m.lock == false && lru_page->_m.in_cache) {
int delta_priority;
int lifetime_frames;
delta_priority = 0;
lifetime_frames = this->_m.current_frame_identifier -
lru_page->_m.first_frame_identifier;
if(lifetime_frames >= 1) {
if(lru_page->_m.update_frame_identifier) {
int target_priority;
int integer_update_frames;
float update_frames;
float one_over_update_frames;
float update_average_frame_utilization;
integer_update_frames = (this->_m.current_frame_identifier -
lru_page->_m.update_frame_identifier);
if(integer_update_frames > 0) {
update_frames = ( float ) integer_update_frames;
one_over_update_frames = 1.0f / update_frames;
update_average_frame_utilization =
(float) (lru_page->_m.update_total_usage)* one_over_update_frames;
lru_page->_m.average_frame_utilization =
calculate_exponential_moving_average(
update_average_frame_utilization, this->_m.weight,
lru_page->_m.average_frame_utilization);
target_priority = lru_page->_m.priority;
if(lru_page->_m.average_frame_utilization >= 1.0f) {
int integer_average_frame_utilization;
integer_average_frame_utilization =
(int) ((lru_page->_m.average_frame_utilization - 1.0f) *
(float) HIGH_PRIORITY_SCALE);
if(integer_average_frame_utilization >= LPP_New) {
integer_average_frame_utilization = LPP_New;
}
integer_average_frame_utilization = LPP_New -
integer_average_frame_utilization;
target_priority = integer_average_frame_utilization;
}
else {
int integer_average_frame_utilization;
integer_average_frame_utilization = (int)
(lru_page->_m.average_frame_utilization *
(float) LOW_PRIORITY_RANGE);
integer_average_frame_utilization = LOW_PRIORITY_RANGE -
integer_average_frame_utilization;
target_priority = LPP_New + integer_average_frame_utilization;
}
delta_priority = target_priority - lru_page->_m.priority;
lru_page->change_priority(delta_priority);
}
}
lru_page->_m.update_frame_identifier = this->_m.current_frame_identifier;
lru_page->_m.update_total_usage = 0;
}
if(lru_page->_m.priority_change) {
if(this->_m.total_lru_page_priority_changes
< FRAME_MAXIMUM_PRIORITY_CHANGES)
{
this->_m.lru_page_priority_change_array
[this->_m.total_lru_page_priority_changes] = lru_page;
this->_m.total_lru_page_priority_changes++;
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: Lru::update_lru_page_old
// Access: Public
// Description: This updates the page's average utilization and
// adds it to the array of pages with changed
// priorities if there was a change in priority.
// Old method.
////////////////////////////////////////////////////////////////////
void Lru::update_lru_page_old (LruPage *lru_page)
{
#if LRU_UNIT_TEST
if(false) {
char string[256];
sprintf(string, " UPDATE %d\n", lru_page->_m.identifier);
OutputDebugString(string);
}
#endif
if(lru_page->_m.lock == false) {
int delta_priority;
delta_priority = 0;
if(false && lru_page->_m.total_usage > 0) {
int lifetime_frames;
lifetime_frames = this->_m.current_frame_identifier -
lru_page->_m.first_frame_identifier;
if(lifetime_frames >= 10) {
float one_over_update_frames;
if(lru_page->_m.update_frame_identifier) {
int target_priority;
int integer_update_frames;
float update_frames;
float update_average_frame_utilization;
float average_frame_bandwidth_utilization;
integer_update_frames = (this->_m.current_frame_identifier -
lru_page->_m.update_frame_identifier);
if(integer_update_frames > 0) {
update_frames = ( float ) integer_update_frames;
one_over_update_frames = 1.0f / update_frames;
update_average_frame_utilization =
(float) (lru_page->_m.update_total_usage) *
one_over_update_frames;
lru_page->_m.average_frame_utilization =
calculate_exponential_moving_average (
update_average_frame_utilization, this->_m.weight,
lru_page->_m.average_frame_utilization);
average_frame_bandwidth_utilization =
lru_page->_m.average_frame_utilization *
lru_page->_m.size;
target_priority = (int) (average_frame_bandwidth_utilization *
this->_m.frame_bandwidth_factor);
target_priority = (LPP_TotalPriorities - 1) - target_priority;
if(target_priority < 0) {
target_priority = 0;
}
if(target_priority >= LPP_TotalPriorities) {
target_priority = LPP_TotalPriorities - 1;
}
delta_priority = target_priority - lru_page->_m.priority;
lru_page->change_priority(delta_priority);
}
}
lru_page->_m.update_frame_identifier =
this->_m.current_frame_identifier;
lru_page->_m.update_total_usage = 0;
}
}
if(delta_priority == 0) {
if(this->_m.current_frame_identifier
== lru_page->_m.current_frame_identifier) {
// page used during this frame twice or more =>
// increase priority
if(lru_page->_m.current_frame_usage >= 2) {
if(lru_page->_m.priority >= LPP_High) {
lru_page->change_priority(-2);
}
}
if(lru_page->_m.total_frame_page_faults >= 1) {
// multiple page faults this frame => increase priority
if(lru_page->_m.total_frame_page_faults >= 2) {
if(lru_page->_m.priority >= LPP_High) {
lru_page->change_priority(-2);
}
}
else {
// single page faults this frame => increase priority
if(lru_page->_m.priority >= LPP_High) {
lru_page->change_priority(-1);
}
}
}
}
else {
// page not used this frame
int last_access_delta;
last_access_delta
= this->_m.current_frame_identifier
- lru_page->_m.current_frame_identifier;
if(last_access_delta > 1) {
if(lru_page->_m.priority < LPP_Low) {
lru_page->change_priority(+1);
}
}
}
}
if(lru_page->_m.priority_change) {
if(this->_m.total_lru_page_priority_changes
< FRAME_MAXIMUM_PRIORITY_CHANGES) {
this->_m.lru_page_priority_change_array
[this->_m.total_lru_page_priority_changes ]= lru_page;
this->_m.total_lru_page_priority_changes++;
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: Lru::update_entire_lru
// Access: Public
// Description: This updates all the pages in the Lru.
// Lru::partial_lru_update should be called instead
// due to performance reasons.
////////////////////////////////////////////////////////////////////
void Lru::update_entire_lru ( )
{
if(this->_m.total_pages > 0) {
int index;
LruPage *lru_page;
LruMutexHolder(this->_m.mutex);
for(index = 0; index < LPP_TotalPriorities; index++) {
LruPage * next_lru_page;
lru_page = this->_m.lru_page_array[index];
while(lru_page) {
next_lru_page = lru_page->_m.next;
this->update_lru_page(lru_page);
lru_page = next_lru_page;
}
}
this->update_page_priorities( );
}
}
////////////////////////////////////////////////////////////////////
// Function: Lru::partial_lru_update
// Access: Public
// Description: This only updates a number of pages up to the
// specified maximum_updates.
////////////////////////////////////////////////////////////////////
void Lru::partial_lru_update (int maximum_updates)
{
int total_page_updates;
if (maximum_updates <= 0) {
// enforce a minimum number of updates
maximum_updates = 1;
}
total_page_updates = 0;
if(this->_m.total_pages > 0) {
int index;
int start_priority;
LruPage *lru_page;
LruMutexHolder(this->_m.mutex);
start_priority = this->_m.start_priority_index;
{
for(index = start_priority; index < LPP_TotalPriorities; index++) {
LruPage *next_lru_page;
if(index == start_priority) {
if(this->_m.start_update_lru_page) {
lru_page = this->_m.start_update_lru_page;
}
else {
lru_page = this->_m.lru_page_array[index];
}
}
else {
lru_page = this->_m.lru_page_array[index];
}
while(lru_page) {
next_lru_page = lru_page->_m.next;
this->update_lru_page(lru_page);
total_page_updates++;
if(total_page_updates >= maximum_updates) {
if(next_lru_page) {
this->_m.start_priority_index = index;
this->_m.start_update_lru_page = next_lru_page;
}
else {
if((index + 1) >= LPP_TotalPriorities) {
this->_m.start_priority_index = 0;
}
else {
this->_m.start_priority_index = index + 1;
}
this->_m.start_update_lru_page = 0;
}
break;
}
lru_page = next_lru_page;
}
if(total_page_updates >= maximum_updates) {
break;
}
}
}
if(total_page_updates < maximum_updates) {
for(index = 0; index <= start_priority; index++) {
LruPage *next_lru_page;
lru_page = this->_m.lru_page_array[index];
while(lru_page) {
next_lru_page = lru_page->_m.next;
this->update_lru_page(lru_page);
total_page_updates++;
if(total_page_updates >= maximum_updates) {
if(next_lru_page) {
this->_m.start_priority_index = index;
this->_m.start_update_lru_page = next_lru_page;
}
else {
if((index + 1) >= LPP_TotalPriorities) {
this->_m.start_priority_index = 0;
}
else {
this->_m.start_priority_index = index + 1;
}
this->_m.start_update_lru_page = 0;
}
break;
}
lru_page = next_lru_page;
}
if(total_page_updates >= maximum_updates) {
break;
}
}
}
if(total_page_updates < maximum_updates) {
this->_m.start_priority_index = 0;
this->_m.start_update_lru_page = 0;
}
this->update_page_priorities( );
}
}
////////////////////////////////////////////////////////////////////
// Function: Lru::unlock_all_pages
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void Lru::unlock_all_pages (void)
{
if(this->_m.total_pages > 0) {
int index;
for(index = 0; index < LPP_TotalPriorities; index++) {
LruPage *lru_page;
LruPage *next_lru_page;
lru_page = this->_m.lru_page_array[index];
while(lru_page) {
next_lru_page = lru_page->_m.next;
lru_page->_m.lock = false;
lru_page = next_lru_page;
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: Lru::page_out_lru
// Access: Public
// Description: Pages out the lowest priority pages until the
// memory_required is satisfied. This will unlock
// all pages if needed.
////////////////////////////////////////////////////////////////////
bool Lru::page_out_lru (int memory_required)
{
bool state;
int attempts;
state = false;
attempts = 0;
if(this->_m.total_pages > 0) {
LruMutexHolder(this->_m.mutex);
do {
int index;
// page out lower priority pages first
for(index = LPP_PageOut - 1; index >= 0; index--) {
LruPage *lru_page;
LruPage *next_lru_page;
lru_page = this->_m.lru_page_array[index];
while(lru_page) {
next_lru_page = lru_page->_m.next;
if(lru_page->_m.lock == false && lru_page->_m.in_cache) {
memory_required -= lru_page->_m.size;
this->_m.available_memory += lru_page->_m.size;
lru_page->_m.in_cache = false;
// PAGE OUT CALLBACK
this->_m.page_out_function_array[lru_page->_m.type](lru_page);
this->_m.total_lifetime_page_outs++;
// MOVE THE PAGE TO THE LPP_PageOut PRIORITY
this->remove_page(lru_page);
this->add_page(LPP_PageOut, lru_page);
if(memory_required <= 0) {
break;
}
}
lru_page = next_lru_page;
}
if(memory_required <= 0) {
break;
}
}
if(memory_required > 0) {
// WARNING: pages could not be freed, all pages unlocked
this->unlock_all_pages( );
state = false;
}
else {
state = true;
}
attempts++;
} while(state == false && attempts < 2);
}
return state;
}
////////////////////////////////////////////////////////////////////
// Function: Lru::count_priority_level_pages
// Access: Public
// Description: Debug function. Counts the number of pages for each
// priority level.
////////////////////////////////////////////////////////////////////
void Lru::count_priority_level_pages (void)
{
int index;
LruMutexHolder(this->_m.mutex);
for(index = 0; index < LPP_TotalPriorities; index++) {
int total_pages;
LruPage *lru_page;
LruPage *next_lru_page;
total_pages = 0;
lru_page = this->_m.lru_page_array[index];
while(lru_page) {
next_lru_page = lru_page->_m.next;
total_pages++;
lru_page = next_lru_page;
}
this->_m.lru_page_count_array[index] = total_pages;
}
}
////////////////////////////////////////////////////////////////////
// Function: Lru::calculate_lru_statistics
// Access: Public
// Description: Debug function.
////////////////////////////////////////////////////////////////////
void Lru::calculate_lru_statistics (void)
{
LruMutexHolder(this->_m.mutex);
if(this->_m.maximum_page_types > 0) {
int index;
memset(this->_m.page_type_statistics_array, 0,
sizeof (PageTypeStatistics) * this->_m.maximum_page_types);
for(index = 0; index < LPP_TotalPriorities; index++) {
LruPage *lru_page;
LruPage *next_lru_page;
PageTypeStatistics *page_type_statistics;
lru_page = this->_m.lru_page_array[index];
while(lru_page) {
int type;
next_lru_page = lru_page->_m.next;
type = lru_page->_m.type;
page_type_statistics = &this->_m.page_type_statistics_array[type];
page_type_statistics->total_pages++;
if(lru_page->_m.in_cache) {
page_type_statistics->total_pages_in++;
page_type_statistics->total_memory_in += lru_page->_m.size;
}
else {
page_type_statistics->total_pages_out++;
page_type_statistics->total_memory_out += lru_page->_m.size;
}
lru_page = next_lru_page;
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: calculate_exponential_moving_average
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
float calculate_exponential_moving_average(float value,
float weight, float average)
{
return ((value - average) * weight) + average;
}
////////////////////////////////////////////////////////////////////
// Function: default_page_in_function
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
bool default_page_in_function(LruPage *lru_page)
{
#if LRU_UNIT_TEST
char string[256];
sprintf(string, " PAGE IN %d\n", lru_page->_m.identifier);
OutputDebugString(string);
#endif
return true;
}
////////////////////////////////////////////////////////////////////
// Function: default_page_out_function
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
bool default_page_out_function(LruPage *lru_page)
{
#if LRU_UNIT_TEST
char string[256];
sprintf(string, " PAGE OUT %d\n", lru_page->_m.identifier);
OutputDebugString(string);
#endif
return true;
}
#if LRU_UNIT_TEST
////////////////////////////////////////////////////////////////////
// Function: test_ema
// Access:
// Description: Unit test function for ema.
////////////////////////////////////////////////////////////////////
void test_ema(void)
{
int index;
float usage;
float weight;
float average;
weight = 0.2f;
average = 1.0f;
for(index = 0; index < 50; index++) {
if(index < 25) {
usage = (float) (index & 0x01);
}
else {
usage = 0.0f;
}
average =
calculate_exponential_moving_average(usage, weight, average);
char string[256];
sprintf(string, "%d %f\n", index, average);
OutputDebugString(string);
}
}
////////////////////////////////////////////////////////////////////
// Function: test_lru
// Access:
// Description: Unit test function for Lru.
////////////////////////////////////////////////////////////////////
void test_lru(void)
{
int maximum_memory;
int maximum_pages;
int maximum_page_types;
Lru *lru;
test_ema( );
maximum_memory = 3000000;
maximum_pages = 3;
maximum_page_types = 4;
lru = new Lru (maximum_memory, maximum_pages, maximum_page_types);
if(lru) {
lru->_m.minimum_memory = 1000000;
LruPage *lru_page_0;
LruPage *lru_page_1;
LruPage *lru_page_2;
LruPage *lru_page_3;
LruPage *lru_page_4;
LruPage *lru_page_5;
lru_page_0 = lru->allocate_page(1000000);
if(lru_page_0) {
lru->add_page(LPP_PageOut, lru_page_0);
}
lru_page_1 = lru->allocate_page(1000000);
if(lru_page_1) {
lru->add_page(LPP_PageOut, lru_page_1);
}
lru_page_2 = lru->allocate_page(1000000);
if(lru_page_2) {
lru->add_page(LPP_PageOut, lru_page_2);
}
lru_page_3 = lru->allocate_page(1000000);
if(lru_page_3) {
lru->add_page(LPP_PageOut, lru_page_3);
}
lru_page_4 = lru->allocate_page(1000000);
if(lru_page_4) {
lru->add_page(LPP_PageOut, lru_page_4);
}
lru_page_5 = lru->allocate_page(1000000);
if(lru_page_5) {
lru->add_page(LPP_PageOut, lru_page_5);
}
int index;
int total_frames;
total_frames = 300;
for(index = 0; index < total_frames; index++) {
char string[256];
sprintf(string, "FRAME %d\n", index);
OutputDebugString(string);
lru->begin_frame( );
if(index <= 5) {
lru->access_page(lru_page_0);
}
lru->access_page(lru_page_1);
lru->access_page(lru_page_1);
if(index & 0x01) {
lru->access_page(lru_page_2);
}
if((index % 10) == 0) {
lru->access_page(lru_page_3);
}
if(index >= 100) {
lru->access_page(lru_page_4);
}
if(index >= 200) {
lru->access_page(lru_page_5);
}
if(false) {
lru->update_entire_lru( );
}
else {
int maximum_updates;
maximum_updates = 3;
lru->partial_lru_update(maximum_updates);
}
}
if(!true) {
lru->remove_page(lru_page_2);
lru->free_page(lru_page_2);
lru->remove_page(lru_page_3);
lru->free_page(lru_page_3);
lru->remove_page(lru_page_1);
lru->free_page(lru_page_1);
}
delete lru;
}
}
#endif