* @brief Traverse the top levels of the tree for the entire block_size. * * In the array layout, it is organized to guarantee that if a node at the current level * has index nidx, then the node index for the left child at the next level is always * 2*nidx, and the node index for the right child at the next level is always 2*nidx+1. * This greatly improves data locality. * * @p
| 174 | * to the level next after kNumDeepLevels |
| 175 | */ |
| 176 | void Process(common::Span<RegTree::FVec> fvec_tloc, std::size_t const block_size, |
| 177 | bst_node_t* p_nidx) { |
| 178 | for (int depth = 0; depth < kNumDeepLevels; ++depth) { |
| 179 | std::size_t first_node = (1u << depth) - 1; |
| 180 | |
| 181 | for (std::size_t i = 0; i < block_size; ++i) { |
| 182 | bst_node_t idx = p_nidx[i]; |
| 183 | |
| 184 | const auto& feat = fvec_tloc[i]; |
| 185 | bst_feature_t split = split_index_[first_node + idx]; |
| 186 | auto fvalue = feat.GetFvalue(split); |
| 187 | if constexpr (any_missing) { |
| 188 | bool go_left = feat.IsMissing(split) ? default_left_[first_node + idx] |
| 189 | : GetDecision(fvalue, first_node + idx); |
| 190 | p_nidx[i] = 2 * idx + !go_left; |
| 191 | } else { |
| 192 | p_nidx[i] = 2 * idx + !GetDecision(fvalue, first_node + idx); |
| 193 | } |
| 194 | } |
| 195 | } |
| 196 | // Remap to the original index. |
| 197 | for (std::size_t i = 0; i < block_size; ++i) { |
| 198 | p_nidx[i] = nidx_in_tree_[p_nidx[i]]; |
| 199 | } |
| 200 | } |
| 201 | }; |
| 202 | |
| 203 | template <bool has_categorical, bool any_missing, int num_deep_levels = 1, typename TreeView> |
no test coverage detected