Skip to content

Commit f260997

Browse files
committed
Update
HW2
1 parent a6d2d6d commit f260997

File tree

12 files changed

+713
-0
lines changed

12 files changed

+713
-0
lines changed

HW2/HTtest

689 KB
Binary file not shown.

HW2/Makefile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
USER_ID := $(shell whoami)
2+
3+
.PHONY: run HTtest clean remote report format
4+
# HT Test
5+
run: HTtest
6+
./HTtest 0 | tee result/base.txt
7+
./HTtest 1 | tee result/better.txt
8+
9+
HTtest: main.cc
10+
g++ -std=c++11 -g -fopenmp -pthread $< -o $@ -lboost_system -lboost_thread
11+
12+
clean:
13+
rm -f HTtest result/*.txt
14+
15+
remote:
16+
scp better_locked_hash_table.h mgp-run:/home/${USER_ID}/HW2
17+
scp locked_hash_table.h mgp-run:/home/${USER_ID}/HW2
18+
scp hash_table.h mgp-run:/home/${USER_ID}/HW2
19+
ssh mgp-run "cd HW2 && make" | tee result/eval_output.txt
20+
21+
report:
22+
cat result/base.txt
23+
cat result/better.txt
24+
25+
# Development
26+
format:
27+
clang-format -i -style=Google *.cc *.h

HW2/README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Homework 2
2+
3+
## Summary
4+
5+
You should improve a performance of locked open addressing hash table.
6+
7+
Go to `better_locked_hash_table.h` and implement `TODO`
8+
9+
```
10+
$ make remote
11+
$ cat result/better.txt
12+
TABLE_SIZE 10000000 init: 4000000 new: 4000000 NT: 16 additional_reads: 9 use_custom: 1
13+
user-defined HT1 1
14+
start filling
15+
init hash table took 0.118376 sec
16+
start test
17+
test 36000000 ops took @@@@@@@ sec
18+
sanity check PASSED:
19+
```
20+
21+
## Functions
22+
23+
```
24+
# Run locally
25+
make
26+
make run
27+
# Compile
28+
make HTtest
29+
# Clean
30+
make clean
31+
# Evaluate performance
32+
make remote
33+
# Remove
34+
make remove
35+
```
36+
37+
## References
38+
39+
- [std::thread - cplusplus.com](http://www.cplusplus.com/reference/thread/thread/)

HW2/better_locked_hash_table.h

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#ifndef _BETTER_LOCKED_HASH_TABLE_H_
2+
#define _BETTER_LOCKED_HASH_TABLE_H_
3+
4+
5+
#include <iostream>
6+
#include <mutex>
7+
#include <thread>
8+
//
9+
#include <vector>
10+
#include <atomic>
11+
#include <shared_mutex>
12+
#include <boost/thread/shared_mutex.hpp>
13+
#include <boost/thread/locks.hpp>
14+
//
15+
#include "hash_table.h"
16+
#include "bucket.h"
17+
18+
class better_locked_probing_hash_table : public hash_table {
19+
20+
private:
21+
Bucket* table;
22+
const int TABLE_SIZE; //we do not consider resizing. Thus the table has to be larger than the max num items.
23+
std::mutex global_mutex;
24+
25+
static constexpr int NUM_STRIPES = 256;
26+
std::vector<std::mutex> stripe_mutexes;
27+
std::vector<boost::shared_mutex> shared_stripe_mutexes;
28+
29+
int get_stripe_index(uint64_t table_index)
30+
{
31+
return table_index % NUM_STRIPES;
32+
}
33+
34+
public:
35+
36+
better_locked_probing_hash_table(int table_size):TABLE_SIZE(table_size), stripe_mutexes(NUM_STRIPES), shared_stripe_mutexes(NUM_STRIPES) {
37+
this->table = new Bucket[TABLE_SIZE]();
38+
for(int i=0;i<TABLE_SIZE;i++) {
39+
this->table[i].valid=0; //means empty
40+
}
41+
}
42+
43+
virtual uint32_t hash(uint32_t x) override
44+
{
45+
//https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key
46+
x = ((x >> 16) ^ x) * 0x45d9f3b;
47+
x = ((x >> 16) ^ x) * 0x45d9f3b;
48+
x = (x >> 16) ^ x;
49+
return (x % TABLE_SIZE);
50+
}
51+
52+
// uint32_t hash(uint32_t x, std::mutex* m)
53+
// {
54+
// m->lock();
55+
// //https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key
56+
// x = ((x >> 16) ^ x) * 0x45d9f3b;
57+
// x = ((x >> 16) ^ x) * 0x45d9f3b;
58+
// x = (x >> 16) ^ x;
59+
// m->unlock();
60+
// return (x % TABLE_SIZE);
61+
// }
62+
63+
// uint32_t hash(std::atomic<uint32_t> *x)
64+
// {
65+
// uint32_t val = x->load();
66+
// //https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key
67+
// val = ((val >> 16) ^ val) * 0x45d9f3b;
68+
// val = ((val >> 16) ^ val) * 0x45d9f3b;
69+
// val = (val >> 16) ^ val;
70+
71+
// x->store(val);
72+
// return (val % TABLE_SIZE);
73+
// }
74+
75+
uint32_t hash2(uint32_t x)
76+
{
77+
// Which one is more effective?
78+
//https://stackoverflow.com/a/41537995
79+
const int p = 23;
80+
const uint32_t knuth = 2654435769;
81+
const uint32_t y = x;
82+
83+
x = (y * knuth) >> (32 - p);
84+
return (x % TABLE_SIZE);
85+
}
86+
87+
virtual uint32_t hash_next(uint32_t key, uint32_t prev_index) override
88+
{
89+
//linear probing. no special secondary hashfunction
90+
return ((prev_index + 1)% TABLE_SIZE);
91+
}
92+
93+
uint32_t hash_next(uint32_t key, uint32_t prev_index, int probe_count)
94+
{
95+
//quadratic probing. no special secondary hashfunction
96+
// std::cout << "We got probe_count: " << probe_count << std::endl;
97+
return ((prev_index + probe_count * probe_count + probe_count)% TABLE_SIZE);
98+
}
99+
100+
//the buffer has to be allocated by the caller
101+
bool read(uint32_t key, uint64_t* value_buffer){
102+
uint64_t index = this->hash(key);
103+
// boost::shared_lock<boost::shared_mutex> lock(shared_stripe_mutexes[get_stripe_index(index)]);
104+
int probe_count=0;
105+
106+
while(table[index].valid == true) {
107+
if(table[index].key == key) {
108+
*value_buffer = table[index].value;
109+
return true;
110+
} else {
111+
probe_count++;
112+
index = this->hash_next(key, index, probe_count);
113+
if(probe_count >= TABLE_SIZE) break;
114+
}
115+
}//end while
116+
117+
//If you reached here, you either encountered an empty slot or the table is full. In any case, the item you're looking for is not here
118+
return false;
119+
120+
}
121+
122+
123+
bool insert(uint32_t key, uint64_t value) {
124+
// std::lock_guard<std::mutex> lock(global_mutex);
125+
uint64_t index = this->hash(key);
126+
127+
int probe_count=0;
128+
129+
while (true) {
130+
std::lock_guard<std::mutex> lock(stripe_mutexes[get_stripe_index(index)]);
131+
// boost::unique_lock<boost::shared_mutex> lock(shared_stripe_mutexes[get_stripe_index(index)]);
132+
if (table[index].valid == false) {
133+
// Found an empty bucket
134+
table[index].valid = true;
135+
table[index].key = key;
136+
table[index].value = value;
137+
return true;
138+
} else if (table[index].key == key) {
139+
// Found a bucket with the same key, modify its value
140+
table[index].value = value;
141+
return true;
142+
} else {
143+
// Bucket is occupied by a different key, continue probing
144+
probe_count++;
145+
index = this->hash_next(key, index, probe_count);
146+
if (probe_count >= TABLE_SIZE) {
147+
// Could not add because the table is full
148+
return false;
149+
}
150+
}
151+
}
152+
153+
}
154+
155+
int num_items() {
156+
int count=0;
157+
for(int i=0;i<TABLE_SIZE;i++) {
158+
if(table[i].valid==true) count++;
159+
}
160+
return count;
161+
}
162+
};
163+
164+
#endif

HW2/bucket.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef _BUCKET_H_
2+
#define _BUCKET_H_
3+
#include <unistd.h>
4+
5+
struct Bucket { //16B in its size
6+
uint32_t key; // 2B key
7+
uint16_t valid; // 0=empty 1=occupied
8+
uint16_t meta; // Currently Unused. Reserved for 1B misc metadata (timestamp, contained lock, memo, tag, or anything)
9+
uint64_t value;
10+
};
11+
12+
#endif

HW2/error.h

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#ifndef _ERROR_H_
2+
#define _ERROR_H_
3+
4+
5+
#include <signal.h>
6+
#include <stdarg.h>
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
#include <errno.h>
10+
#include <sys/prctl.h>
11+
#include <sys/types.h>
12+
#include <sys/wait.h>
13+
#include <unistd.h>
14+
#include <string>
15+
#include <sstream>
16+
#include <iostream>
17+
#include <assert.h>
18+
19+
20+
21+
#define ASSERT(left,operator,right) { if(!((left) operator (right))){ std::cerr << "ASSERT FAILED: " << #left << #operator << #right << " @ " << __FILE__ << " (" << __LINE__ << "). " << #left << "=" << (left) << "; " << #right << "=" << (right) << std::endl; assert((left) operator (right));}}
22+
23+
void format_backtrace(std::string &output) {
24+
// Trim the right side of the output.
25+
size_t index = output.find_last_not_of(" \t\r\n");
26+
output.erase((index != std::string::npos) ? index + 1 : 0);
27+
28+
// Find an entry for the signal handler and trim all entries deeper than it.
29+
index = output.find("<signal handler called>");
30+
if (index != std::string::npos) {
31+
index = output.find('\n', index);
32+
if (index != std::string::npos) {
33+
output.erase(0, index + 1);
34+
}
35+
}
36+
37+
// Insert indentation.
38+
if (!output.empty()) {
39+
const std::string indent = " ";
40+
output.insert(0, indent);
41+
42+
for (size_t offset = output.find('\n'); offset != std::string::npos; ) {
43+
output.insert(offset + 1, indent);
44+
offset = output.find('\n', offset + 1);
45+
}
46+
}
47+
}
48+
void print_backtrace() {
49+
int pipefd[2];
50+
int use_pipe = (pipe(pipefd) == 0);
51+
52+
pid_t child = fork();
53+
if (child == -1) {
54+
return;
55+
}
56+
57+
if (child == 0) {
58+
if (use_pipe) {
59+
close(pipefd[0]);
60+
}
61+
62+
// Create a dummy process so that GDB can attach to it. This is to provide a
63+
// workaround for the case when ptrace_scope is enabled, in which processes
64+
// are not allowed to attach to non-child processes.
65+
pid_t dummy = fork();
66+
if (dummy == -1) {
67+
_Exit(1);
68+
}
69+
70+
if (dummy == 0) {
71+
if (use_pipe) {
72+
close(pipefd[1]);
73+
}
74+
75+
// Wait until the parent process sends SIGTERM on its termination.
76+
int result = prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
77+
if (result == 0) {
78+
pause();
79+
}
80+
} else {
81+
// Ignore stderr and redirect stdout to pipe (or stderr).
82+
dup2(use_pipe ? pipefd[1] : fileno(stderr), fileno(stdout));
83+
//FILE* tmp = freopen("/dev/null", "w", stderr);
84+
85+
// Run GDB to print the current stack trace.
86+
std::string pid = std::to_string(dummy);
87+
execlp("gdb", "gdb", "--pid", pid.c_str(), "--batch",
88+
"-ex", "set print frame-arguments none", "-ex", "bt", NULL);
89+
}
90+
91+
_Exit(0);
92+
} else {
93+
if (use_pipe) {
94+
close(pipefd[1]);
95+
96+
// Read backtrace from GDB.
97+
std::string output;
98+
for (char c; read(pipefd[0], &c, 1) > 0; ) {
99+
output.push_back(c);
100+
}
101+
102+
close(pipefd[0]);
103+
104+
// Format the output.
105+
format_backtrace(output);
106+
107+
// Print the backtrace.
108+
if (!output.empty()) {
109+
fprintf(stderr, "Backtrace:\n%s\n", output.c_str());
110+
}
111+
}
112+
113+
waitpid(child, NULL, 0);
114+
}
115+
}
116+
117+
118+
extern "C" void backtrace_handler(int sig) {
119+
// Print the current stack trace.
120+
print_backtrace();
121+
122+
// Call the original signal handler.
123+
signal(sig, SIG_DFL);
124+
int result = raise(sig);
125+
126+
if (result != 0) {
127+
_Exit(sig);
128+
}
129+
}
130+
131+
void install_backtrace_handler() {
132+
signal(SIGABRT, backtrace_handler);
133+
signal(SIGFPE, backtrace_handler);
134+
signal(SIGSEGV, backtrace_handler);
135+
}
136+
#endif

0 commit comments

Comments
 (0)