Ceph MDS 的半衰期方法的 inode 活跃度统计

本文代码以 Ceph 16.2.10 为准。

MDS 的活跃度统计方法

MDS 会统计 inode 的访问活跃度。

对于任何类型的 inode,都有两个计数器,分别表示该 inode 的读、写活跃度。MDS 会在该 inode 发生读或写时,给相应的计数器增加 1。

为了使活跃度的语义更合理,MDS 为活跃度增加了衰变设定:每过一段时间,活跃度值就减少为原来的一半。相比于一直简单累加或只保留最近一段时间的值,这样做更能体现 inode 被访问的情况。

模拟半衰期

设常数 \(H > 0\),考察函数

\[f_H(x) = (\frac{1}{2})^\frac{x}{H} \]

则有对任意实数 \(x\),有

\[f_H(x+H)=\frac{1}{2}f_H(x) \]

可见,函数 \(f_H\) 的特点类似于放射性元素的衰变,自变量每增大 \(H\),函数值就是原来的一半。

在计算机中,计算 \(e^x\) 比计算其他数的方幂更快,所以为了提高计算速度,令

\[k = \frac{1}{H} \ln \frac{1}{2} \]

\[(\frac{1}{2})^\frac{x}{H} = e^{kx} \]

Ceph 相关代码

在 src/common/DecayCounter.h

class DecayRate {
public:
  friend class DecayCounter;

  DecayRate() {}
  // cppcheck-suppress noExplicitConstructor
  DecayRate(double hl) { set_halflife(hl); }
  DecayRate(const DecayRate &dr) : k(dr.k) {}

  void set_halflife(double hl) {
    k = log(0.5) / hl;
  }
  double get_halflife() const {
    return log(0.5) / k;
  }

private:
  double k = 0;             // k = ln(0.5)/half_life
};

DecayRate 描述衰变率,它的构造函数和 set_halflife 函数的参数都是半衰期,而自身保存了经过计算的 \(k\)

// 去掉了一些无关的代码和注释

class DecayCounter {
public:
  using time = ceph::coarse_mono_time;
  using clock = ceph::coarse_mono_clock;

  explicit DecayCounter(const DecayRate &rate) : last_decay(clock::now()), rate(rate) {}

  double get() const {
    decay();
    return val;
  }

  double get_last() const {
    return val;
  }
  
  time get_last_decay() const {
    return last_decay;
  }

  double hit(double v = 1.0) {
    decay(v);
    return val;
  }

  void adjust(double v = 1.0) {
    decay(v);
  }

  void scale(double f) {
    val *= f;
  }

  void reset() {
    last_decay = clock::now();
    val = 0;
  }

protected:
  void decay(double delta) const {
    auto now = clock::now();
    double el = std::chrono::duration<double>(now - last_decay).count();

    // calculate new value
    double newval = val * exp(el * rate.k) + delta;
    if (newval < 0.01) {
      newval = 0.0;
    }

    val = newval;
    last_decay = now;
  }
  
  void decay() const {decay(0.0);}

private:
  mutable double val = 0.0; // value
  mutable time last_decay = clock::zero(); // time of last decay
  DecayRate rate;
};

比较核心的函数是 decay,表示把旧值衰变,并累加一个值,达到一边增加一边衰变的效果。

一个 inode 一旦被访问,函数 MDBalancer::hit_inode (参见 src/mds/MDBalancer.cc)就会被调用,

void MDBalancer::hit_inode(CInode *in, int type, int who)
{
  // hit inode
  in->pop.get(type).hit();

  if (in->get_parent_dn())
  {
    hit_dir(in->get_parent_dn()->get_dir(), type, who);
  }
}

这里的 CInode 就表示一个 inode,它有成员 pop

class CInode : public MDSCacheObject, public InodeStoreBase, public Counter<CInode> {
public:
  inode_load_vec_t pop;

// ...
}

pop 里面主要就是两个计数器:

class inode_load_vec_t {
public:
  static const size_t NUM = 2;

  inode_load_vec_t(const DecayRate &rate) : vec{DecayCounter(rate), DecayCounter(rate)} {}

  DecayCounter &get(int t) {
    return vec[t];
  }

private:
  std::array<DecayCounter, NUM> vec;

// ...
};

MDBalancer::hit_inode 的参数 type 的取值范围在这里:

#define META_POP_IRD     0
#define META_POP_IWR     1
#define META_POP_READDIR 2
#define META_POP_FETCH   3
#define META_POP_STORE   4
#define META_NPOP        5

对于一般 inode,type 只能是 META_POP_IRDMETA_POP_IWR,也就是只统计读和写。对于目录类型的 inode,还会统计别的信息,不再赘述,因为思路和代码都是类似的。

posted @ 2022-11-18 16:59  风华神使  阅读(228)  评论(0)    收藏  举报