| 3175 | } |
| 3176 | |
| 3177 | BigIntVal AggregateFunctions::SampledNdvFinalize(FunctionContext* ctx, |
| 3178 | const StringVal& src) { |
| 3179 | SampledNdvState* state = reinterpret_cast<SampledNdvState*>(src.ptr); |
| 3180 | |
| 3181 | // Generate 'num_points' data points with x=row_count and y=ndv_estimate. These points |
| 3182 | // are used to fit a function for the NDV growth and estimate the real NDV. |
| 3183 | constexpr int num_points = |
| 3184 | SampledNdvState::NUM_HLL_BUCKETS * SampledNdvState::NUM_HLL_BUCKETS; |
| 3185 | int64_t counts[num_points] = { 0 }; |
| 3186 | int64_t ndvs[num_points] = { 0 }; |
| 3187 | |
| 3188 | int64_t min_ndv = numeric_limits<int64_t>::max(); |
| 3189 | int64_t min_count = numeric_limits<int64_t>::max(); |
| 3190 | // We have a fixed number of HLL intermediates to generate data points. Any unique |
| 3191 | // subset of intermediates can be combined to create a new data point. It was |
| 3192 | // empirically determined that 'num_data' points is typically sufficient and there are |
| 3193 | // diminishing returns from generating additional data points. |
| 3194 | // The generation method below was chosen for its simplicity. It successively merges |
| 3195 | // buckets in a rolling window of size NUM_HLL_BUCKETS. Repeating the last data point |
| 3196 | // where all buckets are merged biases the curve fitting to hit that data point which |
| 3197 | // makes sense because that's likely the most accurate one. The number of data points |
| 3198 | // are sufficient for reasonable accuracy. |
| 3199 | int pidx = 0; |
| 3200 | for (int i = 0; i < SampledNdvState::NUM_HLL_BUCKETS; ++i) { |
| 3201 | uint8_t merged_hll_data[DEFAULT_HLL_LEN]; |
| 3202 | memset(merged_hll_data, 0, DEFAULT_HLL_LEN); |
| 3203 | StringVal merged_hll(merged_hll_data, DEFAULT_HLL_LEN); |
| 3204 | int64_t merged_count = 0; |
| 3205 | for (int j = 0; j < SampledNdvState::NUM_HLL_BUCKETS; ++j) { |
| 3206 | int bucket_idx = (i + j) % SampledNdvState::NUM_HLL_BUCKETS; |
| 3207 | merged_count += state->buckets[bucket_idx].row_count; |
| 3208 | counts[pidx] = merged_count; |
| 3209 | StringVal hll = StringVal(state->buckets[bucket_idx].hll, DEFAULT_HLL_LEN); |
| 3210 | HllMerge(ctx, hll, &merged_hll); |
| 3211 | ndvs[pidx] = HllFinalEstimate(merged_hll.ptr); |
| 3212 | ++pidx; |
| 3213 | } |
| 3214 | min_count = std::min(min_count, state->buckets[i].row_count); |
| 3215 | min_ndv = std::min(min_ndv, ndvs[i * SampledNdvState::NUM_HLL_BUCKETS]); |
| 3216 | } |
| 3217 | // Based on the point-generation method above the last elements represent the data |
| 3218 | // point where all buckets are merged. |
| 3219 | int64_t max_count = counts[num_points - 1]; |
| 3220 | int64_t max_ndv = ndvs[num_points - 1]; |
| 3221 | |
| 3222 | // Scale all values to [0,1] since some objective functions require it (e.g., Sigmoid). |
| 3223 | double count_scale = max_count - min_count; |
| 3224 | double ndv_scale = max_ndv - min_ndv; |
| 3225 | if (count_scale == 0) count_scale = 1.0; |
| 3226 | if (ndv_scale == 0) ndv_scale = 1.0; |
| 3227 | double scaled_counts[num_points]; |
| 3228 | double scaled_ndvs[num_points]; |
| 3229 | for (int i = 0; i < num_points; ++i) { |
| 3230 | scaled_counts[i] = counts[i] / count_scale; |
| 3231 | scaled_ndvs[i] = ndvs[i] / ndv_scale; |
| 3232 | } |
| 3233 | |
| 3234 | // List of objective functions. Curve fitting will select the best values for the |
nothing calls this directly
no test coverage detected