* Copyright (c) 2016-2022 Google, Inc. 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 Google, Inc. 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 GOOGLE, INC. 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.
*/
#include <iostream>
#undef NDEBUG
#include <assert.h>
#include "cache_replacement_policy_unit_test.h"
#include "simulator/cache_fifo.h"
#include "simulator/cache_lru.h"
namespace dynamorio {
namespace drmemtrace {
enum {
ADDR_A,
ADDR_B,
ADDR_C,
ADDR_D,
ADDR_E,
ADDR_F,
ADDR_G,
ADDR_H,
ADDR_I,
ADDR_J,
ADDR_K,
ADDR_L,
};
static const std::vector<addr_t> addr_vec = { 128 * 0, 128 * 1, 128 * 2, 128 * 3,
128 * 4, 128 * 5, 128 * 6, 128 * 7,
128 * 8, 128 * 9, 128 * 10, 128 * 11 };
template <class T> class cache_policy_test_t : public T {
int associativity_;
int line_size_;
int total_size_;
public:
cache_policy_test_t(int associativity, int line_size, int total_size)
{
associativity_ = associativity;
line_size_ = line_size;
total_size_ = total_size;
}
void
initialize_cache()
{
caching_device_stats_t *stats = new cache_stats_t(line_size_, "",
true);
if (!this->init(associativity_, line_size_, total_size_, nullptr, stats,
nullptr)) {
std::cerr << this->get_replace_policy() << " cache failed to initialize\n";
exit(1);
}
}
void
access_and_check(const addr_t addr, const int expected_replacement_way_after_access)
{
memref_t ref;
ref.data.type = TRACE_TYPE_READ;
ref.data.size = 1;
ref.data.addr = addr;
this->request(ref);
assert(this->get_next_way_to_replace(this->get_block_index(addr)) ==
expected_replacement_way_after_access);
}
void
invalidate_and_check(const addr_t addr,
const int expected_replacement_way_after_access)
{
this->invalidate(this->compute_tag(addr), INVALIDATION_INCLUSIVE);
assert(this->get_next_way_to_replace(this->get_block_index(addr)) ==
expected_replacement_way_after_access);
}
bool
tags_are_different(const std::vector<addr_t> &addresses)
{
for (int i = 0; i < addresses.size(); ++i) {
for (int j = 0; j < addresses.size(); ++j) {
if (i != j) {
if (this->compute_tag(addresses[i]) ==
this->compute_tag(addresses[j])) {
return false;
}
}
}
}
return true;
}
bool
block_indices_are_identical(const std::vector<addr_t> &addresses)
{
for (int i = 1; i < addresses.size(); ++i) {
if (this->get_block_index(addresses[i - 1]) !=
this->get_block_index(addresses[i])) {
return false;
}
}
return true;
}
};
void
unit_test_cache_lru_four_way()
{
cache_policy_test_t<cache_lru_t> cache_lru_test(4,
32,
256);
cache_lru_test.initialize_cache();
assert(cache_lru_test.get_replace_policy() == "LRU");
assert(cache_lru_test.block_indices_are_identical(addr_vec));
assert(cache_lru_test.tags_are_different(addr_vec));
cache_lru_test.access_and_check(addr_vec[ADDR_A], 1);
cache_lru_test.access_and_check(addr_vec[ADDR_B], 2);
cache_lru_test.access_and_check(addr_vec[ADDR_C], 3);
cache_lru_test.access_and_check(addr_vec[ADDR_D], 0);
cache_lru_test.access_and_check(addr_vec[ADDR_A], 1);
cache_lru_test.access_and_check(addr_vec[ADDR_A], 1);
cache_lru_test.access_and_check(addr_vec[ADDR_A], 1);
cache_lru_test.access_and_check(addr_vec[ADDR_E], 2);
cache_lru_test.invalidate_and_check(addr_vec[ADDR_A], 0);
cache_lru_test.access_and_check(addr_vec[ADDR_F], 2);
cache_lru_test.access_and_check(addr_vec[ADDR_A], 3);
cache_lru_test.access_and_check(addr_vec[ADDR_B], 1);
cache_lru_test.access_and_check(addr_vec[ADDR_C], 0);
cache_lru_test.invalidate_and_check(addr_vec[ADDR_C], 1);
cache_lru_test.access_and_check(addr_vec[ADDR_D], 0);
cache_lru_test.access_and_check(addr_vec[ADDR_C], 2);
cache_lru_test.access_and_check(addr_vec[ADDR_A], 3);
cache_lru_test.access_and_check(addr_vec[ADDR_E], 1);
}
void
unit_test_cache_lru_eight_way()
{
cache_policy_test_t<cache_lru_t> cache_lru_test(8,
64,
1024);
cache_lru_test.initialize_cache();
assert(cache_lru_test.get_replace_policy() == "LRU");
assert(cache_lru_test.block_indices_are_identical(addr_vec));
assert(cache_lru_test.tags_are_different(addr_vec));
cache_lru_test.access_and_check(addr_vec[ADDR_A], 1);
cache_lru_test.access_and_check(addr_vec[ADDR_B], 2);
cache_lru_test.access_and_check(addr_vec[ADDR_C], 3);
cache_lru_test.access_and_check(addr_vec[ADDR_D], 4);
cache_lru_test.access_and_check(addr_vec[ADDR_E], 5);
cache_lru_test.access_and_check(addr_vec[ADDR_F], 6);
cache_lru_test.access_and_check(addr_vec[ADDR_G], 7);
cache_lru_test.access_and_check(addr_vec[ADDR_H], 0);
cache_lru_test.access_and_check(addr_vec[ADDR_E], 0);
cache_lru_test.access_and_check(addr_vec[ADDR_A], 1);
cache_lru_test.access_and_check(addr_vec[ADDR_A], 1);
cache_lru_test.access_and_check(addr_vec[ADDR_I], 2);
cache_lru_test.access_and_check(addr_vec[ADDR_J], 3);
cache_lru_test.access_and_check(addr_vec[ADDR_K], 5);
cache_lru_test.access_and_check(addr_vec[ADDR_L], 6);
cache_lru_test.access_and_check(addr_vec[ADDR_L], 6);
cache_lru_test.invalidate_and_check(addr_vec[ADDR_G], 6);
cache_lru_test.access_and_check(addr_vec[ADDR_B], 7);
cache_lru_test.access_and_check(addr_vec[ADDR_C], 4);
cache_lru_test.invalidate_and_check(addr_vec[ADDR_L], 5);
cache_lru_test.invalidate_and_check(addr_vec[ADDR_J], 2);
cache_lru_test.invalidate_and_check(addr_vec[ADDR_C], 2);
cache_lru_test.access_and_check(addr_vec[ADDR_I], 2);
cache_lru_test.access_and_check(addr_vec[ADDR_C], 5);
cache_lru_test.access_and_check(addr_vec[ADDR_D], 7);
cache_lru_test.access_and_check(addr_vec[ADDR_F], 4);
}
void
unit_test_cache_fifo_four_way()
{
cache_policy_test_t<cache_fifo_t> cache_fifo_test(4,
32,
256);
cache_fifo_test.initialize_cache();
assert(cache_fifo_test.get_replace_policy() == "FIFO");
assert(cache_fifo_test.block_indices_are_identical(addr_vec));
assert(cache_fifo_test.tags_are_different(addr_vec));
cache_fifo_test.access_and_check(addr_vec[ADDR_A], 1);
cache_fifo_test.access_and_check(addr_vec[ADDR_B], 2);
cache_fifo_test.access_and_check(addr_vec[ADDR_C], 3);
cache_fifo_test.access_and_check(addr_vec[ADDR_D], 0);
cache_fifo_test.access_and_check(addr_vec[ADDR_A], 0);
cache_fifo_test.access_and_check(addr_vec[ADDR_A], 0);
cache_fifo_test.access_and_check(addr_vec[ADDR_A], 0);
cache_fifo_test.access_and_check(addr_vec[ADDR_E], 1);
cache_fifo_test.access_and_check(addr_vec[ADDR_F], 2);
cache_fifo_test.access_and_check(addr_vec[ADDR_F], 2);
cache_fifo_test.access_and_check(addr_vec[ADDR_G], 3);
cache_fifo_test.access_and_check(addr_vec[ADDR_G], 3);
cache_fifo_test.access_and_check(addr_vec[ADDR_H], 0);
cache_fifo_test.access_and_check(addr_vec[ADDR_A], 1);
cache_fifo_test.invalidate_and_check(addr_vec[ADDR_G], 1);
cache_fifo_test.access_and_check(addr_vec[ADDR_G], 2);
cache_fifo_test.access_and_check(addr_vec[ADDR_B], 3);
cache_fifo_test.invalidate_and_check(addr_vec[ADDR_H], 3);
cache_fifo_test.invalidate_and_check(addr_vec[ADDR_A], 3);
cache_fifo_test.access_and_check(addr_vec[ADDR_A], 0);
cache_fifo_test.access_and_check(addr_vec[ADDR_B], 0);
cache_fifo_test.access_and_check(addr_vec[ADDR_C], 1);
cache_fifo_test.access_and_check(addr_vec[ADDR_D], 2);
}
void
unit_test_cache_fifo_eight_way()
{
cache_policy_test_t<cache_fifo_t> cache_fifo_test(8,
64,
1024);
cache_fifo_test.initialize_cache();
assert(cache_fifo_test.get_replace_policy() == "FIFO");
assert(cache_fifo_test.block_indices_are_identical(addr_vec));
assert(cache_fifo_test.tags_are_different(addr_vec));
cache_fifo_test.access_and_check(addr_vec[ADDR_A], 1);
cache_fifo_test.access_and_check(addr_vec[ADDR_B], 2);
cache_fifo_test.access_and_check(addr_vec[ADDR_C], 3);
cache_fifo_test.access_and_check(addr_vec[ADDR_D], 4);
cache_fifo_test.access_and_check(addr_vec[ADDR_E], 5);
cache_fifo_test.access_and_check(addr_vec[ADDR_F], 6);
cache_fifo_test.access_and_check(addr_vec[ADDR_G], 7);
cache_fifo_test.access_and_check(addr_vec[ADDR_H], 0);
cache_fifo_test.access_and_check(addr_vec[ADDR_E], 0);
cache_fifo_test.access_and_check(addr_vec[ADDR_A], 0);
cache_fifo_test.access_and_check(addr_vec[ADDR_A], 0);
cache_fifo_test.access_and_check(addr_vec[ADDR_I], 1);
cache_fifo_test.access_and_check(addr_vec[ADDR_J], 2);
cache_fifo_test.access_and_check(addr_vec[ADDR_K], 3);
cache_fifo_test.access_and_check(addr_vec[ADDR_L], 4);
cache_fifo_test.access_and_check(addr_vec[ADDR_L], 4);
cache_fifo_test.invalidate_and_check(addr_vec[ADDR_G], 4);
cache_fifo_test.invalidate_and_check(addr_vec[ADDR_K], 4);
cache_fifo_test.access_and_check(addr_vec[ADDR_A], 5);
cache_fifo_test.access_and_check(addr_vec[ADDR_B], 6);
cache_fifo_test.access_and_check(addr_vec[ADDR_C], 7);
cache_fifo_test.access_and_check(addr_vec[ADDR_D], 0);
}
void
unit_test_cache_lfu_four_way()
{
cache_policy_test_t<cache_t> cache_lfu_test(4,
32,
256);
cache_lfu_test.initialize_cache();
assert(cache_lfu_test.get_replace_policy() == "LFU");
assert(cache_lfu_test.block_indices_are_identical(addr_vec));
assert(cache_lfu_test.tags_are_different(addr_vec));
cache_lfu_test.access_and_check(addr_vec[ADDR_A], 1);
cache_lfu_test.access_and_check(addr_vec[ADDR_B], 2);
cache_lfu_test.access_and_check(addr_vec[ADDR_C], 3);
cache_lfu_test.access_and_check(addr_vec[ADDR_D], 0);
cache_lfu_test.access_and_check(addr_vec[ADDR_A], 1);
cache_lfu_test.access_and_check(addr_vec[ADDR_D], 1);
cache_lfu_test.access_and_check(addr_vec[ADDR_D], 1);
cache_lfu_test.access_and_check(addr_vec[ADDR_B], 2);
cache_lfu_test.access_and_check(addr_vec[ADDR_C], 0);
cache_lfu_test.access_and_check(addr_vec[ADDR_B], 0);
cache_lfu_test.access_and_check(addr_vec[ADDR_A], 2);
cache_lfu_test.access_and_check(addr_vec[ADDR_C], 0);
cache_lfu_test.invalidate_and_check(addr_vec[ADDR_C], 2);
cache_lfu_test.access_and_check(addr_vec[ADDR_E], 2);
cache_lfu_test.access_and_check(addr_vec[ADDR_E], 2);
cache_lfu_test.invalidate_and_check(addr_vec[ADDR_B], 1);
cache_lfu_test.access_and_check(addr_vec[ADDR_F], 1);
cache_lfu_test.access_and_check(addr_vec[ADDR_G], 1);
cache_lfu_test.access_and_check(addr_vec[ADDR_G], 1);
cache_lfu_test.access_and_check(addr_vec[ADDR_G], 2);
cache_lfu_test.access_and_check(addr_vec[ADDR_E], 0);
cache_lfu_test.access_and_check(addr_vec[ADDR_B], 0);
}
void
unit_test_cache_lfu_eight_way()
{
cache_policy_test_t<cache_t> cache_lfu_test(8,
64,
1024);
cache_lfu_test.initialize_cache();
assert(cache_lfu_test.get_replace_policy() == "LFU");
assert(cache_lfu_test.block_indices_are_identical(addr_vec));
assert(cache_lfu_test.tags_are_different(addr_vec));
cache_lfu_test.access_and_check(addr_vec[ADDR_A], 1);
cache_lfu_test.access_and_check(addr_vec[ADDR_B], 2);
cache_lfu_test.access_and_check(addr_vec[ADDR_C], 3);
cache_lfu_test.access_and_check(addr_vec[ADDR_D], 4);
cache_lfu_test.access_and_check(addr_vec[ADDR_E], 5);
cache_lfu_test.access_and_check(addr_vec[ADDR_F], 6);
cache_lfu_test.access_and_check(addr_vec[ADDR_G], 7);
cache_lfu_test.access_and_check(addr_vec[ADDR_H], 0);
cache_lfu_test.access_and_check(addr_vec[ADDR_E], 0);
cache_lfu_test.access_and_check(addr_vec[ADDR_D], 0);
cache_lfu_test.access_and_check(addr_vec[ADDR_C], 0);
cache_lfu_test.access_and_check(addr_vec[ADDR_A], 1);
cache_lfu_test.access_and_check(addr_vec[ADDR_B], 5);
cache_lfu_test.invalidate_and_check(addr_vec[ADDR_C], 2);
cache_lfu_test.access_and_check(addr_vec[ADDR_I], 2);
cache_lfu_test.access_and_check(addr_vec[ADDR_I], 5);
cache_lfu_test.invalidate_and_check(addr_vec[ADDR_G], 6);
cache_lfu_test.invalidate_and_check(addr_vec[ADDR_B], 1);
cache_lfu_test.access_and_check(addr_vec[ADDR_J], 6);
cache_lfu_test.access_and_check(addr_vec[ADDR_K], 1);
cache_lfu_test.access_and_check(addr_vec[ADDR_L], 1);
cache_lfu_test.access_and_check(addr_vec[ADDR_L], 5);
cache_lfu_test.access_and_check(addr_vec[ADDR_F], 6);
cache_lfu_test.access_and_check(addr_vec[ADDR_A], 6);
cache_lfu_test.access_and_check(addr_vec[ADDR_L], 6);
cache_lfu_test.access_and_check(addr_vec[ADDR_K], 7);
cache_lfu_test.access_and_check(addr_vec[ADDR_H], 2);
cache_lfu_test.access_and_check(addr_vec[ADDR_D], 2);
cache_lfu_test.access_and_check(addr_vec[ADDR_I], 4);
}
void
unit_test_cache_replacement_policy()
{
unit_test_cache_lru_four_way();
unit_test_cache_lru_eight_way();
unit_test_cache_fifo_four_way();
unit_test_cache_fifo_eight_way();
unit_test_cache_lfu_four_way();
unit_test_cache_lfu_eight_way();
}
}
}