|
|
@ -1,10 +1,49 @@ |
|
|
|
#include <iostream>
|
|
|
|
#include <random>
|
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include <omp.h>
|
|
|
|
|
|
|
|
#include "cache.hpp"
|
|
|
|
|
|
|
|
dsacache::Cache CACHE; |
|
|
|
|
|
|
|
void InitCache(const std::string& device) { |
|
|
|
if (device == "default") { |
|
|
|
auto cache_policy = [](const int numa_dst_node, const int numa_src_node, const size_t data_size) { |
|
|
|
return numa_dst_node; |
|
|
|
}; |
|
|
|
|
|
|
|
auto copy_policy = [](const int numa_dst_node, const int numa_src_node) { |
|
|
|
return std::vector<int>{ numa_src_node, numa_dst_node }; |
|
|
|
}; |
|
|
|
|
|
|
|
CACHE.Init(cache_policy,copy_policy); |
|
|
|
} |
|
|
|
else if (device == "xeonmax") { |
|
|
|
auto cache_policy = [](const int numa_dst_node, const int numa_src_node, const size_t data_size) { |
|
|
|
return numa_dst_node < 8 ? numa_dst_node + 8 : numa_dst_node; |
|
|
|
}; |
|
|
|
|
|
|
|
auto copy_policy = [](const int numa_dst_node, const int numa_src_node) { |
|
|
|
const bool same_socket = ((numa_dst_node ^ numa_src_node) & 4) == 0; |
|
|
|
if (same_socket) { |
|
|
|
const bool socket_number = numa_dst_node >> 2; |
|
|
|
if (socket_number == 0) return std::vector<int>{ 0, 1, 2, 3 }; |
|
|
|
else return std::vector<int>{ 4, 5, 6, 7 }; |
|
|
|
} |
|
|
|
else return std::vector<int>{ numa_src_node, numa_dst_node }; |
|
|
|
}; |
|
|
|
|
|
|
|
CACHE.Init(cache_policy,copy_policy); |
|
|
|
} |
|
|
|
else { |
|
|
|
std::cerr << "Given device '" << device << "' not supported!" << std::endl; |
|
|
|
exit(-1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
double* GetRandomArray(const size_t size) { |
|
|
|
double* array = new double[size]; |
|
|
|
|
|
|
@ -31,7 +70,7 @@ bool IsEqual(const double* a, const double* b, const size_t size) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
void PerformAccessAndTest(double* src, const size_t size) { |
|
|
|
void PerformAccessAndTest(double* src, const size_t size, const int tid) { |
|
|
|
std::unique_ptr<dsacache::CacheData> data_cache = CACHE.Access( |
|
|
|
reinterpret_cast<uint8_t *>(src), |
|
|
|
size * sizeof(double) |
|
|
@ -42,13 +81,13 @@ void PerformAccessAndTest(double* src, const size_t size) { |
|
|
|
// check the value immediately just to see if ram or cache was returned
|
|
|
|
|
|
|
|
if (src == cached_imm) { |
|
|
|
std::cout << "Caching did not immediately yield different data location." << std::endl; |
|
|
|
std::cout << "[" << tid << "] Caching did not immediately yield different data location." << std::endl; |
|
|
|
} |
|
|
|
else if (cached_imm == nullptr) { |
|
|
|
std::cout << "Immediately got nullptr." << std::endl; |
|
|
|
std::cout << "[" << tid << "] Immediately got nullptr." << std::endl; |
|
|
|
} |
|
|
|
else { |
|
|
|
std::cout << "Immediately got different data location." << std::endl; |
|
|
|
std::cout << "[" << tid << "] Immediately got different data location." << std::endl; |
|
|
|
} |
|
|
|
|
|
|
|
// waits for the completion of the asynchronous caching operation
|
|
|
@ -62,56 +101,93 @@ void PerformAccessAndTest(double* src, const size_t size) { |
|
|
|
// tests on the resulting value
|
|
|
|
|
|
|
|
if (src == cached) { |
|
|
|
std::cout << "Caching did not affect data location." << std::endl; |
|
|
|
std::cout << "[" << tid << "] Caching did not affect data location." << std::endl; |
|
|
|
} |
|
|
|
else if (cached == nullptr) { |
|
|
|
std::cerr << "Got nullptr from cache." << std::endl; |
|
|
|
std::cerr << "[" << tid << "] Got nullptr from cache." << std::endl; |
|
|
|
} |
|
|
|
else { |
|
|
|
std::cout << "Got different data location from cache." << std::endl; |
|
|
|
std::cout << "[" << tid << "] Got different data location from cache." << std::endl; |
|
|
|
} |
|
|
|
|
|
|
|
if (IsEqual(src,cached,size)) { |
|
|
|
std::cout << "Cached data is correct." << std::endl; |
|
|
|
std::cout << "[" << tid << "] Cached data is correct." << std::endl; |
|
|
|
} |
|
|
|
else { |
|
|
|
std::cerr << "Cached data is wrong." << std::endl; |
|
|
|
std::cerr << "[" << tid << "] Cached data is wrong." << std::endl; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int main(int argc, char **argv) { |
|
|
|
void RunTestST(const size_t size) { |
|
|
|
double* data = GetRandomArray(size); |
|
|
|
|
|
|
|
// given numa destination and source node and the size of the data
|
|
|
|
// this function decides on which the data will be placed
|
|
|
|
// which is used to select the HBM-node for the dst-node if desired
|
|
|
|
static constexpr int tid = 0; |
|
|
|
|
|
|
|
auto cache_policy = [](const int numa_dst_node, const int numa_src_node, const size_t data_size) { |
|
|
|
return numa_dst_node; |
|
|
|
}; |
|
|
|
std::cout << "[" << tid << "] first access --- " << std::endl; |
|
|
|
|
|
|
|
// this function receives the memory source and destination node
|
|
|
|
// and then decides, on which nodes the copy operation will be split
|
|
|
|
PerformAccessAndTest(data, size, tid); |
|
|
|
|
|
|
|
auto copy_policy = [](const int numa_dst_node, const int numa_src_node) { |
|
|
|
return std::vector{ numa_src_node, numa_dst_node }; |
|
|
|
}; |
|
|
|
std::cout << "[" << tid << "] second access --- " << std::endl; |
|
|
|
|
|
|
|
// initializes the cache with the two policies
|
|
|
|
PerformAccessAndTest(data, size, tid); |
|
|
|
|
|
|
|
CACHE.Init(cache_policy,copy_policy); |
|
|
|
std::cout << "[" << tid << "] end of application --- " << std::endl; |
|
|
|
} |
|
|
|
|
|
|
|
void RunTestMT(const size_t size) { |
|
|
|
double* data = GetRandomArray(size); |
|
|
|
|
|
|
|
// generate the test data
|
|
|
|
#pragma omp parallel
|
|
|
|
{ |
|
|
|
const int tid = omp_get_thread_num(); |
|
|
|
|
|
|
|
static constexpr size_t data_size = 1024 * 1024; |
|
|
|
double* data = GetRandomArray(data_size); |
|
|
|
std::cout << "[" << tid << "] first access --- " << std::endl; |
|
|
|
|
|
|
|
std::cout << "--- first access --- " << std::endl; |
|
|
|
PerformAccessAndTest(data, size, tid); |
|
|
|
|
|
|
|
PerformAccessAndTest(data, data_size); |
|
|
|
std::cout << "[" << tid << "] second access --- " << std::endl; |
|
|
|
|
|
|
|
std::cout << "--- second access --- " << std::endl; |
|
|
|
PerformAccessAndTest(data, size, tid); |
|
|
|
|
|
|
|
PerformAccessAndTest(data, data_size); |
|
|
|
std::cout << "[" << tid << "] end of block --- " << std::endl; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int main(int argc, char **argv) { |
|
|
|
if (argc != 4) { |
|
|
|
std::cerr << "This application requires four parameters!" << std::endl; |
|
|
|
|
|
|
|
std::cout << "Please provide the following positional arguments: [device] [mode] [size]" << std::endl; |
|
|
|
std::cout << "[device] from { default, xeonmax } which influences cache and execution placement" << std::endl; |
|
|
|
std::cout << "[mode] from { st, mt } or single and multi threaded respectively" << std::endl; |
|
|
|
std::cout << "[size] positive integral number, amount of float64 in data array" << std::endl; |
|
|
|
|
|
|
|
exit(-1); |
|
|
|
} |
|
|
|
|
|
|
|
std::cout << "--- end of application --- " << std::endl; |
|
|
|
const std::string device = argv[1]; |
|
|
|
const std::string mode = argv[2]; |
|
|
|
const std::string size_s = argv[3]; |
|
|
|
|
|
|
|
uint32_t size = 0; |
|
|
|
|
|
|
|
try { |
|
|
|
size = std::stoul(size_s); |
|
|
|
} |
|
|
|
catch (...) { |
|
|
|
std::cerr << "Given Size '" << size_s << "' caused error during conversion to number!" << std::endl; |
|
|
|
} |
|
|
|
|
|
|
|
InitCache(device); |
|
|
|
|
|
|
|
if (mode == "st") { |
|
|
|
RunTestST(size); |
|
|
|
} |
|
|
|
else if (mode == "mt") { |
|
|
|
RunTestMT(size); |
|
|
|
} |
|
|
|
else { |
|
|
|
std::cerr << "Given Mode '" << mode << "' not supported!" << std::endl; |
|
|
|
exit(-1); |
|
|
|
} |
|
|
|
} |