项目文件夹

文件
Muhammed Fatih BALIN 69a532c1ab [Feature] Gpu cache for node and edge data (#4341)
Co-authored-by: xiny <xiny@nvidia.com>
2023-07-24 13:17:10 +08:00

607 行
26 KiB
Plaintext

/*
* Copyright (c) 2023, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cooperative_groups.h>
#include <cuda_runtime_api.h>
#include <immintrin.h>
#include <atomic>
#include <iostream>
#include <limits>
#include <mutex>
#include <uvm_table.hpp>
namespace cg = cooperative_groups;
namespace {
constexpr int set_size = 4;
constexpr int block_size = 256;
template <typename key_type>
__host__ __device__ key_type hash(key_type key) {
return key;
}
template <typename key_type>
__global__ void hash_add_kernel(const key_type* new_keys, const int num_keys, key_type* keys,
const int num_sets, int* set_sizes, const int max_set_size,
key_type* missing_keys, int* num_missing_keys) {
__shared__ key_type s_missing_keys[block_size];
__shared__ int s_missing_count;
__shared__ size_t s_missing_idx;
auto grid = cg::this_grid();
auto block = cg::this_thread_block();
if (block.thread_rank() == 0) {
s_missing_count = 0;
}
block.sync();
size_t idx = grid.thread_rank();
if (idx < num_keys) {
auto key = new_keys[idx];
size_t idx_set = hash(key) % num_sets;
int prev_set_size = atomicAdd(&set_sizes[idx_set], 1);
if (prev_set_size < max_set_size) {
keys[idx_set * max_set_size + prev_set_size] = key;
} else {
int count = atomicAdd(&s_missing_count, 1);
s_missing_keys[count] = key;
}
}
block.sync();
if (block.thread_rank() == 0) {
s_missing_idx = atomicAdd(num_missing_keys, s_missing_count);
}
block.sync();
for (size_t i = block.thread_rank(); i < s_missing_count; i += block.num_threads()) {
missing_keys[s_missing_idx + i] = s_missing_keys[i];
}
}
template <typename key_type, typename index_type>
__global__ void hash_query_kernel(const key_type* query_keys, int* num_keys_ptr,
const key_type* keys, const size_t num_sets,
const int max_set_size, index_type* output_indices) {
constexpr int tile_size = set_size;
auto grid = cg::this_grid();
auto block = cg::this_thread_block();
auto tile = cg::tiled_partition<tile_size>(block);
int num_keys = *num_keys_ptr;
if (num_keys == 0) return;
#if (CUDA_VERSION < 11060)
size_t num_threads_per_grid = grid.size();
#else
size_t num_threads_per_grid = grid.num_threads();
#endif
size_t step = (num_keys - 1) / num_threads_per_grid + 1;
for (size_t i = 0; i < step; i++) {
size_t idx = i * num_threads_per_grid + grid.thread_rank();
key_type query_key = std::numeric_limits<key_type>::max();
if (idx < num_keys) {
query_key = query_keys[idx];
}
auto idx_set = hash(query_key) % num_sets;
for (int j = 0; j < tile_size; j++) {
auto current_idx_set = tile.shfl(idx_set, j);
auto current_query_key = tile.shfl(query_key, j);
if (current_query_key == std::numeric_limits<key_type>::max()) {
continue;
}
auto candidate_key = keys[current_idx_set * set_size + tile.thread_rank()];
int existed = tile.ballot(current_query_key == candidate_key);
auto current_idx = tile.shfl(idx, 0) + j;
if (existed) {
int src_lane = __ffs(existed) - 1;
size_t found_idx = current_idx_set * set_size + src_lane;
output_indices[current_idx] = num_sets * src_lane + current_idx_set;
} else {
output_indices[current_idx] = std::numeric_limits<index_type>::max();
}
}
}
}
template <typename key_type, typename index_type>
__global__ void hash_query_kernel(const key_type* query_keys, const int num_keys,
const key_type* keys, const size_t num_sets,
const int max_set_size, index_type* output_indices,
key_type* missing_keys, int* missing_positions,
int* missing_count) {
__shared__ key_type s_missing_keys[block_size];
__shared__ key_type s_missing_positions[block_size];
__shared__ int s_missing_count;
__shared__ int s_missing_idx;
constexpr int tile_size = set_size;
auto grid = cg::this_grid();
auto block = cg::this_thread_block();
auto tile = cg::tiled_partition<tile_size>(block);
if (block.thread_rank() == 0) {
s_missing_count = 0;
}
block.sync();
size_t idx = grid.thread_rank();
key_type query_key = std::numeric_limits<key_type>::max();
if (idx < num_keys) {
query_key = query_keys[idx];
}
auto idx_set = hash(query_key) % num_sets;
for (int j = 0; j < tile_size; j++) {
auto current_idx_set = tile.shfl(idx_set, j);
auto current_query_key = tile.shfl(query_key, j);
if (current_query_key == std::numeric_limits<key_type>::max()) {
continue;
}
auto candidate_key = keys[current_idx_set * set_size + tile.thread_rank()];
int existed = tile.ballot(current_query_key == candidate_key);
if (existed) {
int src_lane = __ffs(existed) - 1;
size_t found_idx = current_idx_set * set_size + src_lane;
output_indices[tile.shfl(idx, 0) + j] = num_sets * src_lane + current_idx_set;
} else {
auto current_idx = tile.shfl(idx, 0) + j;
output_indices[current_idx] = std::numeric_limits<index_type>::max();
if (tile.thread_rank() == 0) {
int s_count = atomicAdd(&s_missing_count, 1);
s_missing_keys[s_count] = current_query_key;
s_missing_positions[s_count] = current_idx;
}
}
}
if (missing_keys == nullptr) {
if (grid.thread_rank() == 0 && missing_count) {
*missing_count = 0;
}
return;
}
block.sync();
if (block.thread_rank() == 0) {
s_missing_idx = atomicAdd(missing_count, s_missing_count);
}
block.sync();
for (size_t i = block.thread_rank(); i < s_missing_count; i += block.num_threads()) {
missing_keys[s_missing_idx + i] = s_missing_keys[i];
missing_positions[s_missing_idx + i] = s_missing_positions[i];
}
}
template <int warp_size>
__forceinline__ __device__ void warp_tile_copy(const size_t lane_idx,
const size_t emb_vec_size_in_float,
volatile float* d_dst, const float* d_src) {
// 16 bytes align
if (emb_vec_size_in_float % 4 != 0 || (size_t)d_dst % 16 != 0 || (size_t)d_src % 16 != 0) {
#pragma unroll
for (size_t i = lane_idx; i < emb_vec_size_in_float; i += warp_size) {
d_dst[i] = d_src[i];
}
} else {
#pragma unroll
for (size_t i = lane_idx; i < emb_vec_size_in_float / 4; i += warp_size) {
*(float4*)(d_dst + i * 4) = __ldg((const float4*)(d_src + i * 4));
}
}
}
template <typename index_type, typename vec_type>
__global__ void read_vectors_kernel(const index_type* query_indices, const int num_keys,
const vec_type* vectors, const int vec_size,
vec_type* output_vectors) {
constexpr int warp_size = 32;
auto grid = cg::this_grid();
auto block = cg::this_thread_block();
auto tile = cg::tiled_partition<warp_size>(block);
#if (CUDA_VERSION < 11060)
auto num_threads_per_grid = grid.size();
#else
auto num_threads_per_grid = grid.num_threads();
#endif
for (int step = 0; step < (num_keys - 1) / num_threads_per_grid + 1; step++) {
int key_num = step * num_threads_per_grid + grid.thread_rank();
index_type idx = std::numeric_limits<index_type>::max();
if (key_num < num_keys) {
idx = query_indices[key_num];
}
#pragma unroll 4
for (size_t j = 0; j < warp_size; j++) {
index_type current_idx = tile.shfl(idx, j);
index_type idx_write = tile.shfl(key_num, 0) + j;
if (current_idx == std::numeric_limits<index_type>::max()) continue;
warp_tile_copy<warp_size>(tile.thread_rank(), vec_size, output_vectors + idx_write * vec_size,
vectors + current_idx * vec_size);
}
}
}
template <typename index_type, typename vec_type>
__global__ void distribute_vectors_kernel(const index_type* postions, const size_t num_keys,
const vec_type* vectors, const int vec_size,
vec_type* output_vectors) {
constexpr int warp_size = 32;
auto grid = cg::this_grid();
auto block = cg::this_thread_block();
auto tile = cg::tiled_partition<warp_size>(block);
#if (CUDA_VERSION < 11060)
auto num_threads_per_grid = grid.size();
#else
auto num_threads_per_grid = grid.num_threads();
#endif
for (size_t step = 0; step < (num_keys - 1) / num_threads_per_grid + 1; step++) {
size_t key_num = step * num_threads_per_grid + grid.thread_rank();
index_type idx = std::numeric_limits<index_type>::max();
if (key_num < num_keys) {
idx = postions[key_num];
}
#pragma unroll 4
for (size_t j = 0; j < warp_size; j++) {
size_t idx_write = tile.shfl(idx, j);
size_t idx_read = tile.shfl(key_num, 0) + j;
if (idx_write == std::numeric_limits<index_type>::max()) continue;
warp_tile_copy<warp_size>(tile.thread_rank(), vec_size,
output_vectors + (size_t)idx_write * vec_size,
vectors + (size_t)idx_read * vec_size);
}
}
}
} // namespace
namespace gpu_cache {
template <typename key_type, typename index_type, typename vec_type>
UvmTable<key_type, index_type, vec_type>::UvmTable(const size_t device_table_capacity,
const size_t host_table_capacity,
const int max_batch_size, const int vec_size,
const vec_type default_value)
: max_batch_size_(std::max(100000, max_batch_size)),
vec_size_(vec_size),
num_set_((device_table_capacity - 1) / set_size + 1),
num_host_set_((host_table_capacity - 1) / set_size + 1),
table_capacity_(num_set_ * set_size),
default_vector_(vec_size, default_value),
device_table_(device_table_capacity, set_size, max_batch_size_),
host_table_(host_table_capacity * 1.3, set_size, max_batch_size_) {
CUDA_CHECK(cudaMalloc(&d_keys_buffer_, sizeof(key_type) * max_batch_size_));
CUDA_CHECK(cudaMalloc(&d_vectors_buffer_, sizeof(vec_type) * max_batch_size_ * vec_size_));
CUDA_CHECK(cudaMalloc(&d_vectors_, sizeof(vec_type) * device_table_.capacity * vec_size_));
CUDA_CHECK(cudaMalloc(&d_output_indices_, sizeof(index_type) * max_batch_size_));
CUDA_CHECK(cudaMalloc(&d_output_host_indices_, sizeof(index_type) * max_batch_size_));
CUDA_CHECK(cudaMallocHost(&h_output_host_indices_, sizeof(index_type) * max_batch_size_));
CUDA_CHECK(cudaMalloc(&d_missing_keys_, sizeof(key_type) * max_batch_size_));
CUDA_CHECK(cudaMalloc(&d_missing_positions_, sizeof(int) * max_batch_size_));
CUDA_CHECK(cudaMalloc(&d_missing_count_, sizeof(int)));
CUDA_CHECK(cudaMemset(d_missing_count_, 0, sizeof(int)));
CUDA_CHECK(cudaStreamCreate(&query_stream_));
for (int i = 0; i < num_buffers_; i++) {
int batch_size_per_buffer = ceil(1.0 * max_batch_size_ / num_buffers_);
CUDA_CHECK(
cudaMallocHost(&h_cpy_buffers_[i], sizeof(vec_type) * batch_size_per_buffer * vec_size));
CUDA_CHECK(cudaMalloc(&d_cpy_buffers_[i], sizeof(vec_type) * batch_size_per_buffer * vec_size));
CUDA_CHECK(cudaStreamCreate(&cpy_streams_[i]));
CUDA_CHECK(cudaEventCreate(&cpy_events_[i]));
}
CUDA_CHECK(cudaMallocHost(&h_missing_keys_, sizeof(key_type) * max_batch_size_));
CUDA_CHECK(cudaEventCreate(&query_event_));
h_vectors_.resize(host_table_.capacity * vec_size_);
}
template <typename key_type, typename index_type, typename vec_type>
void UvmTable<key_type, index_type, vec_type>::add(const key_type* h_keys,
const vec_type* h_vectors,
const size_t num_keys) {
std::vector<key_type> h_missing_keys;
size_t num_batches = (num_keys - 1) / max_batch_size_ + 1;
for (size_t i = 0; i < num_batches; i++) {
size_t this_batch_size =
i != num_batches - 1 ? max_batch_size_ : num_keys - i * max_batch_size_;
CUDA_CHECK(cudaMemcpy(d_keys_buffer_, h_keys + i * max_batch_size_,
sizeof(*d_keys_buffer_) * this_batch_size, cudaMemcpyHostToDevice));
CUDA_CHECK(cudaMemset(d_missing_count_, 0, sizeof(*d_missing_count_)));
device_table_.add(d_keys_buffer_, this_batch_size, d_missing_keys_, d_missing_count_, 0);
CUDA_CHECK(cudaDeviceSynchronize());
int num_missing_keys;
CUDA_CHECK(cudaMemcpy(&num_missing_keys, d_missing_count_, sizeof(num_missing_keys),
cudaMemcpyDeviceToHost));
size_t prev_size = h_missing_keys.size();
h_missing_keys.resize(prev_size + num_missing_keys);
CUDA_CHECK(cudaMemcpy(h_missing_keys.data() + prev_size, d_missing_keys_,
sizeof(*d_missing_keys_) * num_missing_keys, cudaMemcpyDeviceToHost));
}
std::vector<key_type> h_final_missing_keys;
num_batches = h_missing_keys.size() ? (h_missing_keys.size() - 1) / max_batch_size_ + 1 : 0;
for (size_t i = 0; i < num_batches; i++) {
size_t this_batch_size =
i != num_batches - 1 ? max_batch_size_ : h_missing_keys.size() - i * max_batch_size_;
CUDA_CHECK(cudaMemcpy(d_keys_buffer_, h_missing_keys.data() + i * max_batch_size_,
sizeof(*d_keys_buffer_) * this_batch_size, cudaMemcpyHostToDevice));
CUDA_CHECK(cudaMemset(d_missing_count_, 0, sizeof(*d_missing_count_)));
host_table_.add(d_keys_buffer_, this_batch_size, d_missing_keys_, d_missing_count_, 0);
CUDA_CHECK(cudaDeviceSynchronize());
int num_missing_keys;
CUDA_CHECK(cudaMemcpy(&num_missing_keys, d_missing_count_, sizeof(num_missing_keys),
cudaMemcpyDeviceToHost));
size_t prev_size = h_final_missing_keys.size();
h_final_missing_keys.resize(prev_size + num_missing_keys);
CUDA_CHECK(cudaMemcpy(h_final_missing_keys.data() + prev_size, d_missing_keys_,
sizeof(*d_missing_keys_) * num_missing_keys, cudaMemcpyDeviceToHost));
}
std::vector<key_type> h_keys_buffer(max_batch_size_);
std::vector<index_type> h_indices_buffer(max_batch_size_);
std::vector<int> h_positions_buffer(max_batch_size_);
num_batches = (num_keys - 1) / max_batch_size_ + 1;
size_t num_hit_keys = 0;
for (size_t i = 0; i < num_batches; i++) {
size_t this_batch_size =
i != num_batches - 1 ? max_batch_size_ : num_keys - i * max_batch_size_;
CUDA_CHECK(cudaMemcpy(d_keys_buffer_, h_keys + i * max_batch_size_,
sizeof(*d_keys_buffer_) * this_batch_size, cudaMemcpyHostToDevice));
CUDA_CHECK(cudaMemset(d_missing_count_, 0, sizeof(*d_missing_count_)));
device_table_.query(d_keys_buffer_, this_batch_size, d_output_indices_, d_missing_keys_,
d_missing_positions_, d_missing_count_, 0);
CUDA_CHECK(cudaStreamSynchronize(0));
CUDA_CHECK(cudaMemcpy(d_vectors_buffer_, h_vectors + i * max_batch_size_ * vec_size_,
sizeof(*d_vectors_) * this_batch_size * vec_size_,
cudaMemcpyHostToDevice));
CUDA_CHECK(cudaStreamSynchronize(0));
if (num_hit_keys < device_table_.capacity) {
distribute_vectors_kernel<<<(this_batch_size - 1) / block_size + 1, block_size, 0, 0>>>(
d_output_indices_, this_batch_size, d_vectors_buffer_, vec_size_, d_vectors_);
CUDA_CHECK(cudaStreamSynchronize(0));
}
int num_missing_keys;
CUDA_CHECK(cudaMemcpy(&num_missing_keys, d_missing_count_, sizeof(num_missing_keys),
cudaMemcpyDeviceToHost));
num_hit_keys += this_batch_size - num_missing_keys;
host_table_.query(d_missing_keys_, num_missing_keys, d_output_indices_, nullptr, nullptr,
nullptr, 0);
CUDA_CHECK(cudaMemcpy(h_keys_buffer.data(), d_missing_keys_,
sizeof(*d_missing_keys_) * num_missing_keys, cudaMemcpyDeviceToHost))
CUDA_CHECK(cudaMemcpy(h_indices_buffer.data(), d_output_indices_,
sizeof(*d_output_indices_) * num_missing_keys, cudaMemcpyDeviceToHost))
CUDA_CHECK(cudaMemcpy(h_positions_buffer.data(), d_missing_positions_,
sizeof(*d_missing_positions_) * num_missing_keys, cudaMemcpyDeviceToHost))
for (int j = 0; j < num_missing_keys; j++) {
if (h_indices_buffer[j] != std::numeric_limits<index_type>::max()) {
memcpy(h_vectors_.data() + h_indices_buffer[j] * vec_size_,
h_vectors + (i * max_batch_size_ + h_positions_buffer[j]) * vec_size_,
sizeof(*h_vectors) * vec_size_);
} else {
size_t prev_idx = h_vectors_.size() / vec_size_;
h_final_missing_items_.emplace(h_keys_buffer[j], prev_idx);
h_vectors_.resize(h_vectors_.size() + vec_size_);
memcpy(h_vectors_.data() + prev_idx * vec_size_,
h_vectors + (i * max_batch_size_ + h_positions_buffer[j]) * vec_size_,
sizeof(*h_vectors) * vec_size_);
}
}
}
CUDA_CHECK(cudaMemset(d_missing_count_, 0, sizeof(*d_missing_count_)));
}
template <typename key_type, typename index_type, typename vec_type>
void UvmTable<key_type, index_type, vec_type>::query(const key_type* d_keys, const int num_keys,
vec_type* d_vectors, cudaStream_t stream) {
if (!num_keys) return;
CUDA_CHECK(cudaEventRecord(query_event_, stream));
CUDA_CHECK(cudaStreamWaitEvent(query_stream_, query_event_));
static_assert(num_buffers_ >= 2);
device_table_.query(d_keys, num_keys, d_output_indices_, d_missing_keys_, d_missing_positions_,
d_missing_count_, query_stream_);
CUDA_CHECK(cudaEventRecord(query_event_, query_stream_));
CUDA_CHECK(cudaStreamWaitEvent(cpy_streams_[0], query_event_));
int num_missing_keys;
CUDA_CHECK(cudaMemcpyAsync(&num_missing_keys, d_missing_count_, sizeof(*d_missing_count_),
cudaMemcpyDeviceToHost, cpy_streams_[0]));
host_table_.query(d_missing_keys_, d_missing_count_, d_output_host_indices_, query_stream_);
CUDA_CHECK(cudaStreamSynchronize(cpy_streams_[0]));
CUDA_CHECK(cudaMemsetAsync(d_missing_count_, 0, sizeof(*d_missing_count_), query_stream_));
CUDA_CHECK(cudaMemcpyAsync(h_output_host_indices_, d_output_host_indices_,
sizeof(index_type) * num_missing_keys, cudaMemcpyDeviceToHost,
query_stream_));
CUDA_CHECK(cudaMemcpyAsync(h_missing_keys_, d_missing_keys_, sizeof(key_type) * num_missing_keys,
cudaMemcpyDeviceToHost, cpy_streams_[0]));
read_vectors_kernel<<<(num_keys - 1) / block_size + 1, block_size, 0, cpy_streams_[1]>>>(
d_output_indices_, num_keys, d_vectors_, vec_size_, d_vectors);
CUDA_CHECK(cudaStreamSynchronize(query_stream_));
CUDA_CHECK(cudaStreamSynchronize(cpy_streams_[0]));
int num_keys_per_buffer = ceil(1.0 * num_missing_keys / num_buffers_);
for (int buffer_num = 0; buffer_num < num_buffers_; buffer_num++) {
int num_keys_this_buffer = buffer_num != num_buffers_ - 1
? num_keys_per_buffer
: num_missing_keys - num_keys_per_buffer * buffer_num;
if (!num_keys_this_buffer) break;
#pragma omp parallel for num_threads(8)
for (size_t i = 0; i < static_cast<size_t>(num_keys_this_buffer); i++) {
size_t idx_key = buffer_num * num_keys_per_buffer + i;
index_type index = h_output_host_indices_[idx_key];
if (index == std::numeric_limits<index_type>::max()) {
key_type key = h_missing_keys_[idx_key];
auto iterator = h_final_missing_items_.find(key);
if (iterator != h_final_missing_items_.end()) {
index = iterator->second;
}
}
if (index != std::numeric_limits<index_type>::max()) {
memcpy(h_cpy_buffers_[buffer_num] + i * vec_size_, h_vectors_.data() + index * vec_size_,
sizeof(vec_type) * vec_size_);
} else {
memcpy(h_cpy_buffers_[buffer_num] + i * vec_size_, default_vector_.data(),
sizeof(vec_type) * vec_size_);
}
}
CUDA_CHECK(cudaMemcpyAsync(d_cpy_buffers_[buffer_num], h_cpy_buffers_[buffer_num],
sizeof(vec_type) * num_keys_this_buffer * vec_size_,
cudaMemcpyHostToDevice, cpy_streams_[buffer_num]));
distribute_vectors_kernel<<<(num_keys_this_buffer - 1) / block_size + 1, block_size, 0,
cpy_streams_[buffer_num]>>>(
d_missing_positions_ + buffer_num * num_keys_per_buffer, num_keys_this_buffer,
d_cpy_buffers_[buffer_num], vec_size_, d_vectors);
}
for (int i = 0; i < num_buffers_; i++) {
CUDA_CHECK(cudaEventRecord(cpy_events_[i], cpy_streams_[i]));
CUDA_CHECK(cudaStreamWaitEvent(stream, cpy_events_[i]));
}
}
template <typename key_type, typename index_type, typename vec_type>
void UvmTable<key_type, index_type, vec_type>::clear(cudaStream_t stream) {
device_table_.clear(stream);
host_table_.clear(stream);
}
template <typename key_type, typename index_type, typename vec_type>
UvmTable<key_type, index_type, vec_type>::~UvmTable() {
CUDA_CHECK(cudaFree(d_keys_buffer_));
CUDA_CHECK(cudaFree(d_vectors_buffer_));
CUDA_CHECK(cudaFree(d_vectors_));
CUDA_CHECK(cudaFree(d_output_indices_));
CUDA_CHECK(cudaFree(d_output_host_indices_));
CUDA_CHECK(cudaFreeHost(h_output_host_indices_));
CUDA_CHECK(cudaFree(d_missing_keys_));
CUDA_CHECK(cudaFree(d_missing_positions_));
CUDA_CHECK(cudaFree(d_missing_count_));
CUDA_CHECK(cudaFreeHost(h_missing_keys_));
CUDA_CHECK(cudaStreamDestroy(query_stream_));
CUDA_CHECK(cudaEventDestroy(query_event_));
for (int i = 0; i < num_buffers_; i++) {
CUDA_CHECK(cudaFreeHost(h_cpy_buffers_[i]));
CUDA_CHECK(cudaFree(d_cpy_buffers_[i]));
CUDA_CHECK(cudaStreamDestroy(cpy_streams_[i]));
CUDA_CHECK(cudaEventDestroy(cpy_events_[i]));
}
}
template <typename key_type, typename index_type>
HashBlock<key_type, index_type>::HashBlock(size_t expected_capacity, int set_size, int batch_size)
: max_set_size_(set_size), batch_size_(batch_size) {
if (expected_capacity) {
num_sets = (expected_capacity - 1) / set_size + 1;
} else {
num_sets = 10000;
}
capacity = num_sets * set_size;
CUDA_CHECK(cudaMalloc(&keys, sizeof(*keys) * capacity));
CUDA_CHECK(cudaMalloc(&set_sizes_, sizeof(*set_sizes_) * num_sets));
CUDA_CHECK(cudaMemset(set_sizes_, 0, sizeof(*set_sizes_) * num_sets));
}
template <typename key_type, typename index_type>
HashBlock<key_type, index_type>::~HashBlock() {
CUDA_CHECK(cudaFree(keys));
CUDA_CHECK(cudaFree(set_sizes_));
}
template <typename key_type, typename index_type>
void HashBlock<key_type, index_type>::query(const key_type* query_keys, const size_t num_keys,
index_type* output_indices, key_type* missing_keys,
int* missing_positions, int* num_missing_keys,
cudaStream_t stream) {
if (num_keys == 0) {
return;
}
size_t num_batches = (num_keys - 1) / batch_size_ + 1;
for (size_t i = 0; i < num_batches; i++) {
size_t this_batch_size = i != num_batches - 1 ? batch_size_ : num_keys - i * batch_size_;
hash_query_kernel<<<(this_batch_size - 1) / block_size + 1, block_size, 0, stream>>>(
query_keys, this_batch_size, keys, num_sets, max_set_size_, output_indices, missing_keys,
missing_positions, num_missing_keys);
}
}
template <typename key_type, typename index_type>
void HashBlock<key_type, index_type>::query(const key_type* query_keys, int* num_keys,
index_type* output_indices, cudaStream_t stream) {
hash_query_kernel<<<128, 64, 0, stream>>>(query_keys, num_keys, keys, num_sets, max_set_size_,
output_indices);
}
template <typename key_type, typename index_type>
void HashBlock<key_type, index_type>::add(const key_type* new_keys, const size_t num_keys,
key_type* missing_keys, int* num_missing_keys,
cudaStream_t stream) {
if (num_keys == 0) {
return;
}
size_t num_batches = (num_keys - 1) / batch_size_ + 1;
for (size_t i = 0; i < num_batches; i++) {
size_t this_batch_size = i != num_batches - 1 ? batch_size_ : num_keys - i * batch_size_;
hash_add_kernel<<<(this_batch_size - 1) / block_size + 1, block_size, 0, stream>>>(
new_keys + i * this_batch_size, this_batch_size, keys, num_sets, set_sizes_, max_set_size_,
missing_keys, num_missing_keys);
}
}
template <typename key_type, typename index_type>
void HashBlock<key_type, index_type>::clear(cudaStream_t stream) {
CUDA_CHECK(cudaMemsetAsync(set_sizes_, 0, sizeof(*set_sizes_) * num_sets, stream));
}
template class HashBlock<int, size_t>;
template class HashBlock<int64_t, size_t>;
template class HashBlock<size_t, size_t>;
template class HashBlock<unsigned int, size_t>;
template class HashBlock<long long, size_t>;
template class UvmTable<int, size_t>;
template class UvmTable<int64_t, size_t>;
template class UvmTable<size_t, size_t>;
template class UvmTable<unsigned int, size_t>;
template class UvmTable<long long, size_t>;
} // namespace gpu_cache