| 157 | template <typename ValueType, typename SumType, SimdLevel::type SimdLevel, |
| 158 | typename ValueFunc> |
| 159 | enable_if_t<std::is_floating_point<SumType>::value, SumType> SumArray( |
| 160 | const ArraySpan& data, ValueFunc&& func) { |
| 161 | using arrow::internal::VisitSetBitRunsVoid; |
| 162 | |
| 163 | const int64_t data_size = data.length - data.GetNullCount(); |
| 164 | if (data_size == 0) { |
| 165 | return 0; |
| 166 | } |
| 167 | |
| 168 | // number of inputs to accumulate before merging with another block |
| 169 | constexpr int kBlockSize = 16; // same as numpy |
| 170 | // levels (tree depth) = ceil(log2(len)) + 1, a bit larger than necessary |
| 171 | const int levels = bit_util::Log2(static_cast<uint64_t>(data_size)) + 1; |
| 172 | // temporary summation per level |
| 173 | std::vector<SumType> sum(levels); |
| 174 | // whether two summations are ready and should be reduced to upper level |
| 175 | // one bit for each level, bit0 -> level0, ... |
| 176 | uint64_t mask = 0; |
| 177 | // level of root node holding the final summation |
| 178 | int root_level = 0; |
| 179 | |
| 180 | // reduce summation of one block (may be smaller than kBlockSize) from leaf node |
| 181 | // continue reducing to upper level if two summations are ready for non-leaf node |
| 182 | // (capture `levels` by value because of ARROW-17567) |
| 183 | auto reduce = [&, levels](SumType block_sum) { |
| 184 | int cur_level = 0; |
| 185 | uint64_t cur_level_mask = 1ULL; |
| 186 | sum[cur_level] += block_sum; |
| 187 | mask ^= cur_level_mask; |
| 188 | while ((mask & cur_level_mask) == 0) { |
| 189 | block_sum = sum[cur_level]; |
| 190 | sum[cur_level] = 0; |
| 191 | ++cur_level; |
| 192 | DCHECK_LT(cur_level, levels); |
| 193 | cur_level_mask <<= 1; |
| 194 | sum[cur_level] += block_sum; |
| 195 | mask ^= cur_level_mask; |
| 196 | } |
| 197 | root_level = std::max(root_level, cur_level); |
| 198 | }; |
| 199 | |
| 200 | const ValueType* values = data.GetValues<ValueType>(1); |
| 201 | VisitSetBitRunsVoid(data.buffers[0].data, data.offset, data.length, |
| 202 | [&](int64_t pos, int64_t len) { |
| 203 | const ValueType* v = &values[pos]; |
| 204 | // unsigned division by constant is cheaper than signed one |
| 205 | const uint64_t blocks = static_cast<uint64_t>(len) / kBlockSize; |
| 206 | const uint64_t remains = static_cast<uint64_t>(len) % kBlockSize; |
| 207 | |
| 208 | for (uint64_t i = 0; i < blocks; ++i) { |
| 209 | SumType block_sum = 0; |
| 210 | for (int j = 0; j < kBlockSize; ++j) { |
| 211 | block_sum += func(v[j]); |
| 212 | } |
| 213 | reduce(block_sum); |
| 214 | v += kBlockSize; |
| 215 | } |
| 216 |
nothing calls this directly
no test coverage detected