]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
crimson/onode-staged-tree: implement benchmark tool
authorYingxin Cheng <yingxin.cheng@intel.com>
Thu, 15 Oct 2020 02:02:19 +0000 (10:02 +0800)
committerYingxin Cheng <yingxin.cheng@intel.com>
Tue, 1 Dec 2020 04:50:54 +0000 (12:50 +0800)
Signed-off-by: Yingxin Cheng <yingxin.cheng@intel.com>
src/crimson/os/seastore/onode_manager/staged-fltree/tree_utils.h [new file with mode: 0644]
src/test/crimson/seastore/onode_tree/test_staged_fltree.cc
src/tools/crimson/CMakeLists.txt
src/tools/crimson/perf_staged_fltree.cc [new file with mode: 0644]

diff --git a/src/crimson/os/seastore/onode_manager/staged-fltree/tree_utils.h b/src/crimson/os/seastore/onode_manager/staged-fltree/tree_utils.h
new file mode 100644 (file)
index 0000000..23c1723
--- /dev/null
@@ -0,0 +1,331 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <cassert>
+#include <cstring>
+#include <random>
+#include <string>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+#include "crimson/common/log.h"
+#include "stages/key_layout.h"
+#include "tree.h"
+
+namespace crimson::os::seastore::onode {
+
+ghobject_t make_ghobj(
+    int shard, unsigned pool, unsigned crush,
+    std::string ns, std::string oid, unsigned snap, unsigned gen) {
+  assert(shard <= std::numeric_limits<shard_t>::max());
+  assert(shard >= std::numeric_limits<shard_t>::min());
+  assert(pool <= std::numeric_limits<pool_t>::max());
+  assert(crush <= std::numeric_limits<crush_hash_t>::max());
+  assert(snap <= std::numeric_limits<snap_t>::max());
+  assert(gen <= std::numeric_limits<gen_t>::max());
+  ghobject_t ghobj;
+  ghobj.shard_id.id = shard;
+  ghobj.hobj.pool = pool;
+  ghobj.hobj.set_hash(crush);
+  ghobj.hobj.nspace = ns;
+  ghobj.hobj.oid.name = oid;
+  ghobj.hobj.snap = snap;
+  ghobj.generation = gen;
+  return ghobj;
+}
+
+class Onodes {
+ public:
+  Onodes(size_t n) {
+    for (size_t i = 1; i <= n; ++i) {
+      auto p_onode = &create(i * 8);
+      onodes.push_back(p_onode);
+    }
+  }
+
+  Onodes(std::vector<size_t> sizes) {
+    for (auto& size : sizes) {
+      auto p_onode = &create(size);
+      onodes.push_back(p_onode);
+    }
+  }
+
+  ~Onodes() {
+    std::for_each(tracked_onodes.begin(), tracked_onodes.end(),
+                  [] (onode_t* onode) {
+      std::free(onode);
+    });
+  }
+
+  const onode_t& create(size_t size) {
+    ceph_assert(size >= sizeof(onode_t) + sizeof(uint32_t));
+    uint32_t target = size * 137;
+    auto p_mem = (char*)std::malloc(size);
+    auto p_onode = (onode_t*)p_mem;
+    tracked_onodes.push_back(p_onode);
+    p_onode->size = size;
+    p_onode->id = id++;
+    p_mem += (size - sizeof(uint32_t));
+    std::memcpy(p_mem, &target, sizeof(uint32_t));
+    validate(*p_onode);
+    return *p_onode;
+  }
+
+  const onode_t& pick() const {
+    auto index = rd() % onodes.size();
+    return *onodes[index];
+  }
+
+  const onode_t& pick_largest() const {
+    return *onodes[onodes.size() - 1];
+  }
+
+  static void validate_cursor(
+      const Btree::Cursor& cursor, const ghobject_t& key, const onode_t& onode) {
+    ceph_assert(!cursor.is_end());
+    ceph_assert(cursor.get_ghobj() == key);
+    ceph_assert(cursor.value());
+    ceph_assert(cursor.value() != &onode);
+    ceph_assert(*cursor.value() == onode);
+    validate(*cursor.value());
+  }
+
+ private:
+  static void validate(const onode_t& node) {
+    auto p_target = (const char*)&node + node.size - sizeof(uint32_t);
+    uint32_t target;
+    std::memcpy(&target, p_target, sizeof(uint32_t));
+    ceph_assert(target == node.size * 137);
+  }
+
+  uint16_t id = 0;
+  mutable std::random_device rd;
+  std::vector<const onode_t*> onodes;
+  std::vector<onode_t*> tracked_onodes;
+};
+
+class KVPool {
+  struct kv_conf_t {
+    unsigned index2;
+    unsigned index1;
+    unsigned index0;
+    size_t ns_size;
+    size_t oid_size;
+    const onode_t* p_value;
+
+    ghobject_t get_ghobj() const {
+      assert(index1 < 10);
+      std::ostringstream os_ns;
+      os_ns << "ns" << index1;
+      unsigned current_size = (unsigned)os_ns.tellp();
+      assert(ns_size >= current_size);
+      os_ns << std::string(ns_size - current_size, '_');
+
+      std::ostringstream os_oid;
+      os_oid << "oid" << index1;
+      current_size = (unsigned)os_oid.tellp();
+      assert(oid_size >= current_size);
+      os_oid << std::string(oid_size - current_size, '_');
+
+      return make_ghobj(index2, index2, index2,
+                        os_ns.str(), os_oid.str(), index0, index0);
+    }
+  };
+  using kv_vector_t = std::vector<kv_conf_t>;
+
+ public:
+  using kv_t = std::pair<ghobject_t, const onode_t*>;
+
+  KVPool(const std::vector<size_t>& str_sizes,
+         const std::vector<size_t>& onode_sizes,
+         const std::pair<unsigned, unsigned>& range2,
+         const std::pair<unsigned, unsigned>& range1,
+         const std::pair<unsigned, unsigned>& range0)
+      : str_sizes{str_sizes}, onodes{onode_sizes} {
+    ceph_assert(range2.first < range2.second);
+    ceph_assert(range2.second - 1 <= (unsigned)std::numeric_limits<shard_t>::max());
+    ceph_assert(range2.second - 1 <= std::numeric_limits<pool_t>::max());
+    ceph_assert(range2.second - 1 <= std::numeric_limits<crush_hash_t>::max());
+    ceph_assert(range1.first < range1.second);
+    ceph_assert(range1.second - 1 <= 9);
+    ceph_assert(range0.first < range0.second);
+    ceph_assert(range0.second - 1 <= std::numeric_limits<snap_t>::max());
+    ceph_assert(range0.second - 1 <= std::numeric_limits<gen_t>::max());
+    std::random_device rd;
+    for (unsigned i = range2.first; i < range2.second; ++i) {
+      for (unsigned j = range1.first; j < range1.second; ++j) {
+        auto ns_size = (unsigned)str_sizes[rd() % str_sizes.size()];
+        auto oid_size = (unsigned)str_sizes[rd() % str_sizes.size()];
+        for (unsigned k = range0.first; k < range0.second; ++k) {
+          kvs.emplace_back(kv_conf_t{i, j, k, ns_size, oid_size, &onodes.pick()});
+        }
+      }
+    }
+    random_kvs = kvs;
+    std::random_shuffle(random_kvs.begin(), random_kvs.end());
+  }
+
+  class iterator_t {
+   public:
+    iterator_t() = default;
+    iterator_t(const iterator_t&) = default;
+    iterator_t(iterator_t&&) = default;
+    iterator_t& operator=(const iterator_t&) = default;
+    iterator_t& operator=(iterator_t&&) = default;
+
+    kv_t get_kv() const {
+      assert(!is_end());
+      auto& conf = (*p_kvs)[i];
+      return std::make_pair(conf.get_ghobj(), conf.p_value);
+    }
+    bool is_end() const { return !p_kvs || i >= p_kvs->size(); }
+    size_t index() const { return i; }
+
+    iterator_t& operator++() {
+      assert(!is_end());
+      ++i;
+      return *this;
+    }
+
+    iterator_t operator++(int) {
+      iterator_t tmp = *this;
+      ++*this;
+      return tmp;
+    }
+
+   private:
+    iterator_t(const kv_vector_t& kvs) : p_kvs{&kvs} {}
+
+    const kv_vector_t* p_kvs = nullptr;
+    size_t i = 0;
+    friend class KVPool;
+  };
+
+  iterator_t begin() const {
+    return iterator_t(kvs);
+  }
+
+  iterator_t random_begin() const {
+    return iterator_t(random_kvs);
+  }
+
+  size_t size() const {
+    return kvs.size();
+  }
+
+ private:
+  std::vector<size_t> str_sizes;
+  Onodes onodes;
+  kv_vector_t kvs;
+  kv_vector_t random_kvs;
+};
+
+template <bool TRACK>
+class TreeBuilder {
+ public:
+  using ertr = Btree::btree_ertr;
+  template <class... ValuesT>
+  using future = ertr::future<ValuesT...>;
+
+  TreeBuilder(KVPool& kvs, NodeExtentManagerURef&& nm)
+    : kvs{kvs}, ref_t{make_transaction()}, t{*ref_t}, tree{std::move(nm)} {}
+
+  future<> bootstrap() {
+    return tree.mkfs(t);
+  }
+
+  future<> run() {
+    std::ostringstream oss;
+#ifndef NDEBUG
+    oss << "debug on, ";
+#else
+    oss << "debug off, ";
+#endif
+    if constexpr (TRACK) {
+      oss << "track on";
+    } else {
+      oss << "track off";
+    }
+    kv_iter = kvs.random_begin();
+    logger().warn("start inserting {} kvs ({}) ...", kvs.size(), oss.str());
+    auto start_time = mono_clock::now();
+    return crimson::do_until([this]() -> future<bool> {
+      if (kv_iter.is_end()) {
+        return ertr::make_ready_future<bool>(true);
+      }
+      auto [key, p_value] = kv_iter.get_kv();
+      logger().debug("[{}] {} -> {}", kv_iter.index(), key_hobj_t{key}, *p_value);
+      return tree.insert(t, key, *p_value).safe_then([this](auto ret) {
+        auto& [cursor, success] = ret;
+        assert(success == true);
+        if constexpr (TRACK) {
+          cursors.emplace_back(cursor);
+        }
+#ifndef NDEBUG
+        auto [key, p_value] = kv_iter.get_kv();
+        Onodes::validate_cursor(cursor, key, *p_value);
+        return tree.lower_bound(t, key).safe_then([this, cursor](auto cursor_) {
+          auto [key, p_value] = kv_iter.get_kv();
+          ceph_assert(cursor_.get_ghobj() == key);
+          ceph_assert(cursor_.value() == cursor.value());
+          ++kv_iter;
+          return ertr::make_ready_future<bool>(false);
+        });
+#else
+        ++kv_iter;
+        return ertr::make_ready_future<bool>(false);
+#endif
+      });
+    }).safe_then([this, start_time] {
+      std::chrono::duration<double> duration = mono_clock::now() - start_time;
+      logger().warn("Insert done! {}s", duration.count());
+      return tree.get_stats_slow(t);
+    }).safe_then([this](auto stats) {
+      logger().warn("{}", stats);
+      if (!cursors.empty()) {
+        logger().info("Verifing tracked cursors ...");
+        kv_iter = kvs.random_begin();
+        return seastar::do_with(
+            cursors.begin(), [this](auto& c_iter) {
+          return crimson::do_until([this, &c_iter]() -> future<bool> {
+            if (kv_iter.is_end()) {
+              logger().info("Verify done!");
+              return ertr::make_ready_future<bool>(true);
+            }
+            assert(c_iter != cursors.end());
+            auto [k, v] = kv_iter.get_kv();
+            // validate values in tree keep intact
+            return tree.lower_bound(t, k).safe_then([this, &c_iter](auto cursor) {
+              auto [k, v] = kv_iter.get_kv();
+              Onodes::validate_cursor(cursor, k, *v);
+              // validate values in cursors keep intact
+              Onodes::validate_cursor(*c_iter, k, *v);
+              ++kv_iter;
+              ++c_iter;
+              return ertr::make_ready_future<bool>(false);
+            });
+          });
+        });
+      } else {
+        return ertr::now();
+      }
+    });
+  }
+
+ private:
+  static seastar::logger& logger() {
+    return crimson::get_logger(ceph_subsys_filestore);
+  }
+
+  KVPool& kvs;
+  TransactionRef ref_t;
+  Transaction& t;
+  Btree tree;
+  KVPool::iterator_t kv_iter;
+  std::vector<Btree::Cursor> cursors;
+};
+
+}
index 13ddab7f86a9b9de8e47d0a861fc5d56e23e3a29..29341720689c2963ca37ad081ddce0aa89d6163f 100644 (file)
@@ -4,7 +4,6 @@
 #include <array>
 #include <cstring>
 #include <memory>
-#include <random>
 #include <set>
 #include <sstream>
 #include <vector>
 #include "crimson/os/seastore/onode_manager/staged-fltree/node_extent_manager.h"
 #include "crimson/os/seastore/onode_manager/staged-fltree/node_layout.h"
 #include "crimson/os/seastore/onode_manager/staged-fltree/tree.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/tree_utils.h"
 #include "crimson/os/seastore/segment_manager.h"
 #include "crimson/os/seastore/transaction_manager.h"
 
 #include "test/crimson/gtest_seastar.h"
 
 using namespace crimson::os::seastore::onode;
-using crimson::os::seastore::Cache;
-using crimson::os::seastore::Journal;
-using crimson::os::seastore::LBAManagerRef;
-using crimson::os::seastore::lba_manager::create_lba_manager;
-using crimson::os::seastore::SegmentCleaner;
-using crimson::os::seastore::SegmentManager;
-using crimson::os::seastore::segment_manager::create_ephemeral;
-using crimson::os::seastore::segment_manager::DEFAULT_TEST_EPHEMERAL;
+namespace seastore = crimson::os::seastore;
+using seastore::lba_manager::create_lba_manager;
+using seastore::segment_manager::create_ephemeral;
+using seastore::segment_manager::DEFAULT_TEST_EPHEMERAL;
+using sc_config_t = seastore::SegmentCleaner::config_t;
 
 namespace {
   [[maybe_unused]] seastar::logger& logger() {
     return crimson::get_logger(ceph_subsys_test);
   }
 
-  ghobject_t make_ghobj(
-      shard_t shard, pool_t pool, crush_hash_t crush,
-      std::string ns, std::string oid, snap_t snap, gen_t gen) {
-    ghobject_t ghobj;
-    ghobj.shard_id.id = shard;
-    ghobj.hobj.pool = pool;
-    ghobj.hobj.set_hash(crush);
-    ghobj.hobj.nspace = ns;
-    ghobj.hobj.oid.name = oid;
-    ghobj.hobj.snap = snap;
-    ghobj.generation = gen;
-    return ghobj;
-  }
-
   // return a key_view_t and its underlying memory buffer.
   // the buffer needs to be freed manually.
   std::pair<key_view_t, void*> build_key_view(const ghobject_t& hobj) {
@@ -78,76 +61,6 @@ namespace {
 
     return {key_view, p_mem};
   }
-
-  class Onodes {
-   public:
-    Onodes(size_t n) {
-      for (size_t i = 1; i <= n; ++i) {
-        auto p_onode = &create(i * 8);
-        onodes.push_back(p_onode);
-      }
-    }
-
-    Onodes(std::vector<size_t> sizes) {
-      for (auto& size : sizes) {
-        auto p_onode = &create(size);
-        onodes.push_back(p_onode);
-      }
-    }
-
-    ~Onodes() {
-      std::for_each(tracked_onodes.begin(), tracked_onodes.end(),
-                    [] (onode_t* onode) {
-        std::free(onode);
-      });
-    }
-
-    const onode_t& create(size_t size) {
-      assert(size >= sizeof(onode_t) + sizeof(uint32_t));
-      uint32_t target = size * 137;
-      auto p_mem = (char*)std::malloc(size);
-      auto p_onode = (onode_t*)p_mem;
-      tracked_onodes.push_back(p_onode);
-      p_onode->size = size;
-      p_onode->id = id++;
-      p_mem += (size - sizeof(uint32_t));
-      std::memcpy(p_mem, &target, sizeof(uint32_t));
-      validate(*p_onode);
-      return *p_onode;
-    }
-
-    const onode_t& pick() const {
-      auto index = rd() % onodes.size();
-      return *onodes[index];
-    }
-
-    const onode_t& pick_largest() const {
-      return *onodes[onodes.size() - 1];
-    }
-
-    static void validate_cursor(
-        const Btree::Cursor& cursor, const ghobject_t& key, const onode_t& onode) {
-      assert(!cursor.is_end());
-      assert(cursor.get_ghobj() == key);
-      assert(cursor.value());
-      assert(cursor.value() != &onode);
-      assert(*cursor.value() == onode);
-      validate(*cursor.value());
-    }
-
-   private:
-    static void validate(const onode_t& node) {
-      auto p_target = (const char*)&node + node.size - sizeof(uint32_t);
-      uint32_t target;
-      std::memcpy(&target, p_target, sizeof(uint32_t));
-      assert(target == node.size * 137);
-    }
-
-    uint16_t id = 0;
-    mutable std::random_device rd;
-    std::vector<const onode_t*> onodes;
-    std::vector<onode_t*> tracked_onodes;
-  };
 }
 
 struct a_basic_test_t : public seastar_test_suite_t {};
@@ -433,6 +346,7 @@ static std::set<ghobject_t> build_key_set(
     std::pair<unsigned, unsigned> range_0,
     std::string padding = "",
     bool is_internal = false) {
+  assert(range_1.second <= 10);
   std::set<ghobject_t> ret;
   ghobject_t key;
   for (unsigned i = range_2.first; i < range_2.second; ++i) {
@@ -1237,143 +1151,33 @@ TEST_F(c_dummy_test_t, 5_split_internal_node)
   });
 }
 
-class KVPool {
-  struct kv_conf_t {
-    unsigned index_2;
-    unsigned index_1;
-    unsigned index_0;
-    size_t ns_size;
-    size_t oid_size;
-    const onode_t* p_value;
-
-    ghobject_t get_ghobj() const {
-      assert(index_1 < 10);
-      std::ostringstream os_ns;
-      os_ns << "ns" << index_1;
-      unsigned current_size = (unsigned)os_ns.tellp();
-      assert(ns_size >= current_size);
-      os_ns << std::string(ns_size - current_size, '_');
-
-      std::ostringstream os_oid;
-      os_oid << "oid" << index_1;
-      current_size = (unsigned)os_oid.tellp();
-      assert(oid_size >= current_size);
-      os_oid << std::string(oid_size - current_size, '_');
-
-      return make_ghobj(index_2, index_2, index_2,
-                        os_ns.str(), os_oid.str(), index_0, index_0);
-    }
-  };
-  using kv_vector_t = std::vector<kv_conf_t>;
-
- public:
-  using kv_t = std::pair<ghobject_t, const onode_t*>;
-
-  KVPool(std::vector<size_t> str_sizes,
-         std::vector<size_t> onode_sizes,
-         std::pair<unsigned, unsigned> range_2,
-         std::pair<unsigned, unsigned> range_1,
-         std::pair<unsigned, unsigned> range_0)
-      : str_sizes{str_sizes}, onodes{onode_sizes} {
-    std::random_device rd;
-    for (unsigned i = range_2.first; i < range_2.second; ++i) {
-      for (unsigned j = range_1.first; j < range_1.second; ++j) {
-        auto ns_size = (unsigned)str_sizes[rd() % str_sizes.size()];
-        auto oid_size = (unsigned)str_sizes[rd() % str_sizes.size()];
-        for (unsigned k = range_0.first; k < range_0.second; ++k) {
-          kvs.emplace_back(kv_conf_t{i, j, k, ns_size, oid_size, &onodes.pick()});
-        }
-      }
-    }
-    random_kvs = kvs;
-    std::random_shuffle(random_kvs.begin(), random_kvs.end());
-  }
-
-  class iterator_t {
-   public:
-    kv_t get_kv() const {
-      assert(!is_end());
-      auto& conf = (*p_kvs)[i];
-      return std::make_pair(conf.get_ghobj(), conf.p_value);
-    }
-    bool is_end() const { return i >= p_kvs->size(); }
-    size_t index() const { return i; }
-
-    iterator_t& operator++() {
-      assert(!is_end());
-      ++i;
-      return *this;
-    }
-
-    iterator_t operator++(int) {
-      iterator_t tmp = *this;
-      ++*this;
-      return tmp;
-    }
-
-   private:
-    iterator_t(const kv_vector_t& kvs) : p_kvs{&kvs} {}
-
-    const kv_vector_t* p_kvs;
-    size_t i = 0;
-    friend class KVPool;
-  };
-
-  iterator_t begin() const {
-    return iterator_t(kvs);
-  }
-
-  iterator_t random_begin() const {
-    return iterator_t(random_kvs);
-  }
-
-  size_t size() const {
-    return kvs.size();
-  }
-
-  kv_t make_kv(unsigned index_2, unsigned index_1, unsigned index_0,
-               size_t ns_size, size_t oid_size, size_t onode_size) {
-    auto& onode = onodes.create(onode_size);
-    kv_conf_t conf{index_2, index_1, index_0, ns_size, oid_size, &onode};
-    return std::make_pair(conf.get_ghobj(), &onode);
-  }
-
- private:
-  std::vector<size_t> str_sizes;
-  Onodes onodes;
-  kv_vector_t kvs;
-  kv_vector_t random_kvs;
-};
-
 struct d_seastore_tree_test_t : public seastar_test_suite_t {
-  std::unique_ptr<SegmentManager> segment_manager;
-  SegmentCleaner segment_cleaner;
-  Journal journal;
-  Cache cache;
-  LBAManagerRef lba_manager;
-  TransactionManager tm;
-  NodeExtentManagerURef moved_nm;
-  TransactionRef ref_t;
-  Transaction& t;
-  Btree tree;
+  std::unique_ptr<seastore::SegmentManager> segment_manager;
+  seastore::SegmentCleaner segment_cleaner;
+  seastore::Journal journal;
+  seastore::Cache cache;
+  seastore::LBAManagerRef lba_manager;
+  seastore::TransactionManager tm;
+  KVPool kvs;
+  TreeBuilder<true> tree;
 
   d_seastore_tree_test_t()
     : segment_manager(create_ephemeral(DEFAULT_TEST_EPHEMERAL)),
-      segment_cleaner(SegmentCleaner::config_t::default_from_segment_manager(
-                        *segment_manager)),
+      segment_cleaner(sc_config_t::default_from_segment_manager(*segment_manager)),
       journal(*segment_manager),
       cache(*segment_manager),
       lba_manager(create_lba_manager(*segment_manager, cache)),
       tm(*segment_manager, segment_cleaner, journal, cache, *lba_manager),
-      moved_nm{
+      kvs({8, 11, 64, 256, 301, 320},
+          {8, 16, 128, 512, 576, 640},
+          {0, 32}, {0, 10}, {0, 4}),
+      tree{kvs,
 #if 0
-        NodeExtentManager::create_dummy(false)},
+        NodeExtentManager::create_dummy(false)
 #else
-        NodeExtentManager::create_seastore(tm)},
+        NodeExtentManager::create_seastore(tm)
 #endif
-      ref_t{make_transaction()},
-      t{*ref_t},
-      tree{std::move(moved_nm)} {
+      } {
     journal.set_segment_provider(&segment_cleaner);
     segment_cleaner.set_extent_callback(&tm);
   }
@@ -1384,7 +1188,7 @@ struct d_seastore_tree_test_t : public seastar_test_suite_t {
     }).safe_then([this] {
       return tm.mount();
     }).safe_then([this] {
-      return tree.mkfs(t);
+      return tree.bootstrap();
     }).handle_error(
       crimson::ct_error::all_same_way([] {
         ASSERT_FALSE("Unable to mkfs");
@@ -1403,45 +1207,11 @@ struct d_seastore_tree_test_t : public seastar_test_suite_t {
 
 TEST_F(d_seastore_tree_test_t, 6_random_insert_leaf_node)
 {
-  run_async([this] {
-    KVPool kvs({8, 11, 64, 256, 301, 320},
-               {8, 16, 128, 512, 576, 640},
-               {0, 32}, {0, 10}, {0, 4});
-    auto iter = kvs.random_begin();
-    std::vector<Btree::Cursor> cursors;
-    while (!iter.is_end()) {
-      auto [key, p_value] = iter.get_kv();
-      logger().info("[{}] {} -> {}", iter.index(), key_hobj_t{key}, *p_value);
-      auto [cursor, success] = tree.insert(t, key, *p_value).unsafe_get0();
-      assert(success == true);
-#if 1 // validate cursor tracking
-      cursors.emplace_back(cursor);
-#endif
-      Onodes::validate_cursor(cursor, key, *p_value);
-      auto cursor_ = tree.lower_bound(t, key).unsafe_get0();
-      assert(cursor_.get_ghobj() == key);
-      assert(cursor_.value() == cursor.value());
-      ++iter;
-    }
-
-    logger().info("Insert done!");
-    logger().info("{}", tree.get_stats_slow(t).unsafe_get0());
-
-    if (!cursors.empty()) {
-      auto kv_iter = kvs.random_begin();
-      auto c_iter = cursors.begin();
-      while (!kv_iter.is_end()) {
-        assert(c_iter != cursors.end());
-        auto [k, v] = kv_iter.get_kv();
-        // validate values in tree keep intact
-        auto cursor = tree.lower_bound(t, k).unsafe_get0();
-        Onodes::validate_cursor(cursor, k, *v);
-        // validate values in cursors keep intact
-        Onodes::validate_cursor(*c_iter, k, *v);
-
-        ++kv_iter;
-        ++c_iter;
-      }
-    }
+  run([this] {
+    return tree.run().handle_error(
+      crimson::ct_error::all_same_way([] {
+        ASSERT_FALSE("Test failed");
+      })
+    );
   });
 }
index fb4baf7a94aea4a9e9f4ba3a9e8e0cffebc943cd..94fc4a636f5c593fb45536c69d26ab8dd7f3a8a5 100644 (file)
@@ -3,3 +3,6 @@ target_link_libraries(perf_crimson_msgr crimson)
 
 add_executable(perf_async_msgr perf_async_msgr.cc)
 target_link_libraries(perf_async_msgr ceph-common global ${ALLOC_LIBS})
+
+add_executable(perf_staged_fltree perf_staged_fltree.cc)
+target_link_libraries(perf_staged_fltree crimson-seastore)
diff --git a/src/tools/crimson/perf_staged_fltree.cc b/src/tools/crimson/perf_staged_fltree.cc
new file mode 100644 (file)
index 0000000..9b3de74
--- /dev/null
@@ -0,0 +1,163 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <boost/program_options.hpp>
+
+#include <seastar/core/app-template.hh>
+#include <seastar/core/thread.hh>
+
+#include "crimson/os/seastore/onode_manager/staged-fltree/tree_utils.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/node_extent_manager.h"
+#include "crimson/os/seastore/segment_manager.h"
+#include "crimson/os/seastore/transaction_manager.h"
+
+using namespace crimson::os::seastore::onode;
+namespace bpo = boost::program_options;
+namespace seastore = crimson::os::seastore;
+using seastore::lba_manager::create_lba_manager;
+using seastore::segment_manager::create_ephemeral;
+using seastore::segment_manager::DEFAULT_TEST_EPHEMERAL;
+using sc_config_t = seastore::SegmentCleaner::config_t;
+
+template <bool TRACK>
+class PerfTree {
+ public:
+  PerfTree(bool is_dummy,
+           const std::vector<size_t>& str_sizes,
+           const std::vector<size_t>& onode_sizes,
+           const std::pair<int, int>& range2,
+           const std::pair<unsigned, unsigned>& range1,
+           const std::pair<unsigned, unsigned>& range0)
+    : is_dummy{is_dummy},
+      segment_manager(create_ephemeral(DEFAULT_TEST_EPHEMERAL)),
+      segment_cleaner(sc_config_t::default_from_segment_manager(*segment_manager)),
+      journal(*segment_manager),
+      cache(*segment_manager),
+      lba_manager(create_lba_manager(*segment_manager, cache)),
+      tm(*segment_manager, segment_cleaner, journal, cache, *lba_manager),
+      kvs(str_sizes, onode_sizes, range2, range1, range0),
+      tree{kvs, is_dummy ? NodeExtentManager::create_dummy(true)
+                         : NodeExtentManager::create_seastore(tm)} {
+    journal.set_segment_provider(&segment_cleaner);
+    segment_cleaner.set_extent_callback(&tm);
+  }
+
+  seastar::future<> run() {
+    return start().then([this] {
+      return tree.run().handle_error(
+        crimson::ct_error::all_same_way([] {
+          ceph_abort("runtime error");
+        })
+      );
+    }).then([this] {
+      return stop();
+    });
+  }
+
+ private:
+  seastar::future<> start() {
+    if (is_dummy) {
+      return tree.bootstrap().handle_error(
+        crimson::ct_error::all_same_way([] {
+          ceph_abort("Unable to mkfs");
+        })
+      );
+    } else {
+      return segment_manager->init().safe_then([this] {
+        return tm.mkfs();
+      }).safe_then([this] {
+        return tm.mount();
+      }).safe_then([this] {
+        return tree.bootstrap();
+      }).handle_error(
+        crimson::ct_error::all_same_way([] {
+          ceph_abort("Unable to mkfs");
+        })
+      );
+    }
+  }
+
+  seastar::future<> stop() {
+    if (is_dummy) {
+      return seastar::now();
+    } else {
+      return tm.close().handle_error(
+        crimson::ct_error::all_same_way([] {
+          ceph_abort("Unable to close");
+        })
+      );
+    }
+  }
+
+  bool is_dummy;
+  std::unique_ptr<seastore::SegmentManager> segment_manager;
+  seastore::SegmentCleaner segment_cleaner;
+  seastore::Journal journal;
+  seastore::Cache cache;
+  seastore::LBAManagerRef lba_manager;
+  seastore::TransactionManager tm;
+  KVPool kvs;
+  TreeBuilder<TRACK> tree;
+};
+
+template <bool TRACK>
+seastar::future<> run(const bpo::variables_map& config) {
+  return seastar::async([&config]{
+    auto backend = config["backend"].as<std::string>();
+    bool is_dummy;
+    if (backend == "dummy") {
+      is_dummy = true;
+    } else if (backend == "seastore") {
+      is_dummy = false;
+    } else {
+      ceph_abort(false && "invalid backend");
+    }
+    auto str_sizes = config["str-sizes"].as<std::vector<size_t>>();
+    auto onode_sizes = config["onode-sizes"].as<std::vector<size_t>>();
+    auto range2 = config["range2"].as<std::vector<int>>();
+    ceph_assert(range2.size() == 2);
+    auto range1 = config["range1"].as<std::vector<unsigned>>();
+    ceph_assert(range1.size() == 2);
+    auto range0 = config["range0"].as<std::vector<unsigned>>();
+    ceph_assert(range0.size() == 2);
+
+    PerfTree<TRACK> perf{is_dummy, str_sizes, onode_sizes,
+      {range2[0], range2[1]}, {range1[0], range1[1]}, {range0[0], range0[1]}};
+    perf.run().get0();
+  });
+}
+
+
+int main(int argc, char** argv)
+{
+  seastar::app_template app;
+  app.add_options()
+    ("backend", bpo::value<std::string>()->default_value("dummy"),
+     "tree backend: dummy, seastore")
+    ("tracked", bpo::value<bool>()->default_value(false),
+     "track inserted cursors")
+    ("str-sizes", bpo::value<std::vector<size_t>>()->default_value(
+        {8, 11, 64, 256, 301, 320}),
+     "sizes of ns/oid strings")
+    ("onode-sizes", bpo::value<std::vector<size_t>>()->default_value(
+        {8, 16, 128, 512, 576, 640}),
+     "sizes of onode")
+    ("range2", bpo::value<std::vector<int>>()->default_value(
+        {0, 128}),
+     "range of shard-pool-crush [a, b)")
+    ("range1", bpo::value<std::vector<unsigned>>()->default_value(
+        {0, 10}),
+     "range of ns-oid strings [a, b)")
+    ("range0", bpo::value<std::vector<unsigned>>()->default_value(
+        {0, 4}),
+     "range of snap-gen [a, b)");
+  return app.run(argc, argv, [&app] {
+    auto&& config = app.configuration();
+    auto tracked = config["tracked"].as<bool>();
+    if (tracked) {
+      return run<true>(config);
+    } else {
+      return run<false>(config);
+    }
+  });
+}