20#ifndef _HLLARRAY_INTERNAL_HPP_
21#define _HLLARRAY_INTERNAL_HPP_
23#include "HllArray.hpp"
24#include "fdlibm_log.hpp"
26#include "HarmonicNumbers.hpp"
27#include "CubicInterpolation.hpp"
28#include "CompositeInterpolationXTable.hpp"
29#include "CouponList.hpp"
30#include "inv_pow2_table.hpp"
39HllArray<A>::HllArray(uint8_t lgConfigK,
target_hll_type tgtHllType,
const A& allocator):
40HllSketchImpl<A>(lgConfigK, tgtHllType, hll_mode::HLL),
44hllByteArr_(allocator),
46numAtCurMin_(1 << lgConfigK),
48rebuild_kxq_curmin_(false)
52HllArray<A>::HllArray(
const HllArray& other, target_hll_type tgtHllType) :
53 HllSketchImpl<A>(other.getLgConfigK(), tgtHllType, hll_mode::HLL),
57 kxq0_(1 << other.getLgConfigK()),
59 hllByteArr_(other.getAllocator()),
61 numAtCurMin_(1 << other.getLgConfigK()),
63 rebuild_kxq_curmin_(false)
67HllArray<A>* HllArray<A>::copyAs(target_hll_type tgtHllType)
const {
70 if (tgtHllType == this->getTgtHllType() && !this->isRebuildKxqCurminFlag()) {
71 return static_cast<HllArray*
>(copy());
77 case target_hll_type::HLL_4:
78 return HllSketchImplFactory<A>::convertToHll4(*
this);
79 case target_hll_type::HLL_6:
80 return HllSketchImplFactory<A>::convertToHll6(*
this);
81 case target_hll_type::HLL_8:
82 return HllSketchImplFactory<A>::convertToHll8(*
this);
84 throw std::invalid_argument(
"Invalid target HLL type");
89HllArray<A>* HllArray<A>::newHll(
const void* bytes,
size_t len,
const A& allocator) {
90 if (len < hll_constants::HLL_BYTE_ARR_START) {
91 throw std::out_of_range(
"Input data length insufficient to hold HLL array");
94 const uint8_t* data =
static_cast<const uint8_t*
>(bytes);
95 if (data[hll_constants::PREAMBLE_INTS_BYTE] != hll_constants::HLL_PREINTS) {
96 throw std::invalid_argument(
"Incorrect number of preInts in input stream");
98 if (data[hll_constants::SER_VER_BYTE] != hll_constants::SER_VER) {
99 throw std::invalid_argument(
"Wrong ser ver in input stream");
101 if (data[hll_constants::FAMILY_BYTE] != hll_constants::FAMILY_ID) {
102 throw std::invalid_argument(
"Input array is not an HLL sketch");
105 const hll_mode mode = HllSketchImpl<A>::extractCurMode(data[hll_constants::MODE_BYTE]);
107 throw std::invalid_argument(
"Calling HLL array constructor with non-HLL mode data");
110 const target_hll_type tgtHllType = HllSketchImpl<A>::extractTgtHllType(data[hll_constants::MODE_BYTE]);
111 const bool oooFlag = ((data[hll_constants::FLAGS_BYTE] & hll_constants::OUT_OF_ORDER_FLAG_MASK) ?
true : false);
112 const bool comapctFlag = ((data[hll_constants::FLAGS_BYTE] & hll_constants::COMPACT_FLAG_MASK) ?
true : false);
114 const uint8_t lgK = data[hll_constants::LG_K_BYTE];
115 const uint8_t curMin = data[hll_constants::HLL_CUR_MIN_BYTE];
117 const uint32_t arrayBytes = hllArrBytes(tgtHllType, lgK);
118 if (len <
static_cast<size_t>(hll_constants::HLL_BYTE_ARR_START + arrayBytes)) {
119 throw std::out_of_range(
"Input array too small to hold sketch image");
122 double hip, kxq0, kxq1;
123 std::memcpy(&hip, data + hll_constants::HIP_ACCUM_DOUBLE,
sizeof(
double));
124 std::memcpy(&kxq0, data + hll_constants::KXQ0_DOUBLE,
sizeof(
double));
125 std::memcpy(&kxq1, data + hll_constants::KXQ1_DOUBLE,
sizeof(
double));
127 uint32_t numAtCurMin, auxCount;
128 std::memcpy(&numAtCurMin, data + hll_constants::CUR_MIN_COUNT_INT,
sizeof(
int));
129 std::memcpy(&auxCount, data + hll_constants::AUX_COUNT_INT,
sizeof(
int));
131 AuxHashMap<A>* auxHashMap =
nullptr;
132 typedef std::unique_ptr<AuxHashMap<A>, std::function<void(AuxHashMap<A>*)>> aux_hash_map_ptr;
133 aux_hash_map_ptr aux_ptr;
135 uint8_t auxLgIntArrSize = data[4];
136 const size_t offset = hll_constants::HLL_BYTE_ARR_START + arrayBytes;
137 const uint8_t* auxDataStart = data + offset;
138 auxHashMap = AuxHashMap<A>::deserialize(auxDataStart, len - offset, lgK, auxCount, auxLgIntArrSize, comapctFlag, allocator);
139 aux_ptr = aux_hash_map_ptr(auxHashMap, auxHashMap->make_deleter());
142 HllArray<A>* sketch = HllSketchImplFactory<A>::newHll(lgK, tgtHllType, allocator);
143 sketch->putCurMin(curMin);
144 sketch->putOutOfOrderFlag(oooFlag);
145 if (!oooFlag) { sketch->putHipAccum(hip); }
146 sketch->putKxQ0(kxq0);
147 sketch->putKxQ1(kxq1);
148 sketch->putNumAtCurMin(numAtCurMin);
150 std::memcpy(sketch->hllByteArr_.data(), data + hll_constants::HLL_BYTE_ARR_START, arrayBytes);
152 if (auxHashMap !=
nullptr) {
153 ((Hll4Array<A>*)sketch)->putAuxHashMap(auxHashMap);
161HllArray<A>* HllArray<A>::newHll(std::istream& is,
const A& allocator) {
162 uint8_t listHeader[8];
163 read(is, listHeader, 8 *
sizeof(uint8_t));
165 if (listHeader[hll_constants::PREAMBLE_INTS_BYTE] != hll_constants::HLL_PREINTS) {
166 throw std::invalid_argument(
"Incorrect number of preInts in input stream");
168 if (listHeader[hll_constants::SER_VER_BYTE] != hll_constants::SER_VER) {
169 throw std::invalid_argument(
"Wrong ser ver in input stream");
171 if (listHeader[hll_constants::FAMILY_BYTE] != hll_constants::FAMILY_ID) {
172 throw std::invalid_argument(
"Input stream is not an HLL sketch");
175 hll_mode mode = HllSketchImpl<A>::extractCurMode(listHeader[hll_constants::MODE_BYTE]);
177 throw std::invalid_argument(
"Calling HLL constructor with non-HLL mode data");
180 const target_hll_type tgtHllType = HllSketchImpl<A>::extractTgtHllType(listHeader[hll_constants::MODE_BYTE]);
181 const bool oooFlag = ((listHeader[hll_constants::FLAGS_BYTE] & hll_constants::OUT_OF_ORDER_FLAG_MASK) ?
true : false);
182 const bool comapctFlag = ((listHeader[hll_constants::FLAGS_BYTE] & hll_constants::COMPACT_FLAG_MASK) ?
true : false);
184 const uint8_t lgK = listHeader[hll_constants::LG_K_BYTE];
185 const uint8_t curMin = listHeader[hll_constants::HLL_CUR_MIN_BYTE];
187 HllArray* sketch = HllSketchImplFactory<A>::newHll(lgK, tgtHllType, allocator);
188 typedef std::unique_ptr<HllArray<A>, std::function<void(HllSketchImpl<A>*)>> hll_array_ptr;
189 hll_array_ptr sketch_ptr(sketch, sketch->get_deleter());
190 sketch->putCurMin(curMin);
191 sketch->putOutOfOrderFlag(oooFlag);
193 const auto hip = read<double>(is);
194 const auto kxq0 = read<double>(is);
195 const auto kxq1 = read<double>(is);
196 if (!oooFlag) { sketch->putHipAccum(hip); }
197 sketch->putKxQ0(kxq0);
198 sketch->putKxQ1(kxq1);
200 const auto numAtCurMin = read<uint32_t>(is);
201 const auto auxCount = read<uint32_t>(is);
202 sketch->putNumAtCurMin(numAtCurMin);
204 read(is, sketch->hllByteArr_.data(), sketch->getHllByteArrBytes());
207 uint8_t auxLgIntArrSize = listHeader[4];
208 AuxHashMap<A>* auxHashMap = AuxHashMap<A>::deserialize(is, lgK, auxCount, auxLgIntArrSize, comapctFlag, allocator);
209 ((Hll4Array<A>*)sketch)->putAuxHashMap(auxHashMap);
212 if (!is.good()) {
throw std::runtime_error(
"error reading from std::istream"); }
214 return sketch_ptr.release();
218auto HllArray<A>::serialize(
bool compact,
unsigned header_size_bytes)
const -> vector_bytes {
219 const size_t sketchSizeBytes = (compact ? getCompactSerializationBytes() : getUpdatableSerializationBytes()) + header_size_bytes;
220 vector_bytes byteArr(sketchSizeBytes, 0, getAllocator());
221 uint8_t* bytes = byteArr.data() + header_size_bytes;
222 AuxHashMap<A>* auxHashMap = getAuxHashMap();
224 bytes[hll_constants::PREAMBLE_INTS_BYTE] =
static_cast<uint8_t
>(getPreInts());
225 bytes[hll_constants::SER_VER_BYTE] =
static_cast<uint8_t
>(hll_constants::SER_VER);
226 bytes[hll_constants::FAMILY_BYTE] =
static_cast<uint8_t
>(hll_constants::FAMILY_ID);
227 bytes[hll_constants::LG_K_BYTE] =
static_cast<uint8_t
>(this->lgConfigK_);
228 bytes[hll_constants::LG_ARR_BYTE] =
static_cast<uint8_t
>(auxHashMap ==
nullptr ? 0 : auxHashMap->getLgAuxArrInts());
229 bytes[hll_constants::FLAGS_BYTE] = this->makeFlagsByte(compact);
230 bytes[hll_constants::HLL_CUR_MIN_BYTE] =
static_cast<uint8_t
>(curMin_);
231 bytes[hll_constants::MODE_BYTE] = this->makeModeByte();
233 std::memcpy(bytes + hll_constants::HIP_ACCUM_DOUBLE, &hipAccum_,
sizeof(
double));
234 std::memcpy(bytes + hll_constants::KXQ0_DOUBLE, &kxq0_,
sizeof(
double));
235 std::memcpy(bytes + hll_constants::KXQ1_DOUBLE, &kxq1_,
sizeof(
double));
236 std::memcpy(bytes + hll_constants::CUR_MIN_COUNT_INT, &numAtCurMin_,
sizeof(uint32_t));
237 const uint32_t auxCount = (auxHashMap ==
nullptr ? 0 : auxHashMap->getAuxCount());
238 std::memcpy(bytes + hll_constants::AUX_COUNT_INT, &auxCount,
sizeof(uint32_t));
240 const uint32_t hllByteArrBytes = getHllByteArrBytes();
241 std::memcpy(bytes + getMemDataStart(), hllByteArr_.data(), hllByteArrBytes);
244 if (this->tgtHllType_ == HLL_4) {
245 bytes += getMemDataStart() + hllByteArrBytes;
246 if (auxHashMap !=
nullptr) {
248 for (
const uint32_t coupon: *auxHashMap) {
249 std::memcpy(bytes, &coupon,
sizeof(coupon));
250 bytes +=
sizeof(coupon);
253 std::memcpy(bytes, auxHashMap->getAuxIntArr(), auxHashMap->getUpdatableSizeBytes());
255 }
else if (!compact) {
257 uint32_t auxBytes = 4 << hll_constants::LG_AUX_ARR_INTS[this->lgConfigK_];
258 std::fill_n(bytes, auxBytes,
static_cast<uint8_t
>(0));
266void HllArray<A>::serialize(std::ostream& os,
bool compact)
const {
268 const uint8_t preInts = getPreInts();
270 const uint8_t serialVersion = hll_constants::SER_VER;
271 write(os, serialVersion);
272 const uint8_t familyId = hll_constants::FAMILY_ID;
274 const uint8_t lgKByte = this->lgConfigK_;
277 AuxHashMap<A>* auxHashMap = getAuxHashMap();
278 uint8_t lgArrByte = 0;
279 if (auxHashMap !=
nullptr) {
280 lgArrByte = auxHashMap->getLgAuxArrInts();
282 write(os, lgArrByte);
284 const uint8_t flagsByte = this->makeFlagsByte(compact);
285 write(os, flagsByte);
287 const uint8_t modeByte = this->makeModeByte();
291 write(os, hipAccum_);
296 write(os, numAtCurMin_);
298 const uint32_t auxCount = (auxHashMap ==
nullptr ? 0 : auxHashMap->getAuxCount());
300 write(os, hllByteArr_.data(), getHllByteArrBytes());
303 if (this->tgtHllType_ == HLL_4) {
304 if (auxHashMap !=
nullptr) {
306 for (
const uint32_t coupon: *auxHashMap) {
310 write(os, auxHashMap->getAuxIntArr(), auxHashMap->getUpdatableSizeBytes());
312 }
else if (!compact) {
314 uint32_t auxBytes = 4 << hll_constants::LG_AUX_ARR_INTS[this->lgConfigK_];
315 std::fill_n(std::ostreambuf_iterator<char>(os), auxBytes,
static_cast<char>(0));
321double HllArray<A>::getEstimate()
const {
323 return getCompositeEstimate();
344double HllArray<A>::getLowerBound(uint8_t numStdDev)
const {
345 HllUtil<A>::checkNumStdDev(numStdDev);
346 const uint32_t configK = 1 << this->lgConfigK_;
347 const double numNonZeros = ((curMin_ == 0) ? (configK - numAtCurMin_) : configK);
348 const double relErr = HllUtil<A>::getRelErr(
false, this->oooFlag_, this->lgConfigK_, numStdDev);
349 return fmax(getEstimate() / (1.0 + relErr), numNonZeros);
353double HllArray<A>::getUpperBound(uint8_t numStdDev)
const {
354 HllUtil<A>::checkNumStdDev(numStdDev);
355 const double relErr = HllUtil<A>::getRelErr(
true, this->oooFlag_, this->lgConfigK_, numStdDev);
356 return getEstimate() / (1.0 + relErr);
366double HllArray<A>::getCompositeEstimate()
const {
367 const double rawEst = getHllRawEstimate();
369 const double* xArr = CompositeInterpolationXTable<A>::get_x_arr(this->lgConfigK_);
370 const uint32_t xArrLen = CompositeInterpolationXTable<A>::get_x_arr_length();
371 const double yStride = CompositeInterpolationXTable<A>::get_y_stride(this->lgConfigK_);
373 if (rawEst < xArr[0]) {
377 const uint32_t xArrLenM1 = xArrLen - 1;
379 if (rawEst > xArr[xArrLenM1]) {
380 const double finalY = yStride * xArrLenM1;
381 const double factor = finalY / xArr[xArrLenM1];
382 return rawEst * factor;
385 double adjEst = CubicInterpolation<A>::usingXArrAndYStride(xArr, xArrLen, yStride, rawEst);
390 if (adjEst > (3 << this->lgConfigK_)) {
return adjEst; }
392 const double linEst = getHllBitMapEstimate();
399 const double avgEst = (adjEst + linEst) / 2.0;
403 double crossOver = 0.64;
404 if (this->lgConfigK_ == 4) { crossOver = 0.718; }
405 else if (this->lgConfigK_ == 5) { crossOver = 0.672; }
407 return (avgEst > (crossOver * (1 << this->lgConfigK_))) ? adjEst : linEst;
411double HllArray<A>::getKxQ0()
const {
416double HllArray<A>::getKxQ1()
const {
421double HllArray<A>::getHipAccum()
const {
426uint8_t HllArray<A>::getCurMin()
const {
431uint32_t HllArray<A>::getNumAtCurMin()
const {
436void HllArray<A>::putKxQ0(
double kxq0) {
441void HllArray<A>::putKxQ1(
double kxq1) {
446void HllArray<A>::putHipAccum(
double hipAccum) {
447 hipAccum_ = hipAccum;
451void HllArray<A>::putCurMin(uint8_t curMin) {
456void HllArray<A>::putNumAtCurMin(uint32_t numAtCurMin) {
457 numAtCurMin_ = numAtCurMin;
461bool HllArray<A>::isCompact()
const {
466bool HllArray<A>::isEmpty()
const {
470 if (rebuild_kxq_curmin_)
return false;
471 const uint32_t configK = 1 << this->lgConfigK_;
472 return (curMin_ == 0) && (numAtCurMin_ == configK);
476void HllArray<A>::putOutOfOrderFlag(
bool flag) {
481bool HllArray<A>::isOutOfOrderFlag()
const {
486uint32_t HllArray<A>::hllArrBytes(target_hll_type tgtHllType, uint8_t lgConfigK) {
487 switch (tgtHllType) {
489 return hll4ArrBytes(lgConfigK);
491 return hll6ArrBytes(lgConfigK);
493 return hll8ArrBytes(lgConfigK);
495 throw std::invalid_argument(
"Invalid target HLL type");
500uint32_t HllArray<A>::hll4ArrBytes(uint8_t lgConfigK) {
501 return 1 << (lgConfigK - 1);
505uint32_t HllArray<A>::hll6ArrBytes(uint8_t lgConfigK) {
506 const uint32_t numSlots = 1 << lgConfigK;
507 return ((numSlots * 3) >> 2) + 1;
511uint32_t HllArray<A>::hll8ArrBytes(uint8_t lgConfigK) {
512 return 1 << lgConfigK;
516uint32_t HllArray<A>::getMemDataStart()
const {
517 return hll_constants::HLL_BYTE_ARR_START;
521uint32_t HllArray<A>::getUpdatableSerializationBytes()
const {
522 return hll_constants::HLL_BYTE_ARR_START + getHllByteArrBytes();
526uint32_t HllArray<A>::getCompactSerializationBytes()
const {
527 AuxHashMap<A>* auxHashMap = getAuxHashMap();
528 const uint32_t auxCountBytes = ((auxHashMap ==
nullptr) ? 0 : auxHashMap->getCompactSizeBytes());
529 return hll_constants::HLL_BYTE_ARR_START + getHllByteArrBytes() + auxCountBytes;
533uint8_t HllArray<A>::getPreInts()
const {
534 return hll_constants::HLL_PREINTS;
538AuxHashMap<A>* HllArray<A>::getAuxHashMap()
const {
543auto HllArray<A>::getHllArray() const -> const vector_bytes& {
548void HllArray<A>::hipAndKxQIncrementalUpdate(uint8_t oldValue, uint8_t newValue) {
549 const uint32_t configK = 1 << this->getLgConfigK();
551 if (!oooFlag_) { hipAccum_ += configK / (kxq0_ + kxq1_); }
553 if (oldValue < 32) { kxq0_ -= INVERSE_POWERS_OF_2[oldValue]; }
554 else { kxq1_ -= INVERSE_POWERS_OF_2[oldValue]; }
555 if (newValue < 32) { kxq0_ += INVERSE_POWERS_OF_2[newValue]; }
556 else { kxq1_ += INVERSE_POWERS_OF_2[newValue]; }
566double HllArray<A>::getHllBitMapEstimate()
const {
567 const uint32_t configK = 1 << this->lgConfigK_;
568 const uint32_t numUnhitBuckets = curMin_ == 0 ? numAtCurMin_ : 0;
571 if (numUnhitBuckets == 0) {
572 return configK * fdlibm::log(configK / 0.5);
575 const uint32_t numHitBuckets = configK - numUnhitBuckets;
576 return HarmonicNumbers<A>::getBitMapEstimate(configK, numHitBuckets);
581double HllArray<A>::getHllRawEstimate()
const {
582 const uint32_t configK = 1 << this->lgConfigK_;
583 double correctionFactor;
584 if (this->lgConfigK_ == 4) { correctionFactor = 0.673; }
585 else if (this->lgConfigK_ == 5) { correctionFactor = 0.697; }
586 else if (this->lgConfigK_ == 6) { correctionFactor = 0.709; }
587 else { correctionFactor = 0.7213 / (1.0 + (1.079 / configK)); }
588 const double hyperEst = (correctionFactor * configK * configK) / (kxq0_ + kxq1_);
593void HllArray<A>::setRebuildKxqCurminFlag(
bool rebuild) {
594 rebuild_kxq_curmin_ = rebuild;
598bool HllArray<A>::isRebuildKxqCurminFlag()
const {
599 return rebuild_kxq_curmin_;
603void HllArray<A>::check_rebuild_kxq_cur_min() {
604 if (!rebuild_kxq_curmin_) {
return; }
607 if (this->getCurMode() != hll_mode::HLL || this->getTgtHllType() != target_hll_type::HLL_8) {
608 rebuild_kxq_curmin_ =
false;
612 uint32_t num_zeros = 0;
613 double kxq0 = 1 << this->lgConfigK_;
616 auto it = this->begin(
true);
617 const auto end = this->end();
619 uint8_t v = HllUtil<A>::getValue(*it);
621 if (v < 32) { kxq0 += INVERSE_POWERS_OF_2[v] - 1.0; }
622 else { kxq1 += INVERSE_POWERS_OF_2[v] - 1.0; }
635 numAtCurMin_ = num_zeros;
636 rebuild_kxq_curmin_ =
false;
642typename HllArray<A>::const_iterator HllArray<A>::begin(
bool all)
const {
643 return const_iterator(hllByteArr_.data(), 1 << this->lgConfigK_, 0, this->tgtHllType_,
nullptr, 0, all);
647typename HllArray<A>::const_iterator HllArray<A>::end()
const {
648 return const_iterator(hllByteArr_.data(), 1 << this->lgConfigK_, 1 << this->lgConfigK_, this->tgtHllType_,
nullptr, 0,
false);
652HllArray<A>::const_iterator::const_iterator(
const uint8_t* array, uint32_t array_size, uint32_t index, target_hll_type hll_type,
const AuxHashMap<A>* exceptions, uint8_t offset,
bool all):
653array_(array), array_size_(array_size), index_(index), hll_type_(hll_type), exceptions_(exceptions), offset_(offset), all_(all)
655 while (index_ < array_size_) {
656 value_ = get_value(array_, index_, hll_type_, exceptions_, offset_);
657 if (all_ || value_ != hll_constants::EMPTY) {
break; }
663typename HllArray<A>::const_iterator& HllArray<A>::const_iterator::operator++() {
664 while (++index_ < array_size_) {
665 value_ = get_value(array_, index_, hll_type_, exceptions_, offset_);
666 if (all_ || value_ != hll_constants::EMPTY) {
break; }
672bool HllArray<A>::const_iterator::operator!=(
const const_iterator& other)
const {
673 return index_ != other.index_;
677auto HllArray<A>::const_iterator::operator*() const -> reference {
678 return HllUtil<A>::pair(index_, value_);
682uint8_t HllArray<A>::const_iterator::get_value(
const uint8_t* array, uint32_t index, target_hll_type hll_type,
const AuxHashMap<A>* exceptions, uint8_t offset) {
685 if (hll_type == target_hll_type::HLL_4) {
686 uint8_t value = array[index >> 1];
687 if ((index & 1) > 0) {
690 value &= hll_constants::loNibbleMask;
692 if (value == hll_constants::AUX_TOKEN) {
693 return exceptions->mustFindValueFor(index);
695 return value + offset;
696 }
else if (hll_type == target_hll_type::HLL_6) {
697 const size_t start_bit = index * 6;
698 const uint8_t shift = start_bit & 0x7;
699 const size_t byte_idx = start_bit >> 3;
700 const uint16_t two_byte_val = (array[byte_idx + 1] << 8) | array[byte_idx];
701 return (two_byte_val >> shift) & hll_constants::VAL_MASK_6;
708A HllArray<A>::getAllocator()
const {
709 return hllByteArr_.get_allocator();
DataSketches namespace.
Definition binomial_bounds.hpp:38
target_hll_type
Specifies the target type of HLL sketch to be created.
Definition hll.hpp:72
@ HLL_6
6 bits per entry (fixed size)
Definition hll.hpp:74
@ HLL_8
8 bits per entry (fastest, fixed size)
Definition hll.hpp:75
@ HLL_4
4 bits per entry (most compact, size may vary)
Definition hll.hpp:73