]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
Create per-access fragmentation metrics
authortridao <daominhtri0503@gmail.com>
Mon, 3 Jul 2023 22:13:48 +0000 (19:13 -0300)
committertridao <daominhtri0503@gmail.com>
Mon, 17 Jul 2023 02:16:08 +0000 (23:16 -0300)
- Keeps track of read ops in the imitator
- The metrics is defined as jumps per blocks read with jumps as the
  number of times having to stop reading continuous extents
Signed-off-by: Tri Dao <daominhtri0503@gmail.com>
src/test/objectstore/Fragmentation_simulator.cc
src/test/objectstore/ObjectStoreImitator.cc
src/test/objectstore/ObjectStoreImitator.h

index 31ef2ab7f0e10d85afed74cb9dc25f70b493ef61..1ad71cac40b472e234af0449303be4557e704ba7 100644 (file)
@@ -96,6 +96,7 @@ int FragmentationSimulator::begin_simulation_with_generators() {
   generators.clear();
   os->print_status();
   os->print_per_object_fragmentation();
+  os->print_per_access_fragmentation();
   return 0;
 }
 
@@ -190,13 +191,17 @@ struct RandomCWGenerator : public FragmentationSimulator::WorkloadGenerator {
     t2.create(ch->get_cid(), obj2);
     tls.emplace_back(std::move(t2));
 
+    os->queue_transactions(ch, tls);
+    os->verify_objects(ch);
+
     gen_type rng(time(0));
     boost::uniform_int<> u_size(0, _1Mb * 4);
     boost::uniform_int<> u_offset(0, _1Mb);
 
     for (unsigned i{0}; i < 200; ++i) {
-      ObjectStore::Transaction t3;
+      tls.clear();
 
+      ObjectStore::Transaction t3;
       auto size = u_size(rng);
       auto offset = u_offset(rng);
 
@@ -204,16 +209,28 @@ struct RandomCWGenerator : public FragmentationSimulator::WorkloadGenerator {
       tls.emplace_back(std::move(t3));
 
       ObjectStore::Transaction t4;
-
       size = u_size(rng);
       offset = u_offset(rng);
 
       t4.write(ch->get_cid(), obj2, offset, size, make_bl(size, 'c'));
       tls.emplace_back(std::move(t4));
+
+      os->queue_transactions(ch, tls);
+      os->verify_objects(ch);
+
+      bufferlist dummy;
+
+      size = u_size(rng);
+      offset = u_offset(rng);
+      os->read(ch, obj1, offset, size, dummy);
+
+      dummy.clear();
+
+      size = u_size(rng);
+      offset = u_offset(rng);
+      os->read(ch, obj2, offset, size, dummy);
     }
 
-    os->queue_transactions(ch, tls);
-    os->verify_objects(ch);
     return 0;
   }
 };
index 42176f1a1e6191749a3fcb6f74d6519886136f9a..f409229c1ec4fe76b506c697fe31beb2d97b388b 100644 (file)
@@ -18,7 +18,6 @@
 // ---------- Object -----------
 
 void ObjectStoreImitator::Object::punch_hole(uint64_t offset, uint64_t length,
-                                             uint64_t min_alloc_size,
                                              PExtentVector &old_extents) {
   if (extent_map.empty())
     return;
@@ -207,6 +206,28 @@ void ObjectStoreImitator::print_per_object_fragmentation() {
   }
 }
 
+void ObjectStoreImitator::print_per_access_fragmentation() {
+  for (auto &[_, coll_ref] : coll_map) {
+    for (auto &[id, read_ops] : coll_ref->read_ops) {
+      unsigned blks{0}, jmps{0};
+      for (auto &op : read_ops) {
+        blks += op.blks;
+        jmps += op.jmps;
+      }
+
+      double avg_total_blks = (double)blks / read_ops.size();
+      double avg_jmps = (double)jmps / read_ops.size();
+      double avg_jmps_per_blk = (double)jmps / (double)blks;
+
+      std::cout << "Object: " << id.hobj.oid.name
+                << ", average total blks read: " << avg_total_blks
+                << ", average total jumps: " << avg_jmps
+                << ", average jumps per block: " << avg_jmps_per_blk
+                << std::endl;
+    }
+  }
+}
+
 // ------- Transactions -------
 
 int ObjectStoreImitator::queue_transactions(CollectionHandle &ch,
@@ -497,16 +518,63 @@ void ObjectStoreImitator::_assign_nid(ObjectRef &o) {
 int ObjectStoreImitator::_do_zero(CollectionRef &c, ObjectRef &o,
                                   uint64_t offset, size_t length) {
   PExtentVector old_extents;
-  o->punch_hole(offset, length, min_alloc_size, old_extents);
+  o->punch_hole(offset, length, old_extents);
   alloc->release(old_extents);
   return 0;
 }
 
 int ObjectStoreImitator::_do_read(Collection *c, ObjectRef &o, uint64_t offset,
-                                  size_t len, ceph::buffer::list &bl,
+                                  size_t length, ceph::buffer::list &bl,
                                   uint32_t op_flags, uint64_t retry_count) {
-  auto data = std::string(len, 'a');
+  auto data = std::string(length, 'a');
   bl.append(data);
+
+  // Keeping track of read ops to evaluate per-access fragmentation
+  ReadOp op(offset, length);
+  bluestore_pextent_t last_ext;
+  uint64_t end = length + offset;
+
+  auto it = o->extent_map.lower_bound(offset);
+  if ((it == o->extent_map.end() || it->first > offset) &&
+      it != o->extent_map.begin()) {
+    it = std::prev(it);
+
+    auto diff = offset - it->first;
+    if (diff < it->second.length) {
+      // end not in this extent
+      if (end > it->first + it->second.length) {
+        op.blks += div_round_up(it->second.length - diff, min_alloc_size);
+      } else { // end is within this extent so we take up the entire length
+        op.blks += div_round_up(length, min_alloc_size);
+      }
+
+      last_ext = it->second;
+      it++;
+    }
+  }
+
+  while (it != o->extent_map.end() && it->first < end) {
+    auto extent = it->second;
+    if (last_ext.length > 0 &&
+        last_ext.offset + last_ext.length != extent.offset) {
+      op.jmps++;
+    }
+
+    if (extent.length > length) {
+      op.blks += div_round_up(length, min_alloc_size);
+      break;
+    }
+
+    op.blks += div_round_up(extent.length, min_alloc_size);
+    length -= extent.length;
+    it++;
+  }
+
+  c->read_ops[o->oid].push_back(op);
+  // std::cout << "blks: " << op.blks << ", jmps: " << op.jmps
+  //           << ", offset: " << op.offset << ", length: " << op.length
+  //           << std::endl;
+
   return bl.length();
 }
 
@@ -532,7 +600,7 @@ int ObjectStoreImitator::_do_write(CollectionRef &c, ObjectRef &o,
   length = p2align(length, min_alloc_size);
 
   PExtentVector punched;
-  o->punch_hole(offset, length, min_alloc_size, punched);
+  o->punch_hole(offset, length, punched);
   alloc->release(punched);
 
   // all writes will trigger an allocation
@@ -639,7 +707,7 @@ void ObjectStoreImitator::_do_truncate(CollectionRef &c, ObjectRef &o,
     return;
 
   PExtentVector old_extents;
-  o->punch_hole(offset, o->size - offset, min_alloc_size, old_extents);
+  o->punch_hole(offset, o->size - offset, old_extents);
   o->size = offset;
   alloc->release(old_extents);
 }
index 719d99dc15c96243a07583457a31184d7a8f9fb3..a98f5cc1c4eb86e41ff086f3a6ff4c4dfda714cc 100644 (file)
@@ -50,18 +50,29 @@ private:
            uint64_t nid_ = 0, uint64_t size_ = 0)
         : c(c_), oid(oid_), exists(exists_), nid(nid_), size(size_) {}
 
-    void punch_hole(uint64_t offset, uint64_t length, uint64_t min_alloc_size,
+    void punch_hole(uint64_t offset, uint64_t length,
                     PExtentVector &old_extents);
     void verify_extents();
     void append(PExtentVector &ext, uint64_t offset);
     uint64_t ext_length();
   };
-
   typedef boost::intrusive_ptr<Object> ObjectRef;
 
+  struct ReadOp {
+    uint64_t offset;
+    uint64_t length;
+    unsigned blks;
+    unsigned
+        jmps; // # of times we have to stop iterating over continuous extents
+    ReadOp(uint64_t offset = 0, uint64_t length = 0, unsigned blks = 0,
+           unsigned jmps = 0)
+        : offset(offset), length(length), blks(blks), jmps(jmps) {}
+  };
+
   struct Collection : public CollectionImpl {
     bluestore_cnode_t cnode;
     std::map<ghobject_t, ObjectRef> objects;
+    std::unordered_map<ghobject_t, std::vector<ReadOp>> read_ops;
 
     ceph::shared_mutex lock = ceph::make_shared_mutex(
         "FragmentationSimulator::Collection::lock", true, false);
@@ -160,8 +171,6 @@ private:
   int _clone(CollectionRef &c, ObjectRef &oldo, ObjectRef &newo);
   int _clone_range(CollectionRef &c, ObjectRef &oldo, ObjectRef &newo,
                    uint64_t srcoff, uint64_t length, uint64_t dstoff);
-  int read(CollectionHandle &c, const ghobject_t &oid, uint64_t offset,
-           size_t len, ceph::buffer::list &bl, uint32_t op_flags = 0) override;
 
   // Helpers
 
@@ -171,7 +180,6 @@ private:
                 uint32_t fadvise_flags);
   int _do_alloc_write(CollectionRef c, ObjectRef &o, bufferlist &bl,
                       uint64_t offset, uint64_t length);
-
   void _do_truncate(CollectionRef &c, ObjectRef &o, uint64_t offset);
   int _do_zero(CollectionRef &c, ObjectRef &o, uint64_t offset, size_t length);
   int _do_clone_range(CollectionRef &c, ObjectRef &oldo, ObjectRef &newo,
@@ -210,19 +218,25 @@ public:
   void print_status();
   void verify_objects(CollectionHandle &ch);
 
-  // Generate metrics for per-object fragmentation, defined by:
-  // frag_score = 1 - sum((size proportion of each extents / object size) ^
-  // index of each extent in a vector sorted by descending length).
-  // This should only be called after the  generators are finished as it will
-  // attempt to change an object's extents.
+  // Generate metrics for per-object fragmentation (how fragmented are each
+  // object's extents), defined by: frag_score = 1 - sum((size proportion of
+  // each extents / object size) ^ index of each extent in a vector sorted by
+  // descending length + 1). This should only be called after the  generators
+  // are finished as it will attempt to change an object's extents.
   void print_per_object_fragmentation();
 
+  // Genereate metrisc for per-access fragmentation, which is jumps/blocks read.
+  // Jumps are how many times we have to stop reading continuous extents
+  void print_per_access_fragmentation();
+
   // Overrides
 
   // This is often not called directly but through queue_transaction
   int queue_transactions(CollectionHandle &ch, std::vector<Transaction> &tls,
                          TrackedOpRef op = TrackedOpRef(),
                          ThreadPool::TPHandle *handle = NULL) override;
+  int read(CollectionHandle &c, const ghobject_t &oid, uint64_t offset,
+           size_t len, ceph::buffer::list &bl, uint32_t op_flags = 0) override;
   CollectionHandle open_collection(const coll_t &cid) override;
   CollectionHandle create_new_collection(const coll_t &cid) override;
   void set_collection_commit_queue(const coll_t &cid,