├── tests ├── random_tests.sh ├── unit.cpp └── random.cpp ├── LICENSE ├── Makefile ├── misc.h ├── cache.h ├── prefetch.h ├── prefetch.cpp ├── main.cpp ├── cache.cpp ├── README ├── system.h └── system.cpp /tests/random_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for lines in "1024" "4096"; do 4 | for assoc in "4" "8" "16"; do 5 | for prefetch in "sequential" "adjacent" "none"; do 6 | for comp in "n"; do 7 | for caches in "1" "4"; do 8 | for dist in "normal 120000" "uniform 3200000"; do 9 | file_name=random_${lines}_${assoc}_${prefetch}_${comp}_${caches}_4_90000_${dist// /}.txt 10 | echo $file_name 11 | git show --summary >> $file_name 12 | cat ../Makefile >> $file_name 13 | for i in 1 2 3; do 14 | ./random $lines $assoc $prefetch $comp $caches 4 90000 $dist >> $file_name 15 | done 16 | done 17 | done 18 | done 19 | done 20 | done 21 | done 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2018 Justin Funston 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | DEBUG_FLAGS = -O2 -g -Wall -Wextra -DDEBUG -std=gnu++14 3 | RELEASE_FLAGS= -O3 -march=native -Wall -Wextra -std=gnu++14 -flto -static 4 | CXXFLAGS=$(RELEASE_FLAGS) 5 | DEPS=$(wildcard *.h) Makefile 6 | OBJ=system.o cache.o prefetch.o 7 | BUILD_DIR=$(shell pwd) 8 | 9 | all: cache tags check tests/random tests/unit cscope.out 10 | 11 | cache: main.cpp $(DEPS) $(OBJ) 12 | $(CXX) $(CXXFLAGS) -o cache main.cpp $(OBJ) 13 | 14 | tests/random: tests/random.cpp $(DEPS) $(OBJ) 15 | $(CXX) $(CXXFLAGS) -I$(BUILD_DIR) -o tests/random tests/random.cpp $(OBJ) 16 | 17 | tests/unit: tests/unit.cpp $(DEPS) $(OBJ) 18 | $(CXX) $(DEBUG_FLAGS) -I$(BUILD_DIR) -o tests/unit tests/unit.cpp $(OBJ) 19 | 20 | %.o: %.cpp $(DEPS) 21 | $(CXX) $(CXXFLAGS) -o $@ -c $< 22 | 23 | tags: *.cpp *.h 24 | ctags *.cpp *.h tests/*.cpp 25 | 26 | cscope.out: *.cpp *.h 27 | cscope -Rb 28 | 29 | .PHONY: check 30 | check: 31 | cppcheck --enable=all . 32 | 33 | .PHONY: clean 34 | clean: 35 | rm -f *.o cache tags cscope.out 36 | -------------------------------------------------------------------------------- /misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2018 Justin Funston 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #pragma once 25 | 26 | #include 27 | 28 | #define PAGE_SIZE_4KB 29 | 30 | #ifdef PAGE_SIZE_4KB 31 | constexpr uint64_t pageMask = 0xFFFFFFFFFFFFF000; 32 | constexpr uint32_t pageShift = 12; 33 | #elif defined PAGE_SIZE_2MB 34 | constexpr uint64_t pageMask = 0xFFFFFFFFFFE00000; 35 | constexpr uint32_t pageShift = 21; 36 | #else 37 | #error "Bad PAGE_SIZE" 38 | #endif 39 | 40 | enum class CacheState {Modified, Owned, Exclusive, Shared, Invalid}; 41 | 42 | enum class AccessType {Read, Write, Prefetch}; 43 | 44 | struct CacheLine{ 45 | uint64_t tag{0}; 46 | CacheState state{CacheState::Invalid}; 47 | 48 | CacheLine(uint64_t tag = 0, CacheState state = CacheState::Invalid) : 49 | tag(tag), state(state) {} 50 | bool operator<(const CacheLine& rhs) const 51 | { return tag < rhs.tag; } 52 | bool operator==(const CacheLine& rhs) const 53 | { return tag == rhs.tag; } 54 | }; 55 | -------------------------------------------------------------------------------- /cache.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2018 Justin Funston 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | 29 | #include "misc.h" 30 | 31 | // Represents a single cache 32 | class Cache { 33 | public: 34 | // num_lines is total line capacity, assoc is the number of ways (locations) 35 | // a single line can be placed 36 | Cache(unsigned int num_lines, unsigned int assoc); 37 | // Returns the state of specified line, or Invalid if not found 38 | CacheState findTag(uint64_t set, uint64_t tag) const; 39 | void changeState(uint64_t set, uint64_t tag, CacheState state); 40 | // Line must exist in the cache 41 | void updateLRU(uint64_t set, uint64_t tag); 42 | // Returns true if writeback necessary, and the tag of the line if true 43 | bool checkWriteback(uint64_t set, uint64_t& tag) const; 44 | // Line should not already exist in cache. Will remove the LRU line in set 45 | // if there is not enough space, so checkWriteback should be called before this 46 | void insertLine(uint64_t set, uint64_t tag, CacheState state); 47 | private: 48 | std::vector> sets; 49 | unsigned int maxSetSize; 50 | }; 51 | 52 | -------------------------------------------------------------------------------- /prefetch.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2018 Justin Funston 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #pragma once 25 | 26 | #include 27 | 28 | class System; 29 | 30 | class Prefetch { 31 | public: 32 | virtual int prefetchMiss(uint64_t address, unsigned int tid, 33 | System& sys) = 0; 34 | virtual int prefetchHit(uint64_t address, unsigned int tid, 35 | System& sys) = 0; 36 | }; 37 | 38 | // Modeling AMD's L1 prefetcher, a sequential 39 | // line prefetcher. Primary difference is that 40 | // the real prefetcher has a dynamic prefetch width. 41 | class SeqPrefetch : public Prefetch { 42 | public: 43 | int prefetchMiss(uint64_t address, unsigned int tid, System& sys) override; 44 | int prefetchHit(uint64_t address, unsigned int tid, System& sys) override; 45 | private: 46 | uint64_t lastMiss{0}; 47 | uint64_t lastPrefetch{0}; 48 | static constexpr unsigned int prefetchNum = 3; 49 | }; 50 | 51 | // A simple adjacent line prefetcher. Always fetches the next line 52 | class AdjPrefetch : public Prefetch { 53 | public: 54 | int prefetchMiss(uint64_t address, unsigned int tid, System& sys) override; 55 | int prefetchHit(uint64_t address, unsigned int tid, System& sys) override; 56 | }; 57 | -------------------------------------------------------------------------------- /prefetch.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2018 Justin Funston 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include "prefetch.h" 25 | #include "system.h" 26 | 27 | int AdjPrefetch::prefetchMiss(uint64_t address, unsigned int tid, System& sys) 28 | { 29 | sys.memAccess(address + (1 << sys.setShift), AccessType::Prefetch, tid); 30 | return 1; 31 | } 32 | 33 | // Called to check for prefetches in the case of a cache miss. 34 | int SeqPrefetch::prefetchMiss(uint64_t address, unsigned int tid, System& sys) 35 | { 36 | uint64_t set = (address & sys.setMask) >> sys.setShift; 37 | uint64_t tag = address & sys.tagMask; 38 | uint64_t lastSet = (lastMiss & sys.setMask) >> sys.setShift; 39 | uint64_t lastTag = lastMiss & sys.tagMask; 40 | int prefetched = 0; 41 | 42 | if(tag == lastTag && (lastSet+1) == set) { 43 | for(uint64_t i=0; i < prefetchNum; i++) { 44 | prefetched++; 45 | // Call memAccess to resolve the prefetch. The address is 46 | // incremented in the set portion of its bits (least 47 | // significant bits not in the cache line offset portion) 48 | sys.memAccess(address + ((1 << sys.setShift) * (i+1)), 49 | AccessType::Prefetch, tid); 50 | } 51 | 52 | lastPrefetch = address + (1 << sys.setShift); 53 | } 54 | 55 | lastMiss = address; 56 | return prefetched; 57 | } 58 | 59 | int AdjPrefetch::prefetchHit(uint64_t address, unsigned int tid, System& sys) 60 | { 61 | sys.memAccess(address + (1 << sys.setShift), AccessType::Prefetch, tid); 62 | return 1; 63 | } 64 | 65 | // Called to check for prefetches in the case of a cache hit. 66 | int SeqPrefetch::prefetchHit(uint64_t address, unsigned int tid, System& sys) 67 | { 68 | uint64_t set = (address & sys.setShift) >> sys.setShift; 69 | uint64_t tag = address & sys.tagMask; 70 | uint64_t lastSet = (lastPrefetch & sys.setMask) 71 | >> sys.setShift; 72 | uint64_t lastTag = lastPrefetch & sys.tagMask; 73 | 74 | if(tag == lastTag && lastSet == set) { 75 | // Call memAccess to resolve the prefetch. The address is 76 | // incremented in the set portion of its bits (least 77 | // significant bits not in the cache line offset portion) 78 | sys.memAccess(address + ((1 << sys.setShift) * prefetchNum), 79 | AccessType::Prefetch, tid); 80 | lastPrefetch = lastPrefetch + (1 << sys.setShift); 81 | } 82 | 83 | return 1; 84 | } 85 | 86 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2018 Justin Funston 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "system.h" 31 | 32 | using namespace std; 33 | 34 | int main() 35 | { 36 | // tid_map is used to inform the simulator how 37 | // thread ids map to NUMA/cache domains. Using 38 | // the tid as an index gives the NUMA domain. 39 | unsigned int arr_map[] = {0, 1}; 40 | vector tid_map(arr_map, arr_map + 41 | sizeof(arr_map) / sizeof(unsigned int)); 42 | std::unique_ptr prefetch = std::make_unique(); 43 | // The constructor parameters are: 44 | // the tid_map, the cache line size in bytes, 45 | // number of cache lines, the associativity, 46 | // the prefetcher object, 47 | // whether to count compulsory misses, 48 | // whether to do virtual to physical translation, 49 | // and number of caches/domains 50 | // WARNING: counting compulsory misses doubles execution time 51 | MultiCacheSystem sys(tid_map, 64, 1024, 64, std::move(prefetch), false, false, 2); 52 | char rw; 53 | uint64_t address; 54 | unsigned long long lines = 0; 55 | ifstream infile; 56 | // This code works with the output from the 57 | // ManualExamples/pinatrace pin tool 58 | infile.open("pinatrace.out", ifstream::in); 59 | assert(infile.is_open()); 60 | 61 | while(!infile.eof()) 62 | { 63 | infile.ignore(256, ':'); 64 | 65 | infile >> rw; 66 | assert(rw == 'R' || rw == 'W'); 67 | AccessType accessType; 68 | if (rw == 'R') { 69 | accessType = AccessType::Read; 70 | } else { 71 | accessType = AccessType::Write; 72 | } 73 | 74 | infile >> hex >> address; 75 | if(address != 0) { 76 | // By default the pinatrace tool doesn't record the tid, 77 | // so we make up a tid to stress the MultiCache functionality 78 | sys.memAccess(address, accessType, lines%2); 79 | } 80 | 81 | ++lines; 82 | } 83 | 84 | cout << "Accesses: " << lines << endl; 85 | cout << "Hits: " << sys.stats.hits << endl; 86 | cout << "Misses: " << lines - sys.stats.hits << endl; 87 | cout << "Local reads: " << sys.stats.local_reads << endl; 88 | cout << "Local writes: " << sys.stats.local_writes << endl; 89 | cout << "Remote reads: " << sys.stats.remote_reads << endl; 90 | cout << "Remote writes: " << sys.stats.remote_writes << endl; 91 | cout << "Other-cache reads: " << sys.stats.othercache_reads << endl; 92 | //cout << "Compulsory Misses: " << sys.stats.compulsory << endl; 93 | 94 | infile.close(); 95 | 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /cache.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2018 Justin Funston 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | #include "misc.h" 28 | #include "cache.h" 29 | 30 | Cache::Cache(unsigned int num_lines, unsigned int assoc) : maxSetSize(assoc) 31 | { 32 | assert(num_lines % assoc == 0); 33 | // The set bits of the address will be used as an index 34 | // into sets. Each set is a deque containing "assoc" items 35 | sets.resize(num_lines / assoc); 36 | } 37 | 38 | // Given the set and tag, return the cache line's state 39 | // Invalid and "not found" are equivalent 40 | CacheState Cache::findTag(uint64_t set, uint64_t tag) const 41 | { 42 | for (auto it = sets[set].cbegin(); it != sets[set].cend(); ++it) { 43 | if (it->tag == tag) { 44 | return it->state; 45 | } 46 | } 47 | 48 | return CacheState::Invalid; 49 | } 50 | 51 | // Changes the cache line specificed by "set" and "tag" to "state" 52 | // The cache only saves lines that are not Invalid, so delete if that 53 | // is the new state 54 | void Cache::changeState(uint64_t set, uint64_t tag, CacheState state) 55 | { 56 | for (auto it = sets[set].begin(); it != sets[set].end(); ++it) { 57 | if (it->tag == tag) { 58 | if (state == CacheState::Invalid) { 59 | sets[set].erase(it); 60 | break; 61 | } else { 62 | it->state = state; 63 | break; 64 | } 65 | } 66 | } 67 | } 68 | 69 | // A complete LRU is mantained for each set, using the ordering 70 | // of the set deque. The end is considered most 71 | // recently used. The specified line must be in the cache, it 72 | // will be moved to the most-recently used position 73 | void Cache::updateLRU(uint64_t set, uint64_t tag) 74 | { 75 | #ifdef DEBUG 76 | CacheState foundState = findTag(set, tag); 77 | assert(foundState != CacheState::Invalid); 78 | #endif 79 | 80 | CacheLine temp; 81 | auto it = sets[set].begin(); 82 | for(; it != sets[set].end(); ++it) { 83 | if(it->tag == tag) { 84 | temp = *it; 85 | break; 86 | } 87 | } 88 | 89 | sets[set].erase(it); 90 | sets[set].push_back(temp); 91 | } 92 | 93 | // Called if a new cache line is to be inserted. Checks if 94 | // the least recently used line needs to be written back to 95 | // main memory. 96 | bool Cache::checkWriteback(uint64_t set, uint64_t& tag) const 97 | { 98 | if (sets[set].size() < maxSetSize) { 99 | // There is room in the set, it does not matter the state of the 100 | // LRU line 101 | return false; 102 | } 103 | 104 | const CacheLine& evict = sets[set].front(); 105 | tag = evict.tag; 106 | return (evict.state == CacheState::Modified || evict.state == CacheState::Owned); 107 | } 108 | 109 | // Insert a new cache line by popping the least recently used line if necessary 110 | // and pushing the new line to the back (most recently used) 111 | void Cache::insertLine(uint64_t set, uint64_t tag, CacheState state) 112 | { 113 | if (sets[set].size() == maxSetSize) { 114 | sets[set].pop_front(); 115 | } 116 | 117 | sets[set].emplace_back(tag, state); 118 | } 119 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Author: Justin Funston 2 | Modified: July 2018 3 | Email: justinfunston@gmail.com 4 | 5 | MultiCacheSim is a cache simulator designed to be used with 6 | memory access traces obtained with Pin: 7 | https://software.intel.com/en-us/articles/pintool. It 8 | has been optimized for performance and can process large inputs 9 | (100s of GB) in a few hours depending on configuration. 10 | 11 | FEATURES 12 | -------- 13 | 14 | * Configurable size, associativity, and line size 15 | * MOESI protocol simulation for multiple caches 16 | * Tracking of miss and data source statistics 17 | * NUMA statistics are maintained based off of a fist-touch policy 18 | and configurable page size 19 | * Virtual-to-physical address translation 20 | * Prefetcher "plugins" 21 | - Adjacent line prefetcher 22 | - Sequential prefetcher (similar to AMD's L1 prefetcher) 23 | * LRU replacement policy 24 | 25 | COMPILATION 26 | ----------- 27 | 28 | Simply run "make" to compile the simulator with the example driver 29 | program (main.cpp). If you are modifying the simulator, use the 30 | DEBUG flags in the Makefile which enables run-time error checking. 31 | 32 | Compilation requires a compiler supporting c++11 33 | 34 | USAGE 35 | ----- 36 | 37 | See main.cpp for an example of using the simulator. The basic 38 | steps are: 39 | 1. Create a vector mapping thread IDs to NUMA domains, 40 | where the index into the vector represents the TID 41 | 2. Create a SeqPrefetch (sequential prefetching similar to AMD L1 42 | prefetcher) or AdjPrefetch (adjacent line prefetcher) if 43 | wanted. 44 | 3. Create a SingleCacheSystem or MultiCacheSystem object using the 45 | constructor, which takes: 46 | the vector from step 1, the size of a cache line in bytes, 47 | the number of cache lines, the associativity, 48 | whether to count compulsory misses, whether to translate addresses, 49 | and the number of caches/NUMA domains (for the MultiCacheSystem). 50 | 4. Call System::memAccess for each memory access, in order, 51 | passing the address, read or write (as an 'R' or 'W' 52 | character), and the TID of the accessing thread. 53 | 5. Read the statistics from the System object 54 | 55 | The assumed page size can be changed in misc.h, and the prefetch width 56 | for the SeqPrefetch class can be changed in prefetch.h (default 3 lines). 57 | 58 | The driver example in main.cpp works with the output from the 59 | ManualExamples/pinatrace pin tool. 60 | 61 | MULTI-PROCESS WORKLOADS 62 | ----------------------- 63 | 64 | To use the simulator with a multi-process workload, only the driver 65 | program needs to be modified. 66 | 67 | First, give each of your processes a unique ID starting with 0 and 68 | increasing by 1 (mimicking TIDs), and use it as the TID in the steps 69 | above. 70 | 71 | Since the same virtual address in different processes actually refers 72 | to different physical addresses, we need a way to differentiate them 73 | in the simulator. To do this we take advantage of the fact that only 74 | the least significant 48 bits of an address are used in practice: 75 | address |= TID << (7*8); 76 | This places the TID in first byte of the address, ensuring that 77 | addresses are unique among processes. 78 | 79 | If the workload uses a shared memory region, find the range for the 80 | region. If an address lies within the range, do not modify the 81 | address as described above, thus making all addresses in the shared 82 | range actually shared among processes. 83 | 84 | ATTRIBUTION 85 | ----------- 86 | 87 | Catch2 (tests/catch.hpp) is used for unit tests, from 88 | https://github.com/catchorg/Catch2. It is (c) 2018 Two Blue Cubes Ltd. 89 | and distributed under the Boost Software License 90 | 91 | LICENSE 92 | ------- 93 | 94 | Copyright (c) 2015-2018 Justin Funston 95 | 96 | This software is provided 'as-is', without any express or implied 97 | warranty. In no event will the authors be held liable for any damages 98 | arising from the use of this software. 99 | 100 | Permission is granted to anyone to use this software for any purpose, 101 | including commercial applications, and to alter it and redistribute it 102 | freely, subject to the following restrictions: 103 | 104 | 1. The origin of this software must not be misrepresented; you must not 105 | claim that you wrote the original software. If you use this software 106 | in a product, an acknowledgment in the product documentation would be 107 | appreciated but is not required. 108 | 109 | 2. Altered source versions must be plainly marked as such, and must not be 110 | misrepresented as being the original software. 111 | 112 | 3. This notice may not be removed or altered from any source 113 | distribution. 114 | 115 | -------------------------------------------------------------------------------- /system.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2018 Justin Funston 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "misc.h" 33 | #include "cache.h" 34 | #include "prefetch.h" 35 | 36 | // All stats exclude prefetcher activity (except prefetched) 37 | struct SystemStats { 38 | uint64_t accesses{0}; // Number of user reads and writes 39 | uint64_t hits{0}; // Cache hits. Misses = accesses - hits 40 | uint64_t local_reads{0}; // Local node RAM reads. Note that write misses can cause RAM reads 41 | uint64_t remote_reads{0}; // Remote node RAM reads 42 | uint64_t othercache_reads{0}; // Read from remote node cache 43 | uint64_t local_writes{0}; 44 | uint64_t remote_writes{0}; 45 | uint64_t compulsory{0}; // Compulsory misses, i.e. the first access to an address 46 | uint64_t prefetched{0}; 47 | }; 48 | 49 | class System { 50 | protected: 51 | friend class Prefetch; 52 | friend class AdjPrefetch; 53 | friend class SeqPrefetch; 54 | std::unique_ptr prefetcher; 55 | uint64_t setMask; 56 | uint64_t tagMask; 57 | uint64_t lineMask; 58 | uint32_t setShift; 59 | 60 | // Used for compulsory misses 61 | std::unordered_set seenLines; 62 | // Stores virtual to physical page mappings 63 | std::unordered_map virtToPhysMap; 64 | // Used for determining new virtual to physical mappings 65 | uint64_t nextPage{0}; 66 | bool countCompulsory; 67 | bool doAddrTrans; 68 | 69 | uint64_t virtToPhys(uint64_t address); 70 | void checkCompulsory(uint64_t line); 71 | public: 72 | virtual ~System() = default; 73 | System(unsigned int line_size, unsigned int num_lines, unsigned int assoc, 74 | std::unique_ptr prefetcher, bool count_compulsory=false, 75 | bool do_addr_trans=false); 76 | virtual void memAccess(uint64_t address, AccessType type, unsigned int tid) = 0; 77 | SystemStats stats; 78 | }; 79 | 80 | //For a system containing multiple caches 81 | class MultiCacheSystem : public System { 82 | private: 83 | // Stores NUMA domain location of pages 84 | std::unordered_map pageToDomain; 85 | std::vector> caches; 86 | std::vector& tidToDomain; 87 | 88 | unsigned int checkRemoteStates(uint64_t set, uint64_t tag, 89 | CacheState& state, unsigned int local); 90 | void updatePageToDomain(uint64_t address, unsigned int curDomain); 91 | void setRemoteStates(uint64_t set, uint64_t tag, 92 | CacheState state, unsigned int local); 93 | void evictTraffic(uint64_t set, uint64_t tag, 94 | unsigned int local); 95 | bool isLocal(uint64_t address, unsigned int local); 96 | CacheState processMOESI(uint64_t set, uint64_t tag, 97 | CacheState remote_state, AccessType accessType, 98 | bool local_traffic, unsigned int local, unsigned int remote); 99 | public: 100 | MultiCacheSystem(std::vector& tid_to_domain, 101 | unsigned int line_size, unsigned int num_lines, unsigned int assoc, 102 | std::unique_ptr prefetcher, bool count_compulsory=false, 103 | bool do_addr_trans=false, unsigned int num_domains=1); 104 | 105 | void memAccess(uint64_t address, AccessType type, unsigned int tid) override; 106 | }; 107 | 108 | // For a system containing a sinle cache 109 | // performs about 10% better than the MultiCache implementation 110 | class SingleCacheSystem : public System { 111 | public: 112 | SingleCacheSystem(unsigned int line_size, unsigned int num_lines, unsigned int assoc, 113 | std::unique_ptr prefetcher, bool count_compulsory=false, 114 | bool do_addr_trans=false); 115 | 116 | void memAccess(uint64_t address, AccessType type, unsigned int tid) override; 117 | private: 118 | std::unique_ptr cache; 119 | }; 120 | -------------------------------------------------------------------------------- /tests/unit.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2018 Justin Funston 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include 25 | 26 | #include "system.h" 27 | 28 | #define CATCH_CONFIG_MAIN 29 | #include "tests/catch.hpp" 30 | 31 | TEST_CASE("Single cache tests", "[cache]") { 32 | unsigned int cache_line_size = 64; 33 | unsigned int cache_lines = 128; 34 | unsigned int way_count = 4; 35 | bool compulsory = false; 36 | 37 | // Address layout we expect: 38 | // Offset mask: 0x000000000000003F 39 | // Set mask: 0x00000000000007C0 40 | 41 | 42 | std::unique_ptr sys = std::make_unique(cache_line_size, 43 | cache_lines, way_count, 44 | nullptr, //no prefetch 45 | compulsory); 46 | 47 | SECTION("Simple miss-hit sequence") { 48 | REQUIRE(sys->stats.accesses == 0); 49 | REQUIRE(sys->stats.hits == 0); 50 | REQUIRE(sys->stats.local_reads == 0); 51 | REQUIRE(sys->stats.local_writes == 0); 52 | 53 | sys->memAccess(0x0000000000000000ULL, AccessType::Write, 0); 54 | REQUIRE(sys->stats.accesses == 1); 55 | REQUIRE(sys->stats.hits == 0); 56 | REQUIRE(sys->stats.local_reads == 1); 57 | 58 | sys->memAccess(0x0000000000000000ULL, AccessType::Read, 0); 59 | REQUIRE(sys->stats.accesses == 2); 60 | REQUIRE(sys->stats.hits == 1); 61 | REQUIRE(sys->stats.local_reads == 1); 62 | 63 | SECTION("Set fill") { 64 | sys->memAccess(0x0001000000000000ULL, AccessType::Write, 0); 65 | sys->memAccess(0x0002000000000000ULL, AccessType::Write, 0); 66 | sys->memAccess(0x0003000000000000ULL, AccessType::Write, 0); 67 | 68 | REQUIRE(sys->stats.accesses == 5); 69 | REQUIRE(sys->stats.hits == 1); 70 | REQUIRE(sys->stats.local_reads == 4); 71 | 72 | SECTION("Other sets fill") { 73 | uint64_t tag = 0x0001000000000000ULL; 74 | // 128 / 4 = 32 total sets 75 | for (uint64_t i = 1; i < 32; ++i) { 76 | uint64_t addr = tag | (i << 6); 77 | sys->memAccess(addr, AccessType::Write, 0); 78 | } 79 | 80 | REQUIRE(sys->stats.accesses == 36); 81 | REQUIRE(sys->stats.hits == 1); 82 | REQUIRE(sys->stats.local_reads == 35); 83 | 84 | // Original set should be unaffect and these should be hits 85 | sys->memAccess(0x0000000000000000ULL, AccessType::Read, 0); 86 | sys->memAccess(0x0001000000000000ULL, AccessType::Read, 0); 87 | sys->memAccess(0x0002000000000000ULL, AccessType::Read, 0); 88 | sys->memAccess(0x0003000000000000ULL, AccessType::Read, 0); 89 | REQUIRE(sys->stats.hits == 5); 90 | } 91 | 92 | SECTION("Set hits") { 93 | sys->memAccess(0x0001000000000000ULL, AccessType::Read, 0); 94 | sys->memAccess(0x0002000000000000ULL, AccessType::Read, 0); 95 | sys->memAccess(0x0003000000000000ULL, AccessType::Read, 0); 96 | 97 | REQUIRE(sys->stats.accesses == 8); 98 | REQUIRE(sys->stats.hits == 4); 99 | REQUIRE(sys->stats.local_reads == 4); 100 | } 101 | 102 | SECTION("Evict") { 103 | sys->memAccess(0x0004000000000000ULL, AccessType::Write, 0); 104 | REQUIRE(sys->stats.local_reads == 5); 105 | REQUIRE(sys->stats.hits == 1); 106 | 107 | sys->memAccess(0x0000000000000000ULL, AccessType::Read, 0); 108 | REQUIRE(sys->stats.local_reads == 6); 109 | REQUIRE(sys->stats.hits == 1); 110 | } 111 | 112 | SECTION("Evict LRU") { 113 | sys->memAccess(0x0000000000000000ULL, AccessType::Read, 0); 114 | REQUIRE(sys->stats.local_reads == 4); 115 | REQUIRE(sys->stats.hits == 2); 116 | 117 | sys->memAccess(0x0004000000000000ULL, AccessType::Write, 0); 118 | REQUIRE(sys->stats.local_reads == 5); 119 | REQUIRE(sys->stats.hits == 2); 120 | 121 | sys->memAccess(0x0000000000000000ULL, AccessType::Read, 0); 122 | REQUIRE(sys->stats.local_reads == 5); 123 | REQUIRE(sys->stats.hits == 3); 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /tests/random.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2018 Justin Funston 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "system.h" 30 | 31 | using namespace std; 32 | 33 | void usage() { 34 | cout << "Usage: ./random <# cache lines> " 35 | << "" 36 | << " <# caches/domains> <# threads>" 37 | << " <# iterations> " << endl; 38 | } 39 | 40 | struct AccessData { 41 | uint64_t address; 42 | unsigned int tid; 43 | AccessType accessType; 44 | }; 45 | 46 | int main(int argc, char* argv[]) { 47 | if (argc != 10) { 48 | usage(); 49 | return -1; 50 | } 51 | 52 | unsigned int cache_line_size = 64; 53 | unsigned int cache_lines = stoi(argv[1]); 54 | unsigned int way_count = stoi(argv[2]); 55 | string prefetcher_choice(argv[3]); 56 | string compulsory_choice(argv[4]); 57 | unsigned int num_caches = stoi(argv[5]); 58 | unsigned int num_threads = stoi(argv[6]); 59 | unsigned int iterations = stoi(argv[7]); 60 | string distribution_choice(argv[8]); 61 | uint64_t range = stoull(argv[9]); 62 | 63 | // tid_map is used to inform the simulator how 64 | // thread ids map to NUMA/cache domains. Using 65 | // the tid as an index gives the NUMA domain. 66 | vector tid_map(num_threads); 67 | for (unsigned int i=0; i prefetch; 87 | if (prefetcher_choice == "none") { 88 | prefetch = nullptr; 89 | } else if (prefetcher_choice == "adjacent") { 90 | prefetch = std::make_unique(); 91 | } else if (prefetcher_choice == "sequential") { 92 | prefetch = std::make_unique(); 93 | } else { 94 | usage(); 95 | return -1; 96 | } 97 | 98 | unique_ptr sys; 99 | if (num_caches == 1) { 100 | sys = make_unique(cache_line_size, cache_lines, way_count, 101 | std::move(prefetch), 102 | compulsory, false); 103 | } else { 104 | sys = make_unique(tid_map, cache_line_size, cache_lines, way_count, 105 | std::move(prefetch), 106 | compulsory, false, num_caches); 107 | } 108 | 109 | array access_buffer; 110 | default_random_engine engine(0); 111 | uniform_int_distribution tid_generator(0, num_threads); 112 | uniform_int_distribution rw_generator(0, 1); 113 | uniform_int_distribution addr_uniform(0, range); 114 | normal_distribution addr_normal(1000000000.0, (double)range); 115 | 116 | chrono::duration run_time; 117 | 118 | for (unsigned int i=0; imemAccess(access_buffer[j].address, access_buffer[j].accessType, access_buffer[j].tid); 135 | } 136 | auto end = chrono::high_resolution_clock::now(); 137 | run_time += chrono::duration_cast>(end - start); 138 | } 139 | 140 | uint64_t accesses = 2000LLU*iterations; 141 | cout << "Execution time: " << run_time.count() << endl; 142 | cout << "Accesses: " << accesses << endl; 143 | cout << "Hits: " << sys->stats.hits << endl; 144 | cout << "Misses: " << accesses - sys->stats.hits << endl; 145 | cout << "Local reads: " << sys->stats.local_reads << endl; 146 | cout << "Local writes: " << sys->stats.local_writes << endl; 147 | cout << "Remote reads: " << sys->stats.remote_reads << endl; 148 | cout << "Remote writes: " << sys->stats.remote_writes << endl; 149 | cout << "Other-cache reads: " << sys->stats.othercache_reads << endl; 150 | cout << "Compulsory Misses: " << sys->stats.compulsory << endl; 151 | cout << "Prefetched: " << sys->stats.prefetched << endl; 152 | 153 | return 0; 154 | } 155 | -------------------------------------------------------------------------------- /system.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2018 Justin Funston 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "misc.h" 29 | #include "cache.h" 30 | #include "system.h" 31 | 32 | System::System( 33 | unsigned int line_size, unsigned int num_lines, unsigned int assoc, 34 | std::unique_ptr prefetcher, 35 | bool count_compulsory /*=false*/, 36 | bool do_addr_trans /*=false*/) : 37 | prefetcher(std::move(prefetcher)), 38 | countCompulsory(count_compulsory), 39 | doAddrTrans(do_addr_trans) 40 | { 41 | assert(num_lines % assoc == 0); 42 | 43 | lineMask = ((uint64_t) line_size)-1; 44 | setShift = log2(line_size); 45 | setMask = ((num_lines / assoc) - 1) << setShift; 46 | tagMask = ~(setMask | lineMask); 47 | } 48 | 49 | void System::checkCompulsory(uint64_t line) 50 | { 51 | if(!seenLines.count(line)) { 52 | stats.compulsory++; 53 | seenLines.insert(line); 54 | } 55 | } 56 | 57 | uint64_t System::virtToPhys(uint64_t address) 58 | { 59 | uint64_t phys_addr = address & (~pageMask); 60 | uint64_t virt_page = address & pageMask; 61 | auto it = virtToPhysMap.find(virt_page); 62 | 63 | if(it != virtToPhysMap.end()) { 64 | uint64_t phys_page = it->second; 65 | phys_addr |= phys_page; 66 | } 67 | else { 68 | uint64_t phys_page = nextPage << pageShift; 69 | phys_addr |= phys_page; 70 | virtToPhysMap.insert(std::make_pair(virt_page, phys_page)); 71 | ++nextPage; 72 | } 73 | 74 | return phys_addr; 75 | } 76 | 77 | unsigned int MultiCacheSystem::checkRemoteStates(uint64_t set, 78 | uint64_t tag, CacheState& state, unsigned int local) 79 | { 80 | CacheState curState = CacheState::Invalid; 81 | state = CacheState::Invalid; 82 | unsigned int remote = 0; 83 | 84 | for(unsigned int i=0; ifindTag(set, tag); 90 | switch (curState) 91 | { 92 | case CacheState::Owned: 93 | state = CacheState::Owned; 94 | return i; 95 | break; 96 | case CacheState::Shared: 97 | // A cache line in a shared state may be 98 | // in the owned state in a different cache 99 | // so don't return i immdiately 100 | state = CacheState::Shared; 101 | remote = i; 102 | break; 103 | case CacheState::Exclusive: 104 | state = CacheState::Exclusive; 105 | return i; 106 | break; 107 | case CacheState::Modified: 108 | state = CacheState::Modified; 109 | return i; 110 | break; 111 | default: 112 | break; 113 | } 114 | } 115 | 116 | return remote; 117 | } 118 | 119 | void MultiCacheSystem::setRemoteStates(uint64_t set, 120 | uint64_t tag, CacheState state, unsigned int local) 121 | { 122 | for(unsigned int i=0; i < caches.size(); ++i) { 123 | if(i != local) { 124 | caches[i]->changeState(set, tag, state); 125 | } 126 | } 127 | } 128 | 129 | // Maintains the statistics for memory write-backs 130 | void MultiCacheSystem::evictTraffic(uint64_t set, 131 | uint64_t tag, unsigned int local) 132 | { 133 | uint64_t page = ((set << setShift) | tag) & pageMask; 134 | 135 | #ifdef DEBUG 136 | const auto it = pageList.find(page); 137 | assert(it != pageList.end()); 138 | #endif 139 | 140 | unsigned int domain = pageToDomain[page]; 141 | if(domain == local) { 142 | stats.local_writes++; 143 | } else { 144 | stats.remote_writes++; 145 | } 146 | } 147 | 148 | bool MultiCacheSystem::isLocal(uint64_t address, unsigned int local) 149 | { 150 | uint64_t page = address & pageMask; 151 | 152 | #ifdef DEBUG 153 | const auto it = pageList.find(page); 154 | assert(it != pageList.end()); 155 | #endif 156 | 157 | unsigned int domain = pageToDomain[page]; 158 | return (domain == local); 159 | } 160 | 161 | 162 | CacheState MultiCacheSystem::processMOESI(uint64_t set, 163 | uint64_t tag, CacheState remote_state, AccessType accessType, 164 | bool local_traffic, unsigned int local, 165 | unsigned int remote) 166 | { 167 | CacheState new_state = CacheState::Invalid; 168 | bool is_prefetch = (accessType == AccessType::Prefetch); 169 | 170 | if (remote_state == CacheState::Invalid && accessType == AccessType::Read) { 171 | new_state = CacheState::Exclusive; 172 | 173 | if (local_traffic && !is_prefetch) { 174 | stats.local_reads++; 175 | } else if (!is_prefetch) { 176 | stats.remote_reads++; 177 | } 178 | } 179 | else if (remote_state == CacheState::Invalid && accessType == AccessType::Write) { 180 | new_state = CacheState::Modified; 181 | 182 | if (local_traffic && !is_prefetch) { 183 | stats.local_reads++; 184 | } else if (!is_prefetch) { 185 | stats.remote_reads++; 186 | } 187 | } 188 | else if (remote_state == CacheState::Shared && accessType == AccessType::Read) { 189 | new_state = CacheState::Shared; 190 | 191 | if (local_traffic && !is_prefetch) { 192 | stats.local_reads++; 193 | } else if (!is_prefetch) { 194 | stats.remote_reads++; 195 | } 196 | } 197 | else if (remote_state == CacheState::Shared && accessType == AccessType::Write) { 198 | new_state = CacheState::Modified; 199 | setRemoteStates(set, tag, CacheState::Invalid, local); 200 | 201 | if (!is_prefetch) { 202 | stats.othercache_reads++; 203 | } 204 | } 205 | else if ((remote_state == CacheState::Modified || 206 | remote_state == CacheState::Owned) && accessType == AccessType::Read) { 207 | new_state = CacheState::Shared; 208 | caches[remote]->changeState(set, tag, CacheState::Owned); 209 | 210 | if (!is_prefetch) { 211 | stats.othercache_reads++; 212 | } 213 | } 214 | else if ((remote_state == CacheState::Modified || 215 | remote_state == CacheState::Owned || 216 | remote_state == CacheState::Exclusive) 217 | && accessType == AccessType::Write) { 218 | new_state = CacheState::Modified; 219 | setRemoteStates(set, tag, CacheState::Invalid, local); 220 | 221 | if (!is_prefetch) { 222 | stats.othercache_reads++; 223 | } 224 | } 225 | else if (remote_state == CacheState::Exclusive && accessType == AccessType::Read) { 226 | new_state = CacheState::Shared; 227 | caches[remote]->changeState(set, tag, CacheState::Shared); 228 | 229 | if (!is_prefetch) { 230 | stats.othercache_reads++; 231 | } 232 | } 233 | 234 | #ifdef DEBUG 235 | assert(new_state != CacheState::Invalid); 236 | #endif 237 | 238 | return new_state; 239 | } 240 | 241 | void MultiCacheSystem::memAccess(uint64_t address, AccessType accessType, 242 | unsigned int tid) 243 | { 244 | if (doAddrTrans) { 245 | address = virtToPhys(address); 246 | } 247 | 248 | if (accessType != AccessType::Prefetch) { 249 | stats.accesses++; 250 | } 251 | 252 | unsigned int local = tidToDomain[tid]; 253 | updatePageToDomain(address, local); 254 | 255 | uint64_t set = (address & setMask) >> setShift; 256 | uint64_t tag = address & tagMask; 257 | CacheState state = caches[local]->findTag(set, tag); 258 | bool hit = (state != CacheState::Invalid); 259 | 260 | if (countCompulsory && accessType != AccessType::Prefetch) { 261 | checkCompulsory(address & (~lineMask)); 262 | } 263 | 264 | // Handle hits 265 | if (accessType == AccessType::Write && hit) { 266 | caches[local]->changeState(set, tag, CacheState::Modified); 267 | setRemoteStates(set, tag, CacheState::Invalid, local); 268 | } 269 | 270 | if (hit) { 271 | caches[local]->updateLRU(set, tag); 272 | 273 | if (accessType != AccessType::Prefetch) { 274 | stats.hits++; 275 | if (prefetcher) { 276 | stats.prefetched += prefetcher->prefetchHit(address, tid, *this); 277 | } 278 | } 279 | } 280 | else { 281 | // Now handle miss cases 282 | 283 | CacheState remote_state; 284 | unsigned int remote = checkRemoteStates(set, tag, remote_state, local); 285 | 286 | uint64_t evicted_tag; 287 | bool writeback = caches[local]->checkWriteback(set, evicted_tag); 288 | // TODO both evictTraffic and isLocal search the the pageToDomain map 289 | if (writeback) { 290 | evictTraffic(set, evicted_tag, local); 291 | } 292 | 293 | bool local_traffic = isLocal(address, local); 294 | CacheState new_state = processMOESI(set, tag, remote_state, accessType, 295 | local_traffic, local, remote); 296 | caches[local]->insertLine(set, tag, new_state); 297 | 298 | if (accessType == AccessType::Prefetch && prefetcher) { 299 | stats.prefetched += prefetcher->prefetchMiss(address, tid, *this); 300 | } 301 | } 302 | } 303 | 304 | // Keeps track of which NUMA domain each memory page is in, 305 | // using a first-touch policy 306 | void MultiCacheSystem::updatePageToDomain(uint64_t address, 307 | unsigned int curDomain) 308 | { 309 | uint64_t page = address & pageMask; 310 | 311 | const auto it = pageToDomain.find(page); 312 | if(it == pageToDomain.end()) { 313 | pageToDomain[page] = curDomain; 314 | } 315 | } 316 | 317 | MultiCacheSystem::MultiCacheSystem(std::vector& tid_to_domain, 318 | unsigned int line_size, unsigned int num_lines, unsigned int assoc, 319 | std::unique_ptr prefetcher, bool count_compulsory /*=false*/, 320 | bool do_addr_trans /*=false*/, unsigned int num_domains) : 321 | System(line_size, num_lines, assoc, std::move(prefetcher), 322 | count_compulsory, do_addr_trans), 323 | tidToDomain(tid_to_domain) 324 | { 325 | caches.reserve(num_domains); 326 | 327 | for (unsigned int i=0; i(num_lines, assoc)); 329 | } 330 | } 331 | 332 | void SingleCacheSystem::memAccess(uint64_t address, AccessType accessType, unsigned int tid) 333 | { 334 | bool is_prefetch = (accessType == AccessType::Prefetch); 335 | 336 | if (doAddrTrans) { 337 | address = virtToPhys(address); 338 | } 339 | 340 | if (!is_prefetch) { 341 | stats.accesses++; 342 | } 343 | 344 | uint64_t set = (address & setMask) >> setShift; 345 | uint64_t tag = address & tagMask; 346 | CacheState state = cache->findTag(set, tag); 347 | bool hit = (state != CacheState::Invalid); 348 | 349 | if (countCompulsory && !is_prefetch) { 350 | checkCompulsory(address & ~lineMask); 351 | } 352 | 353 | // Handle hits 354 | if (accessType == AccessType::Write && hit) { 355 | cache->changeState(set, tag, CacheState::Modified); 356 | } 357 | 358 | if (hit) { 359 | cache->updateLRU(set, tag); 360 | 361 | if (!is_prefetch) { 362 | stats.hits++; 363 | if (prefetcher) { 364 | stats.prefetched += prefetcher->prefetchHit(address, tid, *this); 365 | } 366 | } 367 | 368 | return; 369 | } 370 | 371 | CacheState new_state = CacheState::Invalid; 372 | uint64_t evicted_tag; 373 | bool writeback = cache->checkWriteback(set, evicted_tag); 374 | 375 | if (writeback) { 376 | stats.local_writes++; 377 | } 378 | 379 | if (accessType == AccessType::Read) { 380 | new_state = CacheState::Exclusive; 381 | } else { 382 | new_state = CacheState::Modified; 383 | } 384 | 385 | if (!is_prefetch) { 386 | stats.local_reads++; 387 | } 388 | 389 | cache->insertLine(set, tag, new_state); 390 | if (!is_prefetch && prefetcher) { 391 | stats.prefetched += prefetcher->prefetchMiss(address, tid, *this); 392 | } 393 | } 394 | 395 | SingleCacheSystem::SingleCacheSystem( 396 | unsigned int line_size, unsigned int num_lines, unsigned int assoc, 397 | std::unique_ptr prefetcher, 398 | bool count_compulsory /*=false*/, 399 | bool do_addr_trans /*=false*/) : 400 | System(line_size, num_lines, assoc, 401 | std::move(prefetcher), count_compulsory, do_addr_trans), 402 | cache(std::make_unique(num_lines, assoc)) 403 | {} 404 | --------------------------------------------------------------------------------