OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-SimpleGPUHashTable commits / a2ae55c

Iterate hash table on GPU
a2ae55c879069073f07aab871df4936fa94ce926
by David Farrell <nosferalatu@gmail.com> · 2020-03-02T01:39:58.000Z

Changed files (2) +61 -36

src/linearprobing.cu +35 -13
……@@ -16,7 +16,8 @@ __device__ uint32_t hash(uint32_t k)
1616
1717// Create a hash table. For linear probing, this is just an array
1818// of KeyValues. The hash table is
19KeyValue* create_hashtable(uint32_t capacity) {
19KeyValue* create_hashtable(uint32_t capacity)
20{
2021 // Allocate memory
2122 KeyValue* hashtable;
2223 cudaMalloc(&hashtable, sizeof(KeyValue) * kHashTableCapacity);
……@@ -87,24 +88,45 @@ void insert_hashtable(KeyValue* pHashTable, const KeyValue* kvs, uint32_t num_kv
8788 cudaFree(device_kvs);
8889}
8990
90std::vector<KeyValue> iterate_hashtable(KeyValue* pHashTable)
91__global__ void gpu_iterate_hashtable(KeyValue* pHashTable, KeyValue* kvs, uint32_t* kvs_size)
9192{
92 KeyValue* pHostHashTable;
93 cudaHostAlloc(&pHostHashTable, sizeof(KeyValue) * kHashTableCapacity, 0);
94 cudaMemcpy(pHostHashTable, pHashTable, sizeof(KeyValue) * kHashTableCapacity, cudaMemcpyDeviceToHost);
95
96 std::vector<KeyValue> kvs;
97 kvs.reserve(kHashTableCapacity);
98
99 for (uint32_t i = 0; i < kHashTableCapacity; i++)
93 unsigned int threadid = blockIdx.x * blockDim.x + threadIdx.x;
94 if (threadid < kHashTableCapacity)
10095 {
101 if (pHostHashTable[i].key != kEmpty)
96 if (pHashTable[threadid].key != kEmpty)
10297 {
103 kvs.push_back(pHostHashTable[i]);
98 uint32_t size = atomicAdd(kvs_size, 1);
99 kvs[size] = pHashTable[threadid];
104100 }
105101 }
102}
103
104std::vector<KeyValue> iterate_hashtable(KeyValue* pHashTable)
105{
106 uint32_t* device_num_kvs;
107 cudaMalloc(&device_num_kvs, sizeof(uint32_t));
108 cudaMemset(device_num_kvs, 0, sizeof(uint32_t));
109
110 KeyValue* device_kvs;
111 cudaMalloc(&device_kvs, sizeof(KeyValue) * kNumKeyValues);
112
113 int mingridsize;
114 int threadblocksize;
115 cudaOccupancyMaxPotentialBlockSize(&mingridsize, &threadblocksize, gpu_iterate_hashtable, 0, 0);
116
117 int gridsize = (kHashTableCapacity + threadblocksize - 1) / threadblocksize;
118 gpu_iterate_hashtable<<<gridsize, threadblocksize>>>(pHashTable, device_kvs, device_num_kvs);
106119
107 cudaFree(pHostHashTable);
120 uint32_t num_kvs;
121 cudaMemcpy(&num_kvs, device_num_kvs, sizeof(uint32_t), cudaMemcpyDeviceToHost);
122
123 std::vector<KeyValue> kvs;
124 kvs.resize(num_kvs);
125
126 cudaMemcpy(kvs.data(), device_kvs, sizeof(KeyValue) * num_kvs, cudaMemcpyDeviceToHost);
127
128 cudaFree(device_kvs);
129 cudaFree(device_num_kvs);
108130
109131 return kvs;
110132}
src/main.cpp +26 -23
……@@ -1,42 +1,42 @@
1#include "windows.h"
2#include "stdio.h"
1#include "algorithm"
2#include "random"
33#include "stdint.h"
4#include "unordered_set"
4#include "stdio.h"
55#include "unordered_map"
6#include "unordered_set"
67#include "vector"
7#include "algorithm"
8#include "random"
8#include "windows.h"
99#include "linearprobing.h"
1010
1111// Create random keys/values in the range [0, kEmpty)
1212// kEmpty is used to indicate an empty slot
13std::vector<KeyValue> generate_random_keyvalues()
13std::vector<KeyValue> generate_random_keyvalues()
1414{
1515 std::random_device rd;
16 std::mt19937 gen(rd()); // mersenne_twister_engine
17 std::uniform_int_distribution<uint32_t> dis(0, kEmpty-1);
16 std::mt19937 gen(rd()); // mersenne_twister_engine
17 std::uniform_int_distribution<uint32_t> dis(0, kEmpty - 1);
1818
1919 std::vector<KeyValue> kvs;
2020 kvs.reserve(kNumKeyValues);
2121
22 for (int i = 0; i < kNumKeyValues; i++)
22 for (int i = 0; i < kNumKeyValues; i++)
2323 {
2424 uint32_t rand0 = dis(gen);
2525 uint32_t rand1 = dis(gen);
26 kvs.push_back(KeyValue{ rand0, rand1 });
26 kvs.push_back(KeyValue{rand0, rand1});
2727 }
2828
2929 return kvs;
3030}
3131
32LARGE_INTEGER start_timer()
32LARGE_INTEGER start_timer()
3333{
3434 LARGE_INTEGER timer;
3535 QueryPerformanceCounter(&timer);
3636 return timer;
3737}
3838
39double get_elapsed_time(LARGE_INTEGER start)
39double get_elapsed_time(LARGE_INTEGER start)
4040{
4141 LARGE_INTEGER end;
4242 QueryPerformanceCounter(&end);
……@@ -52,27 +52,30 @@ double get_elapsed_time(LARGE_INTEGER start)
5252 return milliseconds;
5353}
5454
55void test_unordered_map(std::vector<KeyValue> all_kvs)
55void test_unordered_map(std::vector<KeyValue> all_kvs)
5656{
5757 LARGE_INTEGER timer = start_timer();
5858
59 std::unordered_map<uint32_t, uint32_t> kvs_map;
60 for (auto& kv : all_kvs)
59 printf("Testing insertion into std::unordered_map...\n");
60
6161 {
62 kvs_map[kv.key] = kv.value;
62 std::unordered_map<uint32_t, uint32_t> kvs_map;
63 for (auto& kv : all_kvs) {
64 kvs_map[kv.key] = kv.value;
65 }
6366 }
6467
6568 double milliseconds = get_elapsed_time(timer);
6669 double seconds = milliseconds / 1000.0f;
67 printf("std::unordered_map time : %f ms (%f million keys/second)\n",
70 printf("Total time for std::unordered_map: %f ms (%f million keys/second)\n",
6871 milliseconds, kNumKeyValues / seconds / 1000000.0f);
6972}
7073
7174void test_correctness(std::vector<KeyValue>, std::vector<KeyValue>);
7275
73int main()
76int main()
7477{
75 printf("Testing insertion of %d elements\n", kNumKeyValues);
78 printf("Testing insertion of %d elements into GPU hash table...\n", kNumKeyValues);
7679
7780 // Initialize keyvalue pairs with random numbers
7881 std::vector<KeyValue> all_kvs = generate_random_keyvalues();
……@@ -86,9 +89,9 @@ int main()
8689 const uint32_t num_batch_insertions = 16;
8790 static_assert(kNumKeyValues % num_batch_insertions == 0, "needs even divisor");
8891 uint32_t num_kvs_in_batch = kNumKeyValues / num_batch_insertions;
89 for (int i = 0; i < num_batch_insertions; i++)
92 for (int i = 0; i < num_batch_insertions; i++)
9093 {
91 insert_hashtable(pHashTable, all_kvs.data() + i*num_kvs_in_batch, num_kvs_in_batch);
94 insert_hashtable(pHashTable, all_kvs.data() + i * num_kvs_in_batch, num_kvs_in_batch);
9295 }
9396
9497 // Get all the key-values from the hash table
……@@ -99,8 +102,8 @@ int main()
99102 // Summarize results
100103 double milliseconds = get_elapsed_time(timer);
101104 double seconds = milliseconds / 1000.0f;
102 printf("Total time (including memory copies, readback, etc): %f ms (%f million keys/second)\n",
103 milliseconds, kNumKeyValues / seconds / 1000000.0f);
105 printf("Total time (including memory copies, readback, etc): %f ms (%f million keys/second)\n", milliseconds,
106 kNumKeyValues / seconds / 1000000.0f);
104107
105108 test_unordered_map(all_kvs);
106109