// g++ cache-prefetching.cc -Wall -std=c++11 -g -pthread -O2 | |
#include <string> | |
#include <vector> | |
#include <mutex> | |
#include <random> | |
#include <chrono> | |
#include <thread> | |
#include <iomanip> | |
#include <iostream> | |
#include <unordered_map> | |
class Foo { | |
std::mutex m_mutex; | |
std::unordered_map<std::string, int> m_map; | |
public: | |
void pass_by_reference(const std::string &key, int value) { | |
std::lock_guard<std::mutex> lock(m_mutex); | |
m_map.emplace(key, value); | |
} | |
void pass_by_value(const std::string key, int value) { | |
std::lock_guard<std::mutex> lock(m_mutex); | |
m_map.emplace(key, value); | |
} | |
void useless_copy(const std::string &key, int value) { | |
std::string copy(key); | |
std::lock_guard<std::mutex> lock(m_mutex); | |
m_map.emplace(copy, value); | |
} | |
void useless_hash(const std::string &key, int value) { | |
std::hash<std::string> hash_fn; | |
(void) hash_fn(key); | |
std::lock_guard<std::mutex> lock(m_mutex); | |
m_map.emplace(key, value); | |
} | |
void builtin_prefetch(const std::string &key, int value) { | |
__builtin_prefetch(key.data()); | |
std::lock_guard<std::mutex> lock(m_mutex); | |
m_map.emplace(key, value); | |
} | |
}; | |
std::string random_string(int len) { | |
static const char alphanum[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; | |
static std::mt19937 gen(123); | |
static std::uniform_int_distribution<int> dist(0, sizeof(alphanum) - 1); | |
std::string s(len, 0); | |
for (int i = 0; i < len; ++i) | |
s[i] = alphanum[dist(gen)]; | |
return s; | |
} | |
template <typename F> | |
void run(size_t num_workers, size_t num_keys, size_t num_ops, const std::vector<std::string> &key_set, F func) { | |
auto do_worker = [num_workers, num_keys, num_ops, &key_set, func] (size_t seed, Foo &foo) { | |
std::mt19937 gen(seed); | |
std::uniform_int_distribution<int> dist_key(0, key_set.size()-1), dist_value; | |
for (size_t i = 0; i < num_ops; ++i) { | |
auto &key = key_set[dist_key(gen)]; | |
(foo.*func)(key, dist_value(gen)); | |
} | |
}; | |
for (size_t i = 0; i < 16; ++i) { | |
Foo foo; | |
std::vector<std::thread> workers; | |
for (size_t i = 0; i < num_workers; ++i) | |
workers.emplace_back(do_worker, i+1, std::ref(foo)); | |
auto st = std::chrono::high_resolution_clock::now(); | |
for (auto &worker : workers) | |
worker.join(); | |
auto ed = std::chrono::high_resolution_clock::now(); | |
std::chrono::duration<double> diff = ed-st; | |
double time = diff.count(); | |
std::cout << ' ' << std::fixed << std::setprecision(6) << time << std::flush; | |
} | |
std::cout << std::endl; | |
} | |
int main(int argc, char** argv) { | |
const size_t num_workers = std::stoi(argv[1]); | |
const size_t key_size = std::stoi(argv[2]); | |
const size_t num_keys = 1000000 * 32 / key_size; | |
const size_t num_ops = 1000000; | |
std::vector<std::string> key_set; | |
for (size_t i = 0; i < num_keys; ++i) | |
key_set.emplace_back(random_string(key_size)); | |
std::cout << "pass by reference:"; | |
run(num_workers, num_keys, num_ops, key_set, &Foo::pass_by_reference); | |
std::cout << "pass by value :"; | |
run(num_workers, num_keys, num_ops, key_set, &Foo::pass_by_value); | |
std::cout << "useless copy :"; | |
run(num_workers, num_keys, num_ops, key_set, &Foo::useless_copy); | |
std::cout << "useless hash :"; | |
run(num_workers, num_keys, num_ops, key_set, &Foo::useless_hash); | |
std::cout << "builtin prefetch :"; | |
run(num_workers, num_keys, num_ops, key_set, &Foo::builtin_prefetch); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment