last_dirty_io_by_src_ext = {};
last_trim_rewrites = {};
last_reclaim_rewrites = {};
+ last_access = {};
+ last_cache_absent_by_src = {};
+ last_access_by_src_ext = {};
namespace sm = seastar::metrics;
using src_t = Transaction::src_t;
lru.get_stats(ret, report_detail, seconds);
/*
- * get dirty stats
+ * dirty stats
+ * rewrite stats
+ * index stats
+ * access stats
*/
ret.dirty_sizes = cache_size_stats_t{stats.dirty_bytes, dirty.size()};
ret.dirty_io = stats.dirty_io;
ret.dirty_io.minus(last_dirty_io);
+ ret.access = stats.access;
+ ret.access.minus(last_access);
if (report_detail && seconds != 0) {
counter_by_src_t<counter_by_extent_t<dirty_io_stats_t> >
oss << "\ncache total"
<< cache_size_stats_t{extents_index.get_bytes(), extents_index.size()};
+ counter_by_src_t<counter_by_extent_t<extent_access_stats_t> >
+ _access_by_src_ext = stats.access_by_src_ext;
+ counter_by_src_t<cache_access_stats_t> access_by_src;
+ for (uint8_t _src=0; _src<TRANSACTION_TYPE_MAX; ++_src) {
+ auto src = static_cast<transaction_type_t>(_src);
+ cache_access_stats_t& trans_access = get_by_src(access_by_src, src);
+ trans_access.cache_absent = get_by_src(stats.cache_absent_by_src, src);
+ trans_access.cache_absent -= get_by_src(last_cache_absent_by_src, src);
+ auto& access_by_ext = get_by_src(_access_by_src_ext, src);
+ const auto& last_access_by_ext = get_by_src(last_access_by_src_ext, src);
+ for (uint8_t _ext=0; _ext<EXTENT_TYPES_MAX; ++_ext) {
+ auto ext = static_cast<extent_types_t>(_ext);
+ extent_access_stats_t& extent_access = get_by_ext(access_by_ext, ext);
+ const auto& last_extent_access = get_by_ext(last_access_by_ext, ext);
+ extent_access.minus(last_extent_access);
+ trans_access.s.add(extent_access);
+ }
+ }
+ oss << "\naccess: total"
+ << cache_access_stats_printer_t{seconds, ret.access};
+ for (uint8_t _src=0; _src<TRANSACTION_TYPE_MAX; ++_src) {
+ auto src = static_cast<transaction_type_t>(_src);
+ const auto& trans_access = get_by_src(access_by_src, src);
+ if (trans_access.is_empty()) {
+ continue;
+ }
+ extent_access_stats_t data_access;
+ extent_access_stats_t mdat_access;
+ extent_access_stats_t phys_access;
+ const auto& access_by_ext = get_by_src(_access_by_src_ext, src);
+ for (uint8_t _ext=0; _ext<EXTENT_TYPES_MAX; ++_ext) {
+ auto ext = static_cast<extent_types_t>(_ext);
+ const auto& extent_access = get_by_ext(access_by_ext, ext);
+ if (is_data_type(ext)) {
+ data_access.add(extent_access);
+ } else if (is_logical_metadata_type(ext)) {
+ mdat_access.add(extent_access);
+ } else if (is_physical_type(ext)) {
+ phys_access.add(extent_access);
+ }
+ }
+ oss << "\n " << src << ": "
+ << cache_access_stats_printer_t{seconds, trans_access}
+ << "\n data"
+ << extent_access_stats_printer_t{seconds, data_access}
+ << "\n mdat"
+ << extent_access_stats_printer_t{seconds, mdat_access}
+ << "\n phys"
+ << extent_access_stats_printer_t{seconds, phys_access};
+ }
+
INFO("{}", oss.str());
last_dirty_io_by_src_ext = stats.dirty_io_by_src_ext;
last_trim_rewrites = stats.trim_rewrites;
last_reclaim_rewrites = stats.reclaim_rewrites;
+ last_cache_absent_by_src = stats.cache_absent_by_src;
+ last_access_by_src_ext = stats.access_by_src_ext;
}
last_dirty_io = stats.dirty_io;
+ last_access = stats.access;
return ret;
}
last_dirty_io_by_src_ext;
mutable rewrite_stats_t last_trim_rewrites;
mutable rewrite_stats_t last_reclaim_rewrites;
+ mutable cache_access_stats_t last_access;
+ mutable counter_by_src_t<uint64_t> last_cache_absent_by_src;
+ mutable counter_by_src_t<counter_by_extent_t<extent_access_stats_t> >
+ last_access_by_src_ext;
void account_conflict(Transaction::src_t src1, Transaction::src_t src2) {
assert(src1 < Transaction::src_t::MAX);
dirty_sizes_ps,
dirty_io_stats_printer_t{seconds, dirty_io_ps});
+ cache_access_stats_t access_ps = cache_total.access;
+ access_ps.cache_absent /= seastar::smp::count;
+ access_ps.s.trans_pending /= seastar::smp::count;
+ access_ps.s.trans_dirty /= seastar::smp::count;
+ access_ps.s.trans_lru /= seastar::smp::count;
+ access_ps.s.cache_dirty /= seastar::smp::count;
+ access_ps.s.cache_lru /= seastar::smp::count;
+ access_ps.s.load_absent /= seastar::smp::count;
+ access_ps.s.load_present /= seastar::smp::count;
+ INFO("cache_access: total{}; per-shard{}",
+ cache_access_stats_printer_t{seconds, cache_total.access},
+ cache_access_stats_printer_t{seconds, access_ps});
+
return seastar::now();
});
}
return out;
}
+std::ostream& operator<<(std::ostream& out, const extent_access_stats_printer_t& p)
+{
+ constexpr const char* dfmt = "{:.2f}";
+ double est_total_access = static_cast<double>(p.stats.get_estimated_total_access());
+ out << "(~";
+ if (est_total_access > 1000000) {
+ out << fmt::format(dfmt, est_total_access/1000000)
+ << "M, ";
+ } else {
+ out << fmt::format(dfmt, est_total_access/1000)
+ << "K, ";
+ }
+ double trans_hit = static_cast<double>(p.stats.get_trans_hit());
+ double cache_hit = static_cast<double>(p.stats.get_cache_hit());
+ double est_cache_access = static_cast<double>(p.stats.get_estimated_cache_access());
+ double load_absent = static_cast<double>(p.stats.load_absent);
+ out << "trans-hit=~"
+ << fmt::format(dfmt, trans_hit/est_total_access*100)
+ << "%(p"
+ << fmt::format(dfmt, p.stats.trans_pending/trans_hit)
+ << ",d"
+ << fmt::format(dfmt, p.stats.trans_dirty/trans_hit)
+ << ",l"
+ << fmt::format(dfmt, p.stats.trans_lru/trans_hit)
+ << "), cache-hit=~"
+ << fmt::format(dfmt, cache_hit/est_cache_access*100)
+ << "%(d"
+ << fmt::format(dfmt, p.stats.cache_dirty/cache_hit)
+ << ",l"
+ << fmt::format(dfmt, p.stats.cache_lru/cache_hit)
+ <<"), load-present/absent="
+ << fmt::format(dfmt, p.stats.load_present/load_absent)
+ << ")";
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const cache_access_stats_printer_t& p)
+{
+ constexpr const char* dfmt = "{:.2f}";
+ double total_access = static_cast<double>(p.stats.get_total_access());
+ out << "(";
+ if (total_access > 1000000) {
+ out << fmt::format(dfmt, total_access/1000000)
+ << "M, ";
+ } else {
+ out << fmt::format(dfmt, total_access/1000)
+ << "K, ";
+ }
+ double trans_hit = static_cast<double>(p.stats.s.get_trans_hit());
+ double cache_hit = static_cast<double>(p.stats.s.get_cache_hit());
+ double cache_access = static_cast<double>(p.stats.get_cache_access());
+ double load_absent = static_cast<double>(p.stats.s.load_absent);
+ out << "trans-hit="
+ << fmt::format(dfmt, trans_hit/total_access*100)
+ << "%(p"
+ << fmt::format(dfmt, p.stats.s.trans_pending/trans_hit)
+ << ",d"
+ << fmt::format(dfmt, p.stats.s.trans_dirty/trans_hit)
+ << ",l"
+ << fmt::format(dfmt, p.stats.s.trans_lru/trans_hit)
+ << "), cache-hit="
+ << fmt::format(dfmt, cache_hit/cache_access*100)
+ << "%(d"
+ << fmt::format(dfmt, p.stats.s.cache_dirty/cache_hit)
+ << ",l"
+ << fmt::format(dfmt, p.stats.s.cache_lru/cache_hit)
+ <<"), load/absent="
+ << fmt::format(dfmt, load_absent/p.stats.cache_absent*100)
+ << "%, load-present/absent="
+ << fmt::format(dfmt, p.stats.s.load_present/load_absent)
+ << ")";
+ return out;
+}
+
} // namespace crimson::os::seastore
uint64_t load_absent = 0;
uint64_t load_present = 0;
+ uint64_t get_trans_hit() const {
+ return trans_pending + trans_dirty + trans_lru;
+ }
+
uint64_t get_cache_hit() const {
return cache_dirty + cache_lru;
}
+
+ uint64_t get_estimated_cache_access() const {
+ return get_cache_hit() + load_absent;
+ }
+
+ uint64_t get_estimated_total_access() const {
+ return get_trans_hit() + get_cache_hit() + load_absent;
+ }
+
+ bool is_empty() const {
+ return get_estimated_total_access() == 0;
+ }
+
+ void add(const extent_access_stats_t& o) {
+ trans_pending += o.trans_pending;
+ trans_dirty += o.trans_dirty;
+ trans_lru += o.trans_lru;
+ cache_dirty += o.cache_dirty;
+ cache_lru += o.cache_lru;
+ load_absent += o.load_absent;
+ load_present += o.load_present;
+ }
+
+ void minus(const extent_access_stats_t& o) {
+ trans_pending -= o.trans_pending;
+ trans_dirty -= o.trans_dirty;
+ trans_lru -= o.trans_lru;
+ cache_dirty -= o.cache_dirty;
+ cache_lru -= o.cache_lru;
+ load_absent -= o.load_absent;
+ load_present -= o.load_present;
+ }
};
+struct extent_access_stats_printer_t {
+ double seconds;
+ const extent_access_stats_t& stats;
+};
+std::ostream& operator<<(std::ostream&, const extent_access_stats_printer_t&);
struct cache_access_stats_t {
extent_access_stats_t s;
uint64_t get_cache_access() const {
return s.get_cache_hit() + cache_absent;
}
+
+ uint64_t get_total_access() const {
+ return s.get_trans_hit() + get_cache_access();
+ }
+
+ bool is_empty() const {
+ return get_total_access() == 0;
+ }
+
+ void add(const cache_access_stats_t& o) {
+ s.add(o.s);
+ cache_absent += o.cache_absent;
+ }
+
+ void minus(const cache_access_stats_t& o) {
+ s.minus(o.s);
+ cache_absent -= o.cache_absent;
+ }
+};
+struct cache_access_stats_printer_t {
+ double seconds;
+ const cache_access_stats_t& stats;
};
+std::ostream& operator<<(std::ostream&, const cache_access_stats_printer_t&);
struct cache_stats_t {
cache_size_stats_t lru_sizes;
cache_io_stats_t lru_io;
cache_size_stats_t dirty_sizes;
dirty_io_stats_t dirty_io;
+ cache_access_stats_t access;
void add(const cache_stats_t& o) {
lru_sizes.add(o.lru_sizes);
lru_io.add(o.lru_io);
dirty_sizes.add(o.dirty_sizes);
dirty_io.add(o.dirty_io);
+ access.add(o.access);
}
};
WRITE_CLASS_DENC_BOUNDED(crimson::os::seastore::segment_tail_t)
#if FMT_VERSION >= 90000
+template <> struct fmt::formatter<crimson::os::seastore::cache_access_stats_printer_t> : fmt::ostream_formatter {};
template <> struct fmt::formatter<crimson::os::seastore::cache_io_stats_printer_t> : fmt::ostream_formatter {};
template <> struct fmt::formatter<crimson::os::seastore::cache_size_stats_t> : fmt::ostream_formatter {};
template <> struct fmt::formatter<crimson::os::seastore::cache_size_stats_printer_t> : fmt::ostream_formatter {};