Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Erase Functionality for static_map #142

Merged
merged 28 commits into from
May 6, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0215463
basic erase functionality added, still need to modify insert/find
niskos99 Feb 22, 2022
26dd44f
erase appears to be working
niskos99 Feb 23, 2022
9e0821c
cg erase
niskos99 Feb 23, 2022
1777a51
cg erase bug fix
niskos99 Feb 23, 2022
d84e3b8
minor changes
niskos99 Feb 23, 2022
acc7941
fix to CG insert
niskos99 Feb 25, 2022
19aa8bb
insert benchmarking fix
niskos99 Feb 25, 2022
0e66d28
API improvements
niskos99 Mar 10, 2022
364fb1d
[pre-commit.ci] auto code formatting
pre-commit-ci[bot] Mar 10, 2022
7f84d3d
typo fix for non-CG contains
niskos99 Mar 10, 2022
c2bcc25
style changes
niskos99 Mar 18, 2022
9deba51
[pre-commit.ci] auto code formatting
pre-commit-ci[bot] Mar 18, 2022
2872f1d
style and documentation, strong-type wrappers for sentinel values
niskos99 Mar 18, 2022
666ff5b
[pre-commit.ci] auto code formatting
pre-commit-ci[bot] Mar 18, 2022
9781a6b
copyright notices corrected
niskos99 Mar 18, 2022
7faef67
sentinel wrappers moved to separate file
niskos99 Mar 21, 2022
c9a7014
[pre-commit.ci] auto code formatting
pre-commit-ci[bot] Mar 21, 2022
c0d713c
sentinel constructors added for ctad support
niskos99 Mar 22, 2022
d92898c
[pre-commit.ci] auto code formatting
pre-commit-ci[bot] Mar 22, 2022
5bca985
CI fix
niskos99 Mar 23, 2022
60e4b73
switch to fetch_add for erase kernels
niskos99 Mar 24, 2022
4feef9c
[pre-commit.ci] auto code formatting
pre-commit-ci[bot] Mar 24, 2022
a429d8d
minor doc/style changes
niskos99 Apr 2, 2022
dc61121
[pre-commit.ci] auto code formatting
pre-commit-ci[bot] Apr 2, 2022
753641c
prevent implicit conversion of sentinels during construction
niskos99 Apr 5, 2022
03c089c
[pre-commit.ci] auto code formatting
pre-commit-ci[bot] Apr 5, 2022
02e861e
Merge remote-tracking branch 'upstream/dev' into static_map_erase
PointKernel May 6, 2022
7b841a8
Fix a sentinel bug in test
PointKernel May 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion include/cuco/detail/error.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, NVIDIA CORPORATION.
* Copyright (c) 2022, NVIDIA CORPORATION.
PointKernel marked this conversation as resolved.
Show resolved Hide resolved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -80,3 +80,23 @@ struct cuda_error : public std::runtime_error {
cudaError_t const status = (expr); \
assert(cudaSuccess == status); \
} while (0)

/**
* @brief Macro for checking runtime conditions that throws an exception when
* a condition is violated.
*
* Example usage:
*
* @code
* CUCO_RUNTIME_EXPECTS(key == value, "Key value mismatch");
* @endcode
*
* @param[in] cond Expression that evaluates to true or false
* @param[in] reason String literal description of the reason that cond is
* expected to be true
* @throw std::runtime_error if the condition evaluates to false.
*/
#define CUCO_RUNTIME_EXPECTS(cond, reason) \
(!!(cond)) ? static_cast<void>(0) \
: throw std::runtime_error("cuco failure at: " __FILE__ \
":" CUCO_STRINGIFY(__LINE__) ": " reason)
33 changes: 23 additions & 10 deletions include/cuco/detail/static_map.inl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020-2021, NVIDIA CORPORATION.
* Copyright (c) 2021-2022, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,6 +15,7 @@
*/

#include <cuco/detail/bitwise_compare.cuh>
#include <cuco/detail/error.hpp>

namespace cuco {

Expand Down Expand Up @@ -146,15 +147,15 @@ void static_map<Key, Value, Scope, Allocator>::erase(
InputIt first, InputIt last, Hash hash, KeyEqual key_equal, cudaStream_t stream)
{
if (get_empty_key_sentinel() == get_erased_key_sentinel())
throw std::runtime_error(
"Runtime error: You must provide a unique erased key sentinel value at map construction.\n");
CUCO_RUNTIME_EXPECTS(get_empty_key_sentinel() != get_erased_key_sentinel(),
"You must provide a unique erased key sentinel value at map construction.");

auto num_keys = std::distance(first, last);
if (num_keys == 0) { return; }

auto const block_size = 128;
auto const stride = 1;
auto const tile_size = 4;
auto constexpr block_size = 128;
auto constexpr stride = 1;
auto constexpr tile_size = 4;
auto const grid_size = (tile_size * num_keys + stride * block_size - 1) / (stride * block_size);
auto view = get_device_mutable_view();

Expand Down Expand Up @@ -438,8 +439,16 @@ __device__ bool static_map<Key, Value, Scope, Allocator>::device_mutable_view::e
make_pair<Key, Value>(this->get_erased_key_sentinel(), this->get_empty_value_sentinel());

while (true) {
auto existing_key = current_slot->first.load(cuda::std::memory_order_relaxed);
auto existing_value = current_slot->second.load(cuda::std::memory_order_relaxed);
//auto existing_key = current_slot->first.load(cuda::std::memory_order_relaxed);
//auto existing_value = current_slot->second.load(cuda::std::memory_order_relaxed);

Nicolas-Iskos marked this conversation as resolved.
Show resolved Hide resolved
static_assert(sizeof(Key) == sizeof(atomic_key_type));
static_assert(sizeof(Value) == sizeof(atomic_mapped_type));
// TODO: Replace reinterpret_cast with atomic ref when available.
value_type slot_contents = *reinterpret_cast<value_type const*>(current_slot);
auto existing_key = slot_contents.first;
auto existing_value = slot_contents.second;


// Key doesn't exist, return false
if (detail::bitwise_compare(existing_key, this->get_empty_key_sentinel())) { return false; }
Expand Down Expand Up @@ -479,8 +488,12 @@ __device__ bool static_map<Key, Value, Scope, Allocator>::device_mutable_view::e
make_pair<Key, Value>(this->get_erased_key_sentinel(), this->get_empty_value_sentinel());

while (true) {
auto existing_key = current_slot->first.load(cuda::std::memory_order_relaxed);
auto existing_value = current_slot->second.load(cuda::std::memory_order_relaxed);
static_assert(sizeof(Key) == sizeof(atomic_key_type));
static_assert(sizeof(Value) == sizeof(atomic_mapped_type));
// TODO: Replace reinterpret_cast with atomic ref when available.
value_type slot_contents = *reinterpret_cast<value_type const*>(current_slot);
auto existing_key = slot_contents.first;
auto existing_value = slot_contents.second;

auto const slot_is_empty =
detail::bitwise_compare(existing_key, this->get_empty_key_sentinel());
Expand Down
10 changes: 4 additions & 6 deletions include/cuco/detail/static_map_kernels.cuh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, NVIDIA CORPORATION.
* Copyright (c) 2021-2022, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -165,16 +165,15 @@ template <std::size_t block_size,
__global__ void erase(
InputIt first, InputIt last, atomicT* num_successes, viewT view, Hash hash, KeyEqual key_equal)
{
typedef cub::BlockReduce<std::size_t, block_size> BlockReduce;
using BlockReduce = cub::BlockReduce<std::size_t, block_size>;
__shared__ typename BlockReduce::TempStorage temp_storage;
std::size_t thread_num_successes = 0;

auto tid = block_size * blockIdx.x + threadIdx.x;
auto it = first + tid;

while (it < last) {
auto k{*it};
if (view.erase(k, hash, key_equal)) { thread_num_successes++; }
if (view.erase(*it, hash, key_equal)) { thread_num_successes++; }
it += gridDim.x * block_size;
}

Expand Down Expand Up @@ -203,8 +202,7 @@ __global__ void erase(
auto it = first + tid / tile_size;

while (it < last) {
auto k{*it};
if (view.erase(tile, k, hash, key_equal) && tile.thread_rank() == 0) { thread_num_successes++; }
if (view.erase(tile, *it, hash, key_equal) and tile.thread_rank() == 0) { thread_num_successes++; }
it += (gridDim.x * block_size) / tile_size;
}

Expand Down
4 changes: 2 additions & 2 deletions include/cuco/static_map.cuh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020-2021, NVIDIA CORPORATION.
* Copyright (c) 2021-2022, NVIDIA CORPORATION.
PointKernel marked this conversation as resolved.
Show resolved Hide resolved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -373,7 +373,7 @@ class static_map {
using slot_type = slot_type;

Key empty_key_sentinel_{}; ///< Key value that represents an empty slot
Key erased_key_sentinel_{};
Key erased_key_sentinel_{}; ///< Key value that represents an erased slot
Value empty_value_sentinel_{}; ///< Initial Value of empty slot
pair_atomic_type* slots_{}; ///< Pointer to flat slots storage
std::size_t capacity_{}; ///< Total number of slots
Expand Down
18 changes: 4 additions & 14 deletions tests/static_map/erase_test.cu
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020-2021, NVIDIA CORPORATION.
* Copyright (c) 2021-2022, NVIDIA CORPORATION.
PointKernel marked this conversation as resolved.
Show resolved Hide resolved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -26,8 +26,9 @@ TEMPLATE_TEST_CASE_SIG("erase key", "", ((typename T), T), (int32_t), (int64_t))
using Key = T;
using Value = T;

unsigned long num_keys = 1'000'000;
cuco::static_map<Key, Value> map{num_keys * 1.1, -1, -1, -2};
constexpr std::size_t num_keys = 1'000'000;
constexpr std::size_t capacity = 1'100'000;
cuco::static_map<Key, Value> map{capacity, -1, -1, -2};

auto m_view = map.get_device_mutable_view();
auto view = map.get_device_view();
Expand Down Expand Up @@ -81,16 +82,5 @@ TEMPLATE_TEST_CASE_SIG("erase key", "", ((typename T), T), (int32_t), (int64_t))

map.erase(d_keys.begin() + num_keys / 2, d_keys.end());
REQUIRE(map.get_size() == 0);

map.insert(pairs_begin, pairs_begin + num_keys / 2);
map.insert(pairs_begin + num_keys / 2, pairs_begin + num_keys);

map.erase(d_keys.begin(), d_keys.begin() + num_keys / 2);

map.contains(d_keys.begin() + num_keys / 2, d_keys.end(), d_keys_exist.begin());

REQUIRE(cuco::test::all_of(d_keys_exist.begin(),
d_keys_exist.begin() + num_keys / 2,
[] __device__(const bool key_found) { return key_found; }));
}
}