# 代码阅读

把 Index 部分读完了,挺有意思的。做了个 Index 流程图:

NSG_Index

NSG 的代码 search 做了一些非常有意思的优化,但是它们的论文中却没有提到。

1)对于每个 point,把它的全精度向量和它的邻居存放在一起。这样可以尽可能保证局部性。

2)使用 _mm_prefetch 进行预取数据

_mm_prefetch(opt_graph_ + node_size * id, _MM_HINT_T0); //fetch 到所有缓存级别中

_mm_prefetch 代码的作用其实很简单,就是把当前地址的数据 fetch 到特定的缓存级别中,fetch 的数据大小是 cache line_MM_HINT_T0 表示加载到全部缓存级别。

# search 算法实现

记录一下 search 算法的实现

void IndexNSG::get_neighbors(const float *query, const Parameters &parameter,
                             std::vector<Neighbor> &retset,
                             std::vector<Neighbor> &fullset) {
  unsigned L = parameter.Get<unsigned>("L");
  retset.resize(L + 1);
  std::vector<unsigned> init_ids(L);
  // initializer_->Search(query, nullptr, L, parameter, init_ids.data());
  boost::dynamic_bitset<> flags{nd_, 0};
  L = 0;
  for (unsigned i = 0; i < init_ids.size() && i < final_graph_[ep_].size();
       i++) {
    init_ids[i] = final_graph_[ep_][i]; //init 阶段会 rand 一个 ep_
    flags[init_ids[i]] = true;
    L++;
  }
  while (
      L <
      init_ids.size()) { // 如果不够 L 就随机加上几个 point,直到 candidate 池到达 L
    unsigned id = rand() % nd_;
    if (flags[id])
      continue;
    init_ids[L] = id;
    L++;
    flags[id] = true;
  }
  L = 0;
  for (unsigned i = 0; i < init_ids.size(); i++) {
    unsigned id = init_ids[i];
    if (id >= nd_)
      continue; //  这个判断条件意义?
    // std::cout<<id<<std::endl;
    float dist = distance_->compare(data_ + dimension_ * (size_t)id, query,
                                    (unsigned)dimension_); // 得到 distance
    retset[i] = Neighbor(id, dist, true);
    // flags[id] = 1;
    L++;
  }
  std::sort(retset.begin(), retset.begin() + L);
  int k = 0;
  // 不得不吐槽啊,这代码写得真混乱
  while (k < (int)L) {
    int nk = L;
    if (retset[k].flag) { // 防止回溯
      retset[k].flag = false;
      unsigned n = retset[k].id;
      for (unsigned m = 0; m < final_graph_[n].size(); ++m) {
        unsigned id = final_graph_[n][m];
        if (flags[id])
          continue;
        flags[id] = 1;
        float dist = distance_->compare(query, data_ + dimension_ * (size_t)id,
                                        (unsigned)dimension_);
        Neighbor nn(id, dist, true);
        fullset.push_back(nn);
        if (dist >= retset[L - 1].distance)
          continue;
        int r = InsertIntoPool(retset.data(), L, nn);
        if (L + 1 < retset.size())
          ++L;
        if (r < nk)
          nk = r;
      }
    }
    if (nk <= k) // 说明新插入的那个 point 比当前的 point 离 query 更加进。
      k = nk;
    else
      ++k;
  }
}

# 总结

NSG 代码是我第一次看的代码,实现的很精妙。一开始自己完全不懂 SIMD 和 OMP 的相关知识,看得很痛苦。。。补了些相关知识后,终于花了一天把这个实现读完了。

更新于

请我喝[茶]~( ̄▽ ̄)~*

Kalice 微信支付

微信支付

Kalice 支付宝

支付宝