From ff62cbaad13d4bd309de5d3dda8c7051325d5663 Mon Sep 17 00:00:00 2001 From: KunoiSayami Date: Thu, 11 Nov 2021 22:01:31 +0800 Subject: feat(cmake): Support cuda build Signed-off-by: KunoiSayami --- .gitignore | 3 +++ CMakeLists.txt | 14 ++++++++--- db/memtable.h | 7 +++--- db/skiplist.h | 2 +- db/skiplist_test.cc | 6 +++-- util/arena.cc | 66 ------------------------------------------------- util/arena.cu | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ util/arena.cuh | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++ util/arena.h | 71 ----------------------------------------------------- util/arena_test.cc | 66 ------------------------------------------------- util/arena_test.cu | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 226 insertions(+), 213 deletions(-) delete mode 100644 util/arena.cc create mode 100644 util/arena.cu create mode 100644 util/arena.cuh delete mode 100644 util/arena.h delete mode 100644 util/arena_test.cc create mode 100644 util/arena_test.cu diff --git a/.gitignore b/.gitignore index c4b2425..bd9d5c5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ # Build directory. build/ out/ + +cmake-* +.idea/ diff --git a/CMakeLists.txt b/CMakeLists.txt index f8285b8..ad9a488 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.9) # Keep the version below in sync with the one in db.h -project(leveldb VERSION 1.23.0 LANGUAGES C CXX) +project(leveldb VERSION 1.23.0 LANGUAGES C CXX CUDA) # C standard can be overridden when this is used as a sub-project. if(NOT CMAKE_C_STANDARD) @@ -76,6 +76,8 @@ else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -frtti") + # Test whether -Wthread-safety is available. See # https://clang.llvm.org/docs/ThreadSafetyAnalysis.html include(CheckCXXCompilerFlag) @@ -116,6 +118,9 @@ endif(BUILD_SHARED_LIBS) # Must be included before CMAKE_INSTALL_INCLUDEDIR is used. include(GNUInstallDirs) +set(CMAKE_CUDA_STANDARD 14) +find_package(CUDA REQUIRED) + add_library(leveldb "") target_sources(leveldb PRIVATE @@ -169,8 +174,8 @@ target_sources(leveldb "table/table.cc" "table/two_level_iterator.cc" "table/two_level_iterator.h" - "util/arena.cc" - "util/arena.h" + "util/arena.cu" + "util/arena.cuh" "util/bloom.cc" "util/cache.cc" "util/coding.cc" @@ -208,6 +213,7 @@ target_sources(leveldb "${LEVELDB_PUBLIC_INCLUDE_DIR}/table.h" "${LEVELDB_PUBLIC_INCLUDE_DIR}/write_batch.h" ) +set_target_properties(leveldb PROPERTIES CUDA_ARCHITECTURES "35;50;72") if (WIN32) target_sources(leveldb @@ -367,7 +373,7 @@ if(LEVELDB_BUILD_TESTS) leveldb_test("table/filter_block_test.cc") leveldb_test("table/table_test.cc") - leveldb_test("util/arena_test.cc") + leveldb_test("util/arena_test.cu") leveldb_test("util/bloom_test.cc") leveldb_test("util/cache_test.cc") leveldb_test("util/coding_test.cc") diff --git a/db/memtable.h b/db/memtable.h index 9d986b1..3d3444b 100644 --- a/db/memtable.h +++ b/db/memtable.h @@ -5,12 +5,13 @@ #ifndef STORAGE_LEVELDB_DB_MEMTABLE_H_ #define STORAGE_LEVELDB_DB_MEMTABLE_H_ -#include - #include "db/dbformat.h" #include "db/skiplist.h" +#include + #include "leveldb/db.h" -#include "util/arena.h" + +#include "util/arena.cuh" namespace leveldb { diff --git a/db/skiplist.h b/db/skiplist.h index a59b45b..13df778 100644 --- a/db/skiplist.h +++ b/db/skiplist.h @@ -31,7 +31,7 @@ #include #include -#include "util/arena.h" +#include "util/arena.cuh" #include "util/random.h" namespace leveldb { diff --git a/db/skiplist_test.cc b/db/skiplist_test.cc index 79a5b86..402cb47 100644 --- a/db/skiplist_test.cc +++ b/db/skiplist_test.cc @@ -7,15 +7,17 @@ #include #include -#include "gtest/gtest.h" #include "leveldb/env.h" + #include "port/port.h" #include "port/thread_annotations.h" -#include "util/arena.h" +#include "util/arena.cuh" #include "util/hash.h" #include "util/random.h" #include "util/testutil.h" +#include "gtest/gtest.h" + namespace leveldb { typedef uint64_t Key; diff --git a/util/arena.cc b/util/arena.cc deleted file mode 100644 index 46e3b2e..0000000 --- a/util/arena.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2011 The LevelDB Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. See the AUTHORS file for names of contributors. - -#include "util/arena.h" - -namespace leveldb { - -static const int kBlockSize = 4096; - -Arena::Arena() - : alloc_ptr_(nullptr), alloc_bytes_remaining_(0), memory_usage_(0) {} - -Arena::~Arena() { - for (size_t i = 0; i < blocks_.size(); i++) { - delete[] blocks_[i]; - } -} - -char* Arena::AllocateFallback(size_t bytes) { - if (bytes > kBlockSize / 4) { - // Object is more than a quarter of our block size. Allocate it separately - // to avoid wasting too much space in leftover bytes. - char* result = AllocateNewBlock(bytes); - return result; - } - - // We waste the remaining space in the current block. - alloc_ptr_ = AllocateNewBlock(kBlockSize); - alloc_bytes_remaining_ = kBlockSize; - - char* result = alloc_ptr_; - alloc_ptr_ += bytes; - alloc_bytes_remaining_ -= bytes; - return result; -} - -char* Arena::AllocateAligned(size_t bytes) { - const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8; - static_assert((align & (align - 1)) == 0, - "Pointer size should be a power of 2"); - size_t current_mod = reinterpret_cast(alloc_ptr_) & (align - 1); - size_t slop = (current_mod == 0 ? 0 : align - current_mod); - size_t needed = bytes + slop; - char* result; - if (needed <= alloc_bytes_remaining_) { - result = alloc_ptr_ + slop; - alloc_ptr_ += needed; - alloc_bytes_remaining_ -= needed; - } else { - // AllocateFallback always returned aligned memory - result = AllocateFallback(bytes); - } - assert((reinterpret_cast(result) & (align - 1)) == 0); - return result; -} - -char* Arena::AllocateNewBlock(size_t block_bytes) { - char* result = new char[block_bytes]; - blocks_.push_back(result); - memory_usage_.fetch_add(block_bytes + sizeof(char*), - std::memory_order_relaxed); - return result; -} - -} // namespace leveldb diff --git a/util/arena.cu b/util/arena.cu new file mode 100644 index 0000000..09ec6e6 --- /dev/null +++ b/util/arena.cu @@ -0,0 +1,67 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/arena.cuh" + +namespace leveldb { + +static const int kBlockSize = 4096; + +Arena::Arena() + : alloc_ptr_(nullptr), alloc_bytes_remaining_(0), memory_usage_(0) {} + +Arena::~Arena() { + for (size_t i = 0; i < blocks_.size(); i++) { + cudaFree(blocks_[i]); + } +} + +char* Arena::AllocateFallback(size_t bytes) { + if (bytes > kBlockSize / 4) { + // Object is more than a quarter of our block size. Allocate it separately + // to avoid wasting too much space in leftover bytes. + char* result = AllocateNewBlock(bytes); + return result; + } + + // We waste the remaining space in the current block. + alloc_ptr_ = AllocateNewBlock(kBlockSize); + alloc_bytes_remaining_ = kBlockSize; + + char* result = alloc_ptr_; + alloc_ptr_ += bytes; + alloc_bytes_remaining_ -= bytes; + return result; +} + +char* Arena::AllocateAligned(size_t bytes) { + const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8; + static_assert((align & (align - 1)) == 0, + "Pointer size should be a power of 2"); + size_t current_mod = reinterpret_cast(alloc_ptr_) & (align - 1); + size_t slop = (current_mod == 0 ? 0 : align - current_mod); + size_t needed = bytes + slop; + char* result; + if (needed <= alloc_bytes_remaining_) { + result = alloc_ptr_ + slop; + alloc_ptr_ += needed; + alloc_bytes_remaining_ -= needed; + } else { + // AllocateFallback always returned aligned memory + result = AllocateFallback(bytes); + } + assert((reinterpret_cast(result) & (align - 1)) == 0); + return result; +} + +char* Arena::AllocateNewBlock(size_t block_bytes) { + char* result = nullptr; + cudaMallocManaged((void **)&result, sizeof(char) * block_bytes); + blocks_.push_back(result); + memory_usage_.fetch_add(block_bytes + sizeof(char*), + std::memory_order_relaxed); + return result; +} + +} // namespace leveldb diff --git a/util/arena.cuh b/util/arena.cuh new file mode 100644 index 0000000..68fc55d --- /dev/null +++ b/util/arena.cuh @@ -0,0 +1,71 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_ARENA_H_ +#define STORAGE_LEVELDB_UTIL_ARENA_H_ + +#include +#include +#include +#include +#include + +namespace leveldb { + +class Arena { + public: + Arena(); + + Arena(const Arena&) = delete; + Arena& operator=(const Arena&) = delete; + + ~Arena(); + + // Return a pointer to a newly allocated memory block of "bytes" bytes. + char* Allocate(size_t bytes); + + // Allocate memory with the normal alignment guarantees provided by malloc. + char* AllocateAligned(size_t bytes); + + // Returns an estimate of the total memory usage of data allocated + // by the arena. + size_t MemoryUsage() const { + return memory_usage_.load(std::memory_order_relaxed); + } + + private: + char* AllocateFallback(size_t bytes); + char* AllocateNewBlock(size_t block_bytes); + + // Allocation state + char* alloc_ptr_; + size_t alloc_bytes_remaining_; + + // Array of new[] allocated memory blocks + std::vector blocks_; + + // Total memory usage of the arena. + // + // TODO(costan): This member is accessed via atomics, but the others are + // accessed without any locking. Is this OK? + std::atomic memory_usage_; +}; + +inline char* Arena::Allocate(size_t bytes) { + // The semantics of what to return are a bit messy if we allow + // 0-byte allocations, so we disallow them here (we don't need + // them for our internal use). + assert(bytes > 0); + if (bytes <= alloc_bytes_remaining_) { + char* result = alloc_ptr_; + alloc_ptr_ += bytes; + alloc_bytes_remaining_ -= bytes; + return result; + } + return AllocateFallback(bytes); +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_ARENA_H_ diff --git a/util/arena.h b/util/arena.h deleted file mode 100644 index 68fc55d..0000000 --- a/util/arena.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2011 The LevelDB Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. See the AUTHORS file for names of contributors. - -#ifndef STORAGE_LEVELDB_UTIL_ARENA_H_ -#define STORAGE_LEVELDB_UTIL_ARENA_H_ - -#include -#include -#include -#include -#include - -namespace leveldb { - -class Arena { - public: - Arena(); - - Arena(const Arena&) = delete; - Arena& operator=(const Arena&) = delete; - - ~Arena(); - - // Return a pointer to a newly allocated memory block of "bytes" bytes. - char* Allocate(size_t bytes); - - // Allocate memory with the normal alignment guarantees provided by malloc. - char* AllocateAligned(size_t bytes); - - // Returns an estimate of the total memory usage of data allocated - // by the arena. - size_t MemoryUsage() const { - return memory_usage_.load(std::memory_order_relaxed); - } - - private: - char* AllocateFallback(size_t bytes); - char* AllocateNewBlock(size_t block_bytes); - - // Allocation state - char* alloc_ptr_; - size_t alloc_bytes_remaining_; - - // Array of new[] allocated memory blocks - std::vector blocks_; - - // Total memory usage of the arena. - // - // TODO(costan): This member is accessed via atomics, but the others are - // accessed without any locking. Is this OK? - std::atomic memory_usage_; -}; - -inline char* Arena::Allocate(size_t bytes) { - // The semantics of what to return are a bit messy if we allow - // 0-byte allocations, so we disallow them here (we don't need - // them for our internal use). - assert(bytes > 0); - if (bytes <= alloc_bytes_remaining_) { - char* result = alloc_ptr_; - alloc_ptr_ += bytes; - alloc_bytes_remaining_ -= bytes; - return result; - } - return AllocateFallback(bytes); -} - -} // namespace leveldb - -#endif // STORAGE_LEVELDB_UTIL_ARENA_H_ diff --git a/util/arena_test.cc b/util/arena_test.cc deleted file mode 100644 index 90226fe..0000000 --- a/util/arena_test.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2011 The LevelDB Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. See the AUTHORS file for names of contributors. - -#include "util/arena.h" - -#include "gtest/gtest.h" -#include "util/random.h" - -namespace leveldb { - -TEST(ArenaTest, Empty) { Arena arena; } - -TEST(ArenaTest, Simple) { - std::vector> allocated; - Arena arena; - const int N = 100000; - size_t bytes = 0; - Random rnd(301); - for (int i = 0; i < N; i++) { - size_t s; - if (i % (N / 10) == 0) { - s = i; - } else { - s = rnd.OneIn(4000) - ? rnd.Uniform(6000) - : (rnd.OneIn(10) ? rnd.Uniform(100) : rnd.Uniform(20)); - } - if (s == 0) { - // Our arena disallows size 0 allocations. - s = 1; - } - char* r; - if (rnd.OneIn(10)) { - r = arena.AllocateAligned(s); - } else { - r = arena.Allocate(s); - } - - for (size_t b = 0; b < s; b++) { - // Fill the "i"th allocation with a known bit pattern - r[b] = i % 256; - } - bytes += s; - allocated.push_back(std::make_pair(s, r)); - ASSERT_GE(arena.MemoryUsage(), bytes); - if (i > N / 10) { - ASSERT_LE(arena.MemoryUsage(), bytes * 1.10); - } - } - for (size_t i = 0; i < allocated.size(); i++) { - size_t num_bytes = allocated[i].first; - const char* p = allocated[i].second; - for (size_t b = 0; b < num_bytes; b++) { - // Check the "i"th allocation for the known bit pattern - ASSERT_EQ(int(p[b]) & 0xff, i % 256); - } - } -} - -} // namespace leveldb - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/util/arena_test.cu b/util/arena_test.cu new file mode 100644 index 0000000..9b87785 --- /dev/null +++ b/util/arena_test.cu @@ -0,0 +1,66 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/arena.cuh" +#include "util/random.h" + +#include "gtest/gtest.h" + +namespace leveldb { + +TEST(ArenaTest, Empty) { Arena arena; } + +TEST(ArenaTest, Simple) { + std::vector> allocated; + Arena arena; + const int N = 100000; + size_t bytes = 0; + Random rnd(301); + for (int i = 0; i < N; i++) { + size_t s; + if (i % (N / 10) == 0) { + s = i; + } else { + s = rnd.OneIn(4000) + ? rnd.Uniform(6000) + : (rnd.OneIn(10) ? rnd.Uniform(100) : rnd.Uniform(20)); + } + if (s == 0) { + // Our arena disallows size 0 allocations. + s = 1; + } + char* r; + if (rnd.OneIn(10)) { + r = arena.AllocateAligned(s); + } else { + r = arena.Allocate(s); + } + + for (size_t b = 0; b < s; b++) { + // Fill the "i"th allocation with a known bit pattern + r[b] = i % 256; + } + bytes += s; + allocated.push_back(std::make_pair(s, r)); + ASSERT_GE(arena.MemoryUsage(), bytes); + if (i > N / 10) { + ASSERT_LE(arena.MemoryUsage(), bytes * 1.10); + } + } + for (size_t i = 0; i < allocated.size(); i++) { + size_t num_bytes = allocated[i].first; + const char* p = allocated[i].second; + for (size_t b = 0; b < num_bytes; b++) { + // Check the "i"th allocation for the known bit pattern + ASSERT_EQ(int(p[b]) & 0xff, i % 256); + } + } +} + +} // namespace leveldb + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} -- cgit v1.3.1