Last active
April 1, 2019 10:04
-
-
Save abcdabcd987/f0d99b2f21567654276ca14d7d1338db to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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