基于树索引的Annoy的搜索库其节点cpp解读

Annoy简介

   Annoy是Erik Bernhardsson写的一个以树为数据结构的近似最近邻搜索库,并用在Spotify的推荐系统中。Annoy的核心是不断用选取的两个质心的法平面对空间进行分割,最终将每一个区分的子空间里面的样本数据限制在K以内。对于待插入的样本\(x_i\),从根节点依次使用法向量跟做内积运算,从而判断使用法平面的哪一边(左子树or右子树)。对于查询向量\(q_i\),采用同样的方式(在树结构上体现为从根节点向叶子节点递归遍历),即可定位到跟\(q_i\)在同一个子空间或者邻近的子空间的样本,这些样本即为\(q_i\)近邻。

struct ANNOY_NODE_ATTRIBUTE Node {
    S n_descendants;
    union {
        S children[2]; // Will possibly store more than 2
        T norm;
    };
    T dot_factor;
    T v[1]; // We let this one overflow intentionally. Need to allocate at least 1 to make GCC happy
};
向量符号 含义说明
S n_descendants; 后代数量;这个节点下方的叶子节点(样本点)的总数量。
union { ... }; 一个内存优化技巧,union 内部的成员共享同一块内存空间。这意味着,在任何给定时间,你只能使用 children 或 norm 中的一个。
S children[2]; 内部节点(非叶子节点) 使用。存储指向左子节点和右子节点的索引(不是指针,Annoy 将所有节点存储在一个大数组中,通过索引访问)。
T norm; 叶子节点使用
T dot_factor 点积因子,用于Annoy的距离计算
T v[1] 划分向量(Splitting Vector)。这是一个灵活数组成员 (Flexible Array Member, FAM) 的技巧。

   正如注释所说,v[1] 后面的内存被有意地溢出 (overflow intentionally),用来存储 D 维向量的其余 D−1 个分量。这使得 Annoy 可以高效地将节点的元数据和其划分向量(或叶子节点中的实际数据向量)存储在内存中连续的一块区域。

posted @ 2025-09-26 17:23  Xuzzzer  阅读(15)  评论(0)    收藏  举报