ruby: get rid of the Map class

This commit is contained in:
Nathan Binkert 2010-06-10 23:17:07 -07:00
parent 006818aeea
commit 3df84fd8a0
30 changed files with 289 additions and 463 deletions

View file

@ -33,8 +33,10 @@
#if defined(__GNUC__) && __GNUC__ >= 3
#include <ext/hash_map>
#include <ext/hash_set>
#else
#include <hash_map>
#include <hash_set>
#endif
#include <string>
@ -49,7 +51,9 @@
namespace m5 {
using ::__hash_namespace::hash_multimap;
using ::__hash_namespace::hash_multiset;
using ::__hash_namespace::hash_map;
using ::__hash_namespace::hash_set;
using ::__hash_namespace::hash;
}
@ -88,10 +92,6 @@ namespace __hash_namespace {
return (__stl_hash_string(r.first.c_str())) ^ r.second;
}
};
}
/* namespace __hash_namespace */ }
#endif // __HASHMAP_HH__

View file

@ -60,12 +60,13 @@ class ContainerPrint
void
operator()(const T &elem)
{
out << elem;
// First one doesn't get a space before it. The rest do.
if (first)
first = false;
else
out << " ";
out << elem;
}
};

View file

@ -31,12 +31,10 @@
#include "cpu/rubytest/Check.hh"
#include "cpu/rubytest/CheckTable.hh"
#include "cpu/rubytest/CheckTable.hh"
#include "mem/gems_common/Map.hh"
CheckTable::CheckTable(int _num_cpu_sequencers, RubyTester* _tester)
: m_num_cpu_sequencers(_num_cpu_sequencers), m_tester_ptr(_tester)
{
m_lookup_map_ptr = new Map<Address, Check*>;
physical_address_t physical = 0;
Address address;
@ -76,7 +74,6 @@ CheckTable::~CheckTable()
int size = m_check_vector.size();
for (int i = 0; i < size; i++)
delete m_check_vector[i];
delete m_lookup_map_ptr;
}
void
@ -89,7 +86,7 @@ CheckTable::addCheck(const Address& address)
}
for (int i = 0; i < CHECK_SIZE; i++) {
if (m_lookup_map_ptr->exist(Address(address.getAddress()+i))) {
if (m_lookup_map.count(Address(address.getAddress()+i))) {
// A mapping for this byte already existed, discard the
// entire check
return;
@ -100,7 +97,7 @@ CheckTable::addCheck(const Address& address)
m_num_cpu_sequencers, m_tester_ptr);
for (int i = 0; i < CHECK_SIZE; i++) {
// Insert it once per byte
m_lookup_map_ptr->add(Address(address.getAddress() + i), check_ptr);
m_lookup_map[Address(address.getAddress() + i)] = check_ptr;
}
m_check_vector.push_back(check_ptr);
}
@ -117,13 +114,14 @@ CheckTable::getCheck(const Address& address)
DEBUG_MSG(TESTER_COMP, MedPrio, "Looking for check by address");
DEBUG_EXPR(TESTER_COMP, MedPrio, address);
if (m_lookup_map_ptr->exist(address)) {
Check* check = m_lookup_map_ptr->lookup(address);
assert(check != NULL);
return check;
} else {
m5::hash_map<Address, Check*>::iterator i = m_lookup_map.find(address);
if (i == m_lookup_map.end())
return NULL;
}
Check* check = i->second;
assert(check != NULL);
return check;
}
void

View file

@ -33,12 +33,12 @@
#include <iostream>
#include <vector>
#include "base/hashmap.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/Global.hh"
class Address;
class Check;
class RubyTester;
template <class KEY_TYPE, class VALUE_TYPE> class Map;
class CheckTable
{
@ -64,7 +64,7 @@ class CheckTable
CheckTable& operator=(const CheckTable& obj);
std::vector<Check*> m_check_vector;
Map<Address, Check*>* m_lookup_map_ptr;
m5::hash_map<Address, Check*> m_lookup_map;
int m_num_cpu_sequencers;
RubyTester* m_tester_ptr;

View file

@ -1,185 +0,0 @@
/*
* Copyright (c) 1999-2005 Mark D. Hill and David A. Wood
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* $Id$
*
*/
#ifndef MAP_H
#define MAP_H
#include <cassert>
#include <iostream>
#include <vector>
#include "base/hashmap.hh"
template <class KEY_TYPE, class VALUE_TYPE>
class Map
{
private:
typedef typename m5::hash_map<KEY_TYPE, VALUE_TYPE> MapType;
typedef typename MapType::iterator iterator;
typedef typename MapType::const_iterator const_iterator;
public:
Map() { /* empty */ }
~Map() { /* empty */ }
void add(const KEY_TYPE& key, const VALUE_TYPE& value);
bool exist(const KEY_TYPE& key) const;
int size() const { return m_map.size(); }
void erase(const KEY_TYPE& key) { assert(exist(key)); m_map.erase(key); }
std::vector<KEY_TYPE> keys() const;
std::vector<VALUE_TYPE> values() const;
void deleteKeys();
void deleteValues();
VALUE_TYPE& lookup(const KEY_TYPE& key) const;
void clear() { m_map.clear(); }
void print(std::ostream& out) const;
// Synonyms
void remove(const KEY_TYPE& key) { erase(key); }
void deallocate(const KEY_TYPE& key) { erase(key); }
void allocate(const KEY_TYPE& key) { add(key, VALUE_TYPE()); }
void insert(const KEY_TYPE& key, const VALUE_TYPE& value) { add(key, value); }
// Use default copy constructor and assignment operator
private:
// Data members
// m_map is declared mutable because some methods from the STL "map"
// class that should be const are not. Thus we define this as
// mutable so we can still have conceptually const accessors.
mutable m5::hash_map<KEY_TYPE, VALUE_TYPE> m_map;
};
template <class KEY_TYPE, class VALUE_TYPE>
std::ostream&
operator<<(std::ostream& out, const Map<KEY_TYPE, VALUE_TYPE>& map);
// *********************
template <class KEY_TYPE, class VALUE_TYPE>
void Map<KEY_TYPE, VALUE_TYPE>::add(const KEY_TYPE& key, const VALUE_TYPE& value)
{
// Update or add a new key/value pair
m_map[key] = value;
}
template <class KEY_TYPE, class VALUE_TYPE>
bool Map<KEY_TYPE, VALUE_TYPE>::exist(const KEY_TYPE& key) const
{
return (m_map.count(key) != 0);
}
template <class KEY_TYPE, class VALUE_TYPE>
VALUE_TYPE& Map<KEY_TYPE, VALUE_TYPE>::lookup(const KEY_TYPE& key) const
{
if (!exist(key))
std::cerr << *this << " is looking for " << key << std::endl;
assert(exist(key));
return m_map[key];
}
template <class KEY_TYPE, class VALUE_TYPE>
std::vector<KEY_TYPE> Map<KEY_TYPE, VALUE_TYPE>::keys() const
{
std::vector<KEY_TYPE> keys(m_map.size());
const_iterator iter = m_map.begin();
for (int i = 0; i < m_map.size(); ++i) {
keys[i] = iter->first;
++iter;
}
assert(iter == m_map.end());
return keys;
}
template <class KEY_TYPE, class VALUE_TYPE>
std::vector<VALUE_TYPE> Map<KEY_TYPE, VALUE_TYPE>::values() const
{
std::vector<VALUE_TYPE> values(m_map.size());
const_iterator iter = m_map.begin();
for (int i = 0; i < m_map.size(); ++i) {
values[i] = iter->second;
++iter;
}
assert(iter == m_map.end());
return values;
}
template <class KEY_TYPE, class VALUE_TYPE>
void Map<KEY_TYPE, VALUE_TYPE>::deleteKeys()
{
const_iterator iter;
std::pair<KEY_TYPE, VALUE_TYPE> p;
for (iter = m_map.begin(); iter != m_map.end(); iter++) {
p = *iter;
delete p.first;
}
}
template <class KEY_TYPE, class VALUE_TYPE>
void Map<KEY_TYPE, VALUE_TYPE>::deleteValues()
{
const_iterator iter;
std::pair<KEY_TYPE, VALUE_TYPE> p;
for (iter = m_map.begin(); iter != m_map.end(); iter++) {
p = *iter;
delete p.second;
}
}
template <class KEY_TYPE, class VALUE_TYPE>
void Map<KEY_TYPE, VALUE_TYPE>::print(std::ostream& out) const
{
const_iterator iter;
std::pair<KEY_TYPE, VALUE_TYPE> p;
out << "[";
for (iter = m_map.begin(); iter != m_map.end(); iter++) {
// unparse each basic block
p = *iter;
out << " " << p.first << "=" << p.second;
}
out << " ]";
}
template <class KEY_TYPE, class VALUE_TYPE>
std::ostream&
operator<<(std::ostream& out, const Map<KEY_TYPE, VALUE_TYPE>& map)
{
map.print(out);
return out;
}
#endif //MAP_H

View file

@ -28,8 +28,6 @@
#include "base/intmath.hh"
#include "base/str.hh"
#include "mem/gems_common/Map.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/filters/BlockBloomFilter.hh"
using namespace std;

View file

@ -28,8 +28,6 @@
#include "base/intmath.hh"
#include "base/str.hh"
#include "mem/gems_common/Map.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/filters/BulkBloomFilter.hh"
using namespace std;

View file

@ -28,8 +28,6 @@
#include "base/intmath.hh"
#include "base/str.hh"
#include "mem/gems_common/Map.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/filters/H3BloomFilter.hh"
using namespace std;

View file

@ -28,8 +28,6 @@
#include "base/intmath.hh"
#include "base/str.hh"
#include "mem/gems_common/Map.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/filters/LSB_CountingBloomFilter.hh"
using namespace std;

View file

@ -30,8 +30,6 @@
#include "base/intmath.hh"
#include "base/str.hh"
#include "mem/gems_common/Map.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/filters/MultiBitSelBloomFilter.hh"
using namespace std;

View file

@ -28,8 +28,6 @@
#include "base/intmath.hh"
#include "base/str.hh"
#include "mem/gems_common/Map.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/filters/MultiGrainBloomFilter.hh"
using namespace std;

View file

@ -28,8 +28,6 @@
#include "base/intmath.hh"
#include "base/str.hh"
#include "mem/gems_common/Map.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/filters/NonCountingBloomFilter.hh"
using namespace std;

View file

@ -27,7 +27,6 @@
*/
#include "base/stl_helpers.hh"
#include "mem/gems_common/Map.hh"
#include "mem/protocol/MachineType.hh"
#include "mem/protocol/Protocol.hh"
#include "mem/protocol/TopologyType.hh"

View file

@ -29,26 +29,9 @@
#include "mem/ruby/common/Histogram.hh"
#include "mem/ruby/profiler/AccessTraceForAddress.hh"
AccessTraceForAddress::AccessTraceForAddress()
{
m_histogram_ptr = NULL;
}
AccessTraceForAddress::AccessTraceForAddress(const Address& addr)
{
m_addr = addr;
m_total = 0;
m_loads = 0;
m_stores = 0;
m_atomics = 0;
m_user = 0;
m_sharing = 0;
m_histogram_ptr = NULL;
}
AccessTraceForAddress::~AccessTraceForAddress()
{
if (m_histogram_ptr != NULL) {
if (m_histogram_ptr) {
delete m_histogram_ptr;
m_histogram_ptr = NULL;
}

View file

@ -43,10 +43,13 @@ class Histogram;
class AccessTraceForAddress
{
public:
AccessTraceForAddress();
explicit AccessTraceForAddress(const Address& addr);
AccessTraceForAddress()
: m_loads(0), m_stores(0), m_atomics(0), m_total(0), m_user(0),
m_sharing(0), m_histogram_ptr(NULL)
{ }
~AccessTraceForAddress();
void setAddress(const Address& addr) { m_addr = addr; }
void update(CacheRequestType type, AccessModeType access_mode, NodeID cpu,
bool sharing_miss);
int getTotal() const;

View file

@ -29,10 +29,8 @@
#include <vector>
#include "base/stl_helpers.hh"
#include "mem/gems_common/Map.hh"
#include "mem/gems_common/PrioHeap.hh"
#include "mem/protocol/CacheMsg.hh"
#include "mem/ruby/profiler/AccessTraceForAddress.hh"
#include "mem/ruby/profiler/AddressProfiler.hh"
#include "mem/ruby/profiler/Profiler.hh"
#include "mem/ruby/system/System.hh"
@ -44,30 +42,46 @@ using m5::stl_helpers::operator<<;
// Helper functions
AccessTraceForAddress&
lookupTraceForAddress(const Address& addr, AddressMap* record_map)
lookupTraceForAddress(const Address& addr, AddressMap& record_map)
{
if (!record_map->exist(addr)) {
record_map->add(addr, AccessTraceForAddress(addr));
// we create a static default object here that is used to insert
// since the insertion will create a copy of the object in the
// process. Perhaps this is optimizing early, but it doesn't seem
// like it could hurt.
static const AccessTraceForAddress dflt;
pair<AddressMap::iterator, bool> r =
record_map.insert(make_pair(addr, dflt));
AddressMap::iterator i = r.first;
AccessTraceForAddress &access_trace = i->second;
if (r.second) {
// there was nothing there and the insert succeed, so we need
// to actually set the address.
access_trace.setAddress(addr);
}
return record_map->lookup(addr);
return access_trace;
}
void
printSorted(ostream& out, int num_of_sequencers, const AddressMap* record_map,
printSorted(ostream& out, int num_of_sequencers, const AddressMap &record_map,
string description)
{
const int records_printed = 100;
uint64 misses = 0;
PrioHeap<AccessTraceForAddress*> heap;
std::vector<Address> keys = record_map->keys();
for (int i = 0; i < keys.size(); i++) {
AccessTraceForAddress* record = &(record_map->lookup(keys[i]));
PrioHeap<const AccessTraceForAddress*> heap;
AddressMap::const_iterator i = record_map.begin();
AddressMap::const_iterator end = record_map.end();
for (; i != end; ++i) {
const AccessTraceForAddress* record = &i->second;
misses += record->getTotal();
heap.insert(record);
}
out << "Total_entries_" << description << ": " << keys.size() << endl;
out << "Total_entries_" << description << ": " << record_map.size()
<< endl;
if (g_system_ptr->getProfiler()->getAllInstructions())
out << "Total_Instructions_" << description << ": " << misses << endl;
else
@ -93,7 +107,7 @@ printSorted(ostream& out, int num_of_sequencers, const AddressMap* record_map,
int counter = 0;
while (heap.size() > 0 && counter < records_printed) {
AccessTraceForAddress* record = heap.extractMin();
const AccessTraceForAddress* record = heap.extractMin();
double percent = 100.0 * (record->getTotal() / double(misses));
out << description << " | " << percent << " % " << *record << endl;
all_records.add(record->getTotal());
@ -104,7 +118,7 @@ printSorted(ostream& out, int num_of_sequencers, const AddressMap* record_map,
}
while (heap.size() > 0) {
AccessTraceForAddress* record = heap.extractMin();
const AccessTraceForAddress* record = heap.extractMin();
all_records.add(record->getTotal());
remaining_records.add(record->getTotal());
all_records_log.add(record->getTotal());
@ -130,20 +144,12 @@ printSorted(ostream& out, int num_of_sequencers, const AddressMap* record_map,
AddressProfiler::AddressProfiler(int num_of_sequencers)
{
m_dataAccessTrace = new AddressMap;
m_macroBlockAccessTrace = new AddressMap;
m_programCounterAccessTrace = new AddressMap;
m_retryProfileMap = new AddressMap;
m_num_of_sequencers = num_of_sequencers;
clearStats();
}
AddressProfiler::~AddressProfiler()
{
delete m_dataAccessTrace;
delete m_macroBlockAccessTrace;
delete m_programCounterAccessTrace;
delete m_retryProfileMap;
}
void
@ -225,10 +231,10 @@ AddressProfiler::clearStats()
{
// Clear the maps
m_sharing_miss_counter = 0;
m_dataAccessTrace->clear();
m_macroBlockAccessTrace->clear();
m_programCounterAccessTrace->clear();
m_retryProfileMap->clear();
m_dataAccessTrace.clear();
m_macroBlockAccessTrace.clear();
m_programCounterAccessTrace.clear();
m_retryProfileMap.clear();
m_retryProfileHisto.clear();
m_retryProfileHistoRead.clear();
m_retryProfileHistoWrite.clear();

View file

@ -31,21 +31,21 @@
#include <iostream>
#include "base/hashmap.hh"
#include "mem/protocol/AccessType.hh"
#include "mem/protocol/CacheMsg.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/Global.hh"
#include "mem/ruby/common/Histogram.hh"
#include "mem/ruby/profiler/AccessTraceForAddress.hh"
#include "mem/ruby/system/NodeID.hh"
class AccessTraceForAddress;
class Set;
template <class KEY_TYPE, class VALUE_TYPE> class Map;
class AddressProfiler
{
public:
typedef Map<Address, AccessTraceForAddress> AddressMap;
typedef m5::hash_map<Address, AccessTraceForAddress> AddressMap;
public:
AddressProfiler(int num_of_sequencers);
@ -76,10 +76,10 @@ class AddressProfiler
int64 m_sharing_miss_counter;
AddressMap* m_dataAccessTrace;
AddressMap* m_macroBlockAccessTrace;
AddressMap* m_programCounterAccessTrace;
AddressMap* m_retryProfileMap;
AddressMap m_dataAccessTrace;
AddressMap m_macroBlockAccessTrace;
AddressMap m_programCounterAccessTrace;
AddressMap m_retryProfileMap;
Histogram m_retryProfileHisto;
Histogram m_retryProfileHistoWrite;
Histogram m_retryProfileHistoRead;

View file

@ -27,7 +27,6 @@
*/
#include "mem/gems_common/PrioHeap.hh"
#include "mem/ruby/profiler/AccessTraceForAddress.hh"
#include "mem/ruby/profiler/CacheProfiler.hh"
#include "mem/ruby/profiler/Profiler.hh"
#include "mem/ruby/system/System.hh"

View file

@ -50,7 +50,6 @@
#include "base/stl_helpers.hh"
#include "base/str.hh"
#include "mem/gems_common/Map.hh"
#include "mem/gems_common/PrioHeap.hh"
#include "mem/protocol/CacheMsg.hh"
#include "mem/protocol/MachineType.hh"
@ -73,8 +72,6 @@ static double process_memory_resident();
Profiler::Profiler(const Params *p)
: SimObject(p)
{
m_requestProfileMap_ptr = new Map<string, int>;
m_inst_profiler_ptr = NULL;
m_address_profiler_ptr = NULL;
@ -106,8 +103,6 @@ Profiler::~Profiler()
if (m_periodic_output_file_ptr != &cerr) {
delete m_periodic_output_file_ptr;
}
delete m_requestProfileMap_ptr;
}
void
@ -355,20 +350,20 @@ Profiler::printStats(ostream& out, bool short_stats)
out << "--------------------------------" << endl;
out << endl;
vector<string> requestProfileKeys = m_requestProfileMap_ptr->keys();
sort(requestProfileKeys.begin(), requestProfileKeys.end());
map<string, int>::const_iterator i = m_requestProfileMap.begin();
map<string, int>::const_iterator end = m_requestProfileMap.end();
for (; i != end; ++i) {
const string &key = i->first;
int count = i->second;
for (int i = 0; i < requestProfileKeys.size(); i++) {
int temp_int =
m_requestProfileMap_ptr->lookup(requestProfileKeys[i]);
double percent = (100.0 * double(temp_int)) / double(m_requests);
double percent = (100.0 * double(count)) / double(m_requests);
vector<string> items;
tokenize(items, requestProfileKeys[i], ':');
vector<string>::iterator i = items.begin();
tokenize(items, key, ':');
vector<string>::iterator j = items.begin();
vector<string>::iterator end = items.end();
for (; i != end; ++i)
out << setw(10) << *i;
out << setw(11) << temp_int;
for (; j != end; ++i)
out << setw(10) << *j;
out << setw(11) << count;
out << setw(14) << percent << endl;
}
out << endl;
@ -480,7 +475,7 @@ Profiler::clearStats()
m_memory_to_cache = 0;
// clear HashMaps
m_requestProfileMap_ptr->clear();
m_requestProfileMap.clear();
// count requests profiled
m_requests = 0;
@ -555,11 +550,9 @@ Profiler::profileRequest(const string& requestStr)
{
m_requests++;
if (m_requestProfileMap_ptr->exist(requestStr)) {
(m_requestProfileMap_ptr->lookup(requestStr))++;
} else {
m_requestProfileMap_ptr->add(requestStr, 1);
}
// if it doesn't exist, conveniently, it will be created with the
// default value which is 0
m_requestProfileMap[requestStr]++;
}
void
@ -679,18 +672,14 @@ Profiler::rubyWatch(int id)
out << setw(ID_SPACES) << id << " "
<< "RUBY WATCH " << watch_address << endl;
if (!m_watch_address_list_ptr->exist(watch_address)) {
m_watch_address_list_ptr->add(watch_address, 1);
}
// don't care about success or failure
m_watch_address_set.insert(watch_address);
}
bool
Profiler::watchAddress(Address addr)
{
if (m_watch_address_list_ptr->exist(addr))
return true;
else
return false;
return m_watch_address_set.count(addr) > 0;
}
Profiler *

View file

@ -46,9 +46,11 @@
#define __MEM_RUBY_PROFILER_PROFILER_HH__
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include "base/hashmap.hh"
#include "mem/protocol/AccessModeType.hh"
#include "mem/protocol/AccessType.hh"
#include "mem/protocol/CacheRequestType.hh"
@ -70,8 +72,6 @@
class CacheMsg;
class AddressProfiler;
template <class KEY_TYPE, class VALUE_TYPE> class Map;
class Profiler : public SimObject, public Consumer
{
public:
@ -210,10 +210,10 @@ class Profiler : public SimObject, public Consumer
Histogram m_average_latency_estimate;
Map<Address, int>* m_watch_address_list_ptr;
m5::hash_set<Address> m_watch_address_set;
// counts all initiated cache request including PUTs
int m_requests;
Map <std::string, int>* m_requestProfileMap_ptr;
std::map<std::string, int> m_requestProfileMap;
//added by SS
bool m_hot_lines;

View file

@ -105,7 +105,6 @@
*/
#include "base/cprintf.hh"
#include "mem/gems_common/Map.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/Consumer.hh"
#include "mem/ruby/common/Global.hh"

View file

@ -33,7 +33,6 @@
#include <list>
#include <string>
#include "mem/gems_common/Map.hh"
#include "mem/protocol/MemoryMsg.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/Consumer.hh"

View file

@ -29,7 +29,7 @@
#ifndef __MEM_RUBY_SYSTEM_PERFECTCACHEMEMORY_HH__
#define __MEM_RUBY_SYSTEM_PERFECTCACHEMEMORY_HH__
#include "mem/gems_common/Map.hh"
#include "base/hashmap.hh"
#include "mem/protocol/AccessPermission.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/Global.hh"
@ -94,7 +94,7 @@ class PerfectCacheMemory
PerfectCacheMemory& operator=(const PerfectCacheMemory& obj);
// Data Members (m_prefix)
Map<Address, PerfectCacheLineState<ENTRY> > m_map;
m5::hash_map<Address, PerfectCacheLineState<ENTRY> > m_map;
};
template<class ENTRY>
@ -131,7 +131,7 @@ template<class ENTRY>
inline bool
PerfectCacheMemory<ENTRY>::isTagPresent(const Address& address) const
{
return m_map.exist(line_address(address));
return m_map.count(line_address(address)) > 0;
}
template<class ENTRY>
@ -150,7 +150,7 @@ PerfectCacheMemory<ENTRY>::allocate(const Address& address)
PerfectCacheLineState<ENTRY> line_state;
line_state.m_permission = AccessPermission_Busy;
line_state.m_entry = ENTRY();
m_map.add(line_address(address), line_state);
m_map[line_address(address)] = line_state;
}
// deallocate entry
@ -174,7 +174,7 @@ template<class ENTRY>
inline ENTRY&
PerfectCacheMemory<ENTRY>::lookup(const Address& address)
{
return m_map.lookup(line_address(address)).m_entry;
return m_map[line_address(address)].m_entry;
}
// looks an address up in the cache
@ -182,14 +182,14 @@ template<class ENTRY>
inline const ENTRY&
PerfectCacheMemory<ENTRY>::lookup(const Address& address) const
{
return m_map.lookup(line_address(address)).m_entry;
return m_map[line_address(address)].m_entry;
}
template<class ENTRY>
inline AccessPermission
PerfectCacheMemory<ENTRY>::getPermission(const Address& address) const
{
return m_map.lookup(line_address(address)).m_permission;
return m_map[line_address(address)].m_permission;
}
template<class ENTRY>
@ -199,7 +199,7 @@ PerfectCacheMemory<ENTRY>::changePermission(const Address& address,
{
Address line_address = address;
line_address.makeLineAddress();
PerfectCacheLineState<ENTRY>& line_state = m_map.lookup(line_address);
PerfectCacheLineState<ENTRY>& line_state = m_map[line_address];
AccessPermission old_perm = line_state.m_permission;
line_state.m_permission = new_perm;
}

View file

@ -40,13 +40,10 @@ int persistent_randomize[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
PersistentTable::PersistentTable()
{
m_map_ptr = new Map<Address, PersistentTableEntry>;
}
PersistentTable::~PersistentTable()
{
delete m_map_ptr;
m_map_ptr = NULL;
}
void
@ -63,28 +60,25 @@ PersistentTable::persistentRequestLock(const Address& address,
#endif
assert(address == line_address(address));
if (!m_map_ptr->exist(address)) {
// Allocate if not present
PersistentTableEntry entry;
entry.m_starving.add(locker);
if (type == AccessType_Write) {
entry.m_request_to_write.add(locker);
}
m_map_ptr->add(address, entry);
} else {
PersistentTableEntry& entry = m_map_ptr->lookup(address);
//
static const PersistentTableEntry dflt;
pair<AddressMap::iterator, bool> r =
m_map.insert(AddressMap::value_type(address, dflt));
bool present = !r.second;
AddressMap::iterator i = r.first;
PersistentTableEntry &entry = i->second;
if (present) {
// Make sure we're not already in the locked set
//
assert(!(entry.m_starving.isElement(locker)));
entry.m_starving.add(locker);
if (type == AccessType_Write) {
entry.m_request_to_write.add(locker);
}
assert(entry.m_marked.isSubset(entry.m_starving));
}
entry.m_starving.add(locker);
if (type == AccessType_Write)
entry.m_request_to_write.add(locker);
if (present)
assert(entry.m_marked.isSubset(entry.m_starving));
}
void
@ -100,8 +94,8 @@ PersistentTable::persistentRequestUnlock(const Address& address,
#endif
assert(address == line_address(address));
assert(m_map_ptr->exist(address));
PersistentTableEntry& entry = m_map_ptr->lookup(address);
assert(m_map.count(address));
PersistentTableEntry& entry = m_map[address];
//
// Make sure we're in the locked set
@ -116,7 +110,7 @@ PersistentTable::persistentRequestUnlock(const Address& address,
// Deallocate if empty
if (entry.m_starving.isEmpty()) {
assert(entry.m_marked.isEmpty());
m_map_ptr->erase(address);
m_map.erase(address);
}
}
@ -125,24 +119,31 @@ PersistentTable::okToIssueStarving(const Address& address,
MachineID machId) const
{
assert(address == line_address(address));
if (!m_map_ptr->exist(address)) {
AddressMap::const_iterator i = m_map.find(address);
if (i == m_map.end()) {
// No entry present
return true;
} else if (m_map_ptr->lookup(address).m_starving.isElement(machId)) {
}
const PersistentTableEntry &entry = i->second;
if (entry.m_starving.isElement(machId)) {
// We can't issue another lockdown until are previous unlock
// has occurred
return false;
} else {
return m_map_ptr->lookup(address).m_marked.isEmpty();
}
return entry.m_marked.isEmpty();
}
MachineID
PersistentTable::findSmallest(const Address& address) const
{
assert(address == line_address(address));
assert(m_map_ptr->exist(address));
const PersistentTableEntry& entry = m_map_ptr->lookup(address);
AddressMap::const_iterator i = m_map.find(address);
assert(i != m_map.end());
const PersistentTableEntry& entry = i->second;
return entry.m_starving.smallestElement();
}
@ -150,8 +151,9 @@ AccessType
PersistentTable::typeOfSmallest(const Address& address) const
{
assert(address == line_address(address));
assert(m_map_ptr->exist(address));
const PersistentTableEntry& entry = m_map_ptr->lookup(address);
AddressMap::const_iterator i = m_map.find(address);
assert(i != m_map.end());
const PersistentTableEntry& entry = i->second;
if (entry.m_request_to_write.
isElement(entry.m_starving.smallestElement())) {
return AccessType_Write;
@ -164,15 +166,17 @@ void
PersistentTable::markEntries(const Address& address)
{
assert(address == line_address(address));
if (m_map_ptr->exist(address)) {
PersistentTableEntry& entry = m_map_ptr->lookup(address);
AddressMap::iterator i = m_map.find(address);
if (i == m_map.end())
return;
// None should be marked
assert(entry.m_marked.isEmpty());
PersistentTableEntry& entry = i->second;
// Mark all the nodes currently in the table
entry.m_marked = entry.m_starving;
}
// None should be marked
assert(entry.m_marked.isEmpty());
// Mark all the nodes currently in the table
entry.m_marked = entry.m_starving;
}
bool
@ -181,29 +185,31 @@ PersistentTable::isLocked(const Address& address) const
assert(address == line_address(address));
// If an entry is present, it must be locked
return m_map_ptr->exist(address);
return m_map.count(address) > 0;
}
int
PersistentTable::countStarvingForAddress(const Address& address) const
{
if (m_map_ptr->exist(address)) {
PersistentTableEntry& entry = m_map_ptr->lookup(address);
return (entry.m_starving.count());
} else {
assert(address == line_address(address));
AddressMap::const_iterator i = m_map.find(address);
if (i == m_map.end())
return 0;
}
const PersistentTableEntry& entry = i->second;
return entry.m_starving.count();
}
int
PersistentTable::countReadStarvingForAddress(const Address& address) const
{
if (m_map_ptr->exist(address)) {
PersistentTableEntry& entry = m_map_ptr->lookup(address);
return (entry.m_starving.count() - entry.m_request_to_write.count());
} else {
assert(address == line_address(address));
AddressMap::const_iterator i = m_map.find(address);
if (i == m_map.end())
return 0;
}
const PersistentTableEntry& entry = i->second;
return entry.m_starving.count() - entry.m_request_to_write.count();
}
void

View file

@ -31,7 +31,7 @@
#include <iostream>
#include "mem/gems_common/Map.hh"
#include "base/hashmap.hh"
#include "mem/protocol/AccessType.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/Global.hh"
@ -79,7 +79,8 @@ class PersistentTable
PersistentTable& operator=(const PersistentTable& obj);
// Data Members (m_prefix)
Map<Address, PersistentTableEntry>* m_map_ptr;
typedef m5::hash_map<Address, PersistentTableEntry> AddressMap;
AddressMap m_map;
};
inline std::ostream&

View file

@ -28,7 +28,6 @@
#include "base/str.hh"
#include "cpu/rubytest/RubyTester.hh"
#include "mem/gems_common/Map.hh"
#include "mem/protocol/CacheMsg.hh"
#include "mem/protocol/Protocol.hh"
#include "mem/protocol/Protocol.hh"
@ -92,35 +91,39 @@ Sequencer::wakeup()
// Check across all outstanding requests
int total_outstanding = 0;
std::vector<Address> keys = m_readRequestTable.keys();
for (int i = 0; i < keys.size(); i++) {
SequencerRequest* request = m_readRequestTable.lookup(keys[i]);
if (current_time - request->issue_time >= m_deadlock_threshold) {
WARN_MSG("Possible Deadlock detected");
WARN_EXPR(request);
WARN_EXPR(m_version);
WARN_EXPR(request->ruby_request.paddr);
WARN_EXPR(keys.size());
WARN_EXPR(current_time);
WARN_EXPR(request->issue_time);
WARN_EXPR(current_time - request->issue_time);
ERROR_MSG("Aborting");
}
RequestTable::iterator read = m_readRequestTable.begin();
RequestTable::iterator read_end = m_readRequestTable.end();
for (; read != read_end; ++read) {
SequencerRequest* request = read->second;
if (current_time - request->issue_time < m_deadlock_threshold)
continue;
WARN_MSG("Possible Deadlock detected");
WARN_EXPR(request);
WARN_EXPR(m_version);
WARN_EXPR(request->ruby_request.paddr);
WARN_EXPR(m_readRequestTable.size());
WARN_EXPR(current_time);
WARN_EXPR(request->issue_time);
WARN_EXPR(current_time - request->issue_time);
ERROR_MSG("Aborting");
}
keys = m_writeRequestTable.keys();
for (int i = 0; i < keys.size(); i++) {
SequencerRequest* request = m_writeRequestTable.lookup(keys[i]);
if (current_time - request->issue_time >= m_deadlock_threshold) {
WARN_MSG("Possible Deadlock detected");
WARN_EXPR(request);
WARN_EXPR(m_version);
WARN_EXPR(current_time);
WARN_EXPR(request->issue_time);
WARN_EXPR(current_time - request->issue_time);
WARN_EXPR(keys.size());
ERROR_MSG("Aborting");
}
RequestTable::iterator write = m_writeRequestTable.begin();
RequestTable::iterator write_end = m_writeRequestTable.end();
for (; write != write_end; ++write) {
SequencerRequest* request = write->second;
if (current_time - request->issue_time < m_deadlock_threshold)
continue;
WARN_MSG("Possible Deadlock detected");
WARN_EXPR(request);
WARN_EXPR(m_version);
WARN_EXPR(current_time);
WARN_EXPR(request->issue_time);
WARN_EXPR(current_time - request->issue_time);
WARN_EXPR(m_writeRequestTable.size());
ERROR_MSG("Aborting");
}
total_outstanding += m_writeRequestTable.size();
@ -160,13 +163,14 @@ Sequencer::printProgress(ostream& out) const
out << "---------------" << endl;
out << "outstanding requests" << endl;
std::vector<Address> rkeys = m_readRequestTable.keys();
int read_size = rkeys.size();
out << "proc " << m_version << " Read Requests = " << read_size << endl;
out << "proc " << m_Read
<< " version Requests = " << m_readRequestTable.size() << endl;
// print the request table
for (int i = 0; i < read_size; ++i) {
SequencerRequest *request = m_readRequestTable.lookup(rkeys[i]);
RequestTable::iterator read = m_readRequestTable.begin();
RequestTable::iterator read_end = m_readRequestTable.end();
for (; read != read_end; ++read) {
SequencerRequest* request = read->second;
out << "\tRequest[ " << i << " ] = " << request->type
<< " Address " << rkeys[i]
<< " Posted " << request->issue_time
@ -174,13 +178,14 @@ Sequencer::printProgress(ostream& out) const
total_demand++;
}
std::vector<Address> wkeys = m_writeRequestTable.keys();
int write_size = wkeys.size();
out << "proc " << m_version << " Write Requests = " << write_size << endl;
out << "proc " << m_version
<< " Write Requests = " << m_writeRequestTable.size << endl;
// print the request table
for (int i = 0; i < write_size; ++i){
CacheMsg &request = m_writeRequestTable.lookup(wkeys[i]);
RequestTable::iterator write = m_writeRequestTable.begin();
RequestTable::iterator write_end = m_writeRequestTable.end();
for (; write != write_end; ++write) {
SequencerRequest* request = write->second;
out << "\tRequest[ " << i << " ] = " << request.getType()
<< " Address " << wkeys[i]
<< " Posted " << request.getTime()
@ -231,26 +236,32 @@ Sequencer::insertRequest(SequencerRequest* request)
(request->ruby_request.type == RubyRequestType_RMW_Write) ||
(request->ruby_request.type == RubyRequestType_Locked_Read) ||
(request->ruby_request.type == RubyRequestType_Locked_Write)) {
if (m_writeRequestTable.exist(line_addr)) {
m_writeRequestTable.lookup(line_addr) = request;
pair<RequestTable::iterator, bool> r =
m_writeRequestTable.insert(RequestTable::value_type(line_addr, 0));
bool success = r.second;
RequestTable::iterator i = r.first;
if (!success) {
i->second = request;
// return true;
// drh5: isn't this an error? do you lose the initial request?
assert(0);
}
m_writeRequestTable.allocate(line_addr);
m_writeRequestTable.lookup(line_addr) = request;
i->second = request;
m_outstanding_count++;
} else {
if (m_readRequestTable.exist(line_addr)) {
m_readRequestTable.lookup(line_addr) = request;
pair<RequestTable::iterator, bool> r =
m_readRequestTable.insert(RequestTable::value_type(line_addr, 0));
bool success = r.second;
RequestTable::iterator i = r.first;
if (!success) {
i->second = request;
// return true;
// drh5: isn't this an error? do you lose the initial request?
assert(0);
}
m_readRequestTable.allocate(line_addr);
m_readRequestTable.lookup(line_addr) = request;
i->second = request;
m_outstanding_count++;
}
@ -262,6 +273,14 @@ Sequencer::insertRequest(SequencerRequest* request)
return false;
}
void
Sequencer::markRemoved()
{
m_outstanding_count--;
assert(m_outstanding_count ==
m_writeRequestTable.size() + m_readRequestTable.size());
}
void
Sequencer::removeRequest(SequencerRequest* srequest)
{
@ -276,24 +295,26 @@ Sequencer::removeRequest(SequencerRequest* srequest)
(ruby_request.type == RubyRequestType_RMW_Write) ||
(ruby_request.type == RubyRequestType_Locked_Read) ||
(ruby_request.type == RubyRequestType_Locked_Write)) {
m_writeRequestTable.deallocate(line_addr);
m_writeRequestTable.erase(line_addr);
} else {
m_readRequestTable.deallocate(line_addr);
m_readRequestTable.erase(line_addr);
}
m_outstanding_count--;
assert(m_outstanding_count == m_writeRequestTable.size() + m_readRequestTable.size());
markRemoved();
}
void
Sequencer::writeCallback(const Address& address, DataBlock& data)
{
assert(address == line_address(address));
assert(m_writeRequestTable.exist(line_address(address)));
assert(m_writeRequestTable.count(line_address(address)));
SequencerRequest* request = m_writeRequestTable.lookup(address);
RequestTable::iterator i = m_writeRequestTable.find(address);
assert(i != m_writeRequestTable.end());
SequencerRequest* request = i->second;
removeRequest(request);
m_writeRequestTable.erase(i);
markRemoved();
assert((request->ruby_request.type == RubyRequestType_ST) ||
(request->ruby_request.type == RubyRequestType_RMW_Read) ||
@ -316,10 +337,14 @@ void
Sequencer::readCallback(const Address& address, DataBlock& data)
{
assert(address == line_address(address));
assert(m_readRequestTable.exist(line_address(address)));
assert(m_readRequestTable.count(line_address(address)));
SequencerRequest* request = m_readRequestTable.lookup(address);
removeRequest(request);
RequestTable::iterator i = m_readRequestTable.find(address);
assert(i != m_readRequestTable.end());
SequencerRequest* request = i->second;
m_readRequestTable.erase(i);
markRemoved();
assert((request->ruby_request.type == RubyRequestType_LD) ||
(request->ruby_request.type == RubyRequestType_RMW_Read) ||
@ -409,9 +434,9 @@ RequestStatus
Sequencer::getRequestStatus(const RubyRequest& request)
{
bool is_outstanding_store =
m_writeRequestTable.exist(line_address(Address(request.paddr)));
!!m_writeRequestTable.count(line_address(Address(request.paddr)));
bool is_outstanding_load =
m_readRequestTable.exist(line_address(Address(request.paddr)));
!!m_readRequestTable.count(line_address(Address(request.paddr)));
if (is_outstanding_store) {
if ((request.type == RubyRequestType_LD) ||
(request.type == RubyRequestType_IFETCH) ||
@ -441,7 +466,7 @@ Sequencer::getRequestStatus(const RubyRequest& request)
bool
Sequencer::empty() const
{
return m_writeRequestTable.size() == 0 && m_readRequestTable.size() == 0;
return m_writeRequestTable.empty() && m_readRequestTable.empty();
}
RequestStatus
@ -580,6 +605,21 @@ Sequencer::tryCacheAccess(const Address& addr, CacheRequestType type,
}
#endif
template <class KEY, class VALUE>
std::ostream &
operator<<(ostream &out, const m5::hash_map<KEY, VALUE> &map)
{
typename m5::hash_map<KEY, VALUE>::const_iterator i = map.begin();
typename m5::hash_map<KEY, VALUE>::const_iterator end = map.end();
out << "[";
for (; i != end; ++i)
out << " " << i->first << "=" << i->second;
out << " ]";
return out;
}
void
Sequencer::print(ostream& out) const
{

View file

@ -31,7 +31,7 @@
#include <iostream>
#include "mem/gems_common/Map.hh"
#include "base/hashmap.hh"
#include "mem/protocol/AccessModeType.hh"
#include "mem/protocol/CacheRequestType.hh"
#include "mem/protocol/GenericMachineType.hh"
@ -85,6 +85,7 @@ class Sequencer : public RubyPort, public Consumer
void printStats(std::ostream& out) const;
void checkCoherence(const Address& address);
void markRemoved();
void removeRequest(SequencerRequest* request);
private:
@ -108,8 +109,9 @@ class Sequencer : public RubyPort, public Consumer
CacheMemory* m_dataCache_ptr;
CacheMemory* m_instCache_ptr;
Map<Address, SequencerRequest*> m_writeRequestTable;
Map<Address, SequencerRequest*> m_readRequestTable;
typedef m5::hash_map<Address, SequencerRequest*> RequestTable;
RequestTable m_writeRequestTable;
RequestTable m_readRequestTable;
// Global outstanding request count, across all request tables
int m_outstanding_count;
bool m_deadlock_check_scheduled;

View file

@ -31,7 +31,7 @@
#include <iostream>
#include "mem/gems_common/Map.hh"
#include "base/hashmap.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/Global.hh"
#include "mem/ruby/profiler/Profiler.hh"
@ -73,7 +73,7 @@ class TBETable
TBETable& operator=(const TBETable& obj);
// Data Members (m_prefix)
Map<Address, ENTRY> m_map;
m5::hash_map<Address, ENTRY> m_map;
private:
int m_number_of_TBEs;
@ -94,23 +94,23 @@ TBETable<ENTRY>::isPresent(const Address& address) const
{
assert(address == line_address(address));
assert(m_map.size() <= m_number_of_TBEs);
return m_map.exist(address);
return !!m_map.count(address);
}
template<class ENTRY>
inline void
TBETable<ENTRY>::allocate(const Address& address)
{
assert(isPresent(address) == false);
assert(!isPresent(address));
assert(m_map.size() < m_number_of_TBEs);
m_map.add(address, ENTRY());
m_map[address] = ENTRY();
}
template<class ENTRY>
inline void
TBETable<ENTRY>::deallocate(const Address& address)
{
assert(isPresent(address) == true);
assert(isPresent(address));
assert(m_map.size() > 0);
m_map.erase(address);
}
@ -120,8 +120,8 @@ template<class ENTRY>
inline ENTRY&
TBETable<ENTRY>::lookup(const Address& address)
{
assert(isPresent(address) == true);
return m_map.lookup(address);
assert(isPresent(address));
return m_map.find(address)->second;
}
// looks an address up in the cache
@ -129,8 +129,8 @@ template<class ENTRY>
inline const ENTRY&
TBETable<ENTRY>::lookup(const Address& address) const
{
assert(isPresent(address) == true);
return m_map.lookup(address);
assert(isPresent(address));
return m_map.find(address)->second;
}
template<class ENTRY>

View file

@ -41,9 +41,8 @@ TimerTable::TimerTable()
bool
TimerTable::isReady() const
{
if (m_map.size() == 0) {
if (m_map.empty())
return false;
}
if (!m_next_valid) {
updateNext();
@ -69,9 +68,9 @@ TimerTable::set(const Address& address, Time relative_latency)
{
assert(address == line_address(address));
assert(relative_latency > 0);
assert(m_map.exist(address) == false);
assert(!m_map.count(address));
Time ready_time = g_eventQueue_ptr->getTime() + relative_latency;
m_map.add(address, ready_time);
m_map[address] = ready_time;
assert(m_consumer_ptr != NULL);
g_eventQueue_ptr->scheduleEventAbsolute(m_consumer_ptr, ready_time);
m_next_valid = false;
@ -86,8 +85,8 @@ void
TimerTable::unset(const Address& address)
{
assert(address == line_address(address));
assert(m_map.exist(address) == true);
m_map.remove(address);
assert(m_map.count(address));
m_map.erase(address);
// Don't always recalculate the next ready address
if (address == m_next_address) {
@ -103,24 +102,24 @@ TimerTable::print(std::ostream& out) const
void
TimerTable::updateNext() const
{
if (m_map.size() == 0) {
assert(m_next_valid == false);
if (m_map.empty()) {
assert(!m_next_valid);
return;
}
std::vector<Address> addresses = m_map.keys();
m_next_address = addresses[0];
m_next_time = m_map.lookup(m_next_address);
AddressMap::const_iterator i = m_map.begin();
AddressMap::const_iterator end = m_map.end();
// Search for the minimum time
int size = addresses.size();
for (int i=1; i<size; i++) {
Address maybe_next_address = addresses[i];
Time maybe_next_time = m_map.lookup(maybe_next_address);
if (maybe_next_time < m_next_time) {
m_next_time = maybe_next_time;
m_next_address= maybe_next_address;
m_next_address = i->first;
m_next_time = i->second;
++i;
for (; i != end; ++i) {
if (i->second < m_next_time) {
m_next_address = i->first;
m_next_time = i->second;
}
}
m_next_valid = true;
}

View file

@ -33,7 +33,7 @@
#include <iostream>
#include <string>
#include "mem/gems_common/Map.hh"
#include "base/hashmap.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/Global.hh"
@ -61,7 +61,7 @@ class TimerTable
bool isReady() const;
const Address& readyAddress() const;
bool isSet(const Address& address) const { return m_map.exist(address); }
bool isSet(const Address& address) const { return !!m_map.count(address); }
void set(const Address& address, Time relative_latency);
void unset(const Address& address);
void print(std::ostream& out) const;
@ -74,7 +74,8 @@ class TimerTable
TimerTable& operator=(const TimerTable& obj);
// Data Members (m_prefix)
Map<Address, Time> m_map;
typedef m5::hash_map<Address, Time> AddressMap;
AddressMap m_map;
mutable bool m_next_valid;
mutable Time m_next_time; // Only valid if m_next_valid is true
mutable Address m_next_address; // Only valid if m_next_valid is true