Skip to content

Commit 96897a6

Browse files
authored
Merge pull request #364 from apache/hll_delay_kxq
Hll delay kxq
2 parents 993b622 + 089d232 commit 96897a6

11 files changed

Lines changed: 236 additions & 82 deletions

hll/include/Hll4Array-internal.hpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@ Hll4Array<A>::Hll4Array(const Hll4Array<A>& that) :
5151
}
5252
}
5353

54+
template<typename A>
55+
Hll4Array<A>::Hll4Array(const HllArray<A>& other) :
56+
HllArray<A>(other.getLgConfigK(), target_hll_type::HLL_4, other.isStartFullSize(), other.getAllocator()),
57+
auxHashMap_(nullptr)
58+
{
59+
const int numBytes = this->hll4ArrBytes(this->lgConfigK_);
60+
this->hllByteArr_.resize(numBytes, 0);
61+
this->oooFlag_ = other.isOutOfOrderFlag();
62+
63+
for (const auto coupon : other) { // all = false, so skip empty values
64+
internalCouponUpdate(coupon); // updates KxQ registers
65+
}
66+
this->hipAccum_ = other.getHipAccum();
67+
this->rebuild_kxq_curmin_ = false;
68+
}
69+
5470
template<typename A>
5571
Hll4Array<A>::~Hll4Array() {
5672
// hllByteArr deleted in parent
@@ -327,13 +343,6 @@ typename HllArray<A>::const_iterator Hll4Array<A>::end() const {
327343
this->tgtHllType_, auxHashMap_, this->curMin_, false);
328344
}
329345

330-
template<typename A>
331-
void Hll4Array<A>::mergeHll(const HllArray<A>& src) {
332-
for (const auto coupon: src) {
333-
internalCouponUpdate(coupon);
334-
}
335-
}
336-
337346
}
338347

339348
#endif // _HLL4ARRAY_INTERNAL_HPP_

hll/include/Hll4Array.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class Hll4Array final : public HllArray<A> {
3030
public:
3131
explicit Hll4Array(uint8_t lgConfigK, bool startFullSize, const A& allocator);
3232
explicit Hll4Array(const Hll4Array<A>& that);
33+
explicit Hll4Array(const HllArray<A>& that);
3334

3435
virtual ~Hll4Array();
3536
virtual std::function<void(HllSketchImpl<A>*)> get_deleter() const;
@@ -44,7 +45,6 @@ class Hll4Array final : public HllArray<A> {
4445
virtual uint32_t getHllByteArrBytes() const;
4546

4647
virtual HllSketchImpl<A>* couponUpdate(uint32_t coupon) final;
47-
void mergeHll(const HllArray<A>& src);
4848

4949
virtual AuxHashMap<A>* getAuxHashMap() const;
5050
// does *not* delete old map if overwriting

hll/include/Hll6Array-internal.hpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,25 @@ HllArray<A>(lgConfigK, target_hll_type::HLL_6, startFullSize, allocator)
3434
this->hllByteArr_.resize(numBytes, 0);
3535
}
3636

37+
template<typename A>
38+
Hll6Array<A>::Hll6Array(const HllArray<A>& other) :
39+
HllArray<A>(other.getLgConfigK(), target_hll_type::HLL_6, other.isStartFullSize(), other.getAllocator())
40+
{
41+
const int numBytes = this->hll6ArrBytes(this->lgConfigK_);
42+
this->hllByteArr_.resize(numBytes, 0);
43+
this->oooFlag_ = other.isOutOfOrderFlag();
44+
uint32_t num_zeros = 1 << this->lgConfigK_;
45+
46+
for (const auto coupon : other) { // all = false, so skip empty values
47+
num_zeros--;
48+
internalCouponUpdate(coupon); // updates KxQ registers
49+
}
50+
51+
this->numAtCurMin_ = num_zeros;
52+
this->hipAccum_ = other.getHipAccum();
53+
this->rebuild_kxq_curmin_ = false;
54+
}
55+
3756
template<typename A>
3857
std::function<void(HllSketchImpl<A>*)> Hll6Array<A>::get_deleter() const {
3958
return [](HllSketchImpl<A>* ptr) {
@@ -101,13 +120,6 @@ void Hll6Array<A>::internalCouponUpdate(uint32_t coupon) {
101120
}
102121
}
103122

104-
template<typename A>
105-
void Hll6Array<A>::mergeHll(const HllArray<A>& src) {
106-
for (const auto coupon: src) {
107-
internalCouponUpdate(coupon);
108-
}
109-
}
110-
111123
}
112124

113125
#endif // _HLL6ARRAY_INTERNAL_HPP_

hll/include/Hll6Array.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ template<typename A>
3131
class Hll6Array final : public HllArray<A> {
3232
public:
3333
Hll6Array(uint8_t lgConfigK, bool startFullSize, const A& allocator);
34+
explicit Hll6Array(const HllArray<A>& that);
3435

3536
virtual ~Hll6Array() = default;
3637
virtual std::function<void(HllSketchImpl<A>*)> get_deleter() const;
@@ -41,7 +42,6 @@ class Hll6Array final : public HllArray<A> {
4142
inline void putSlot(uint32_t slotNo, uint8_t value);
4243

4344
virtual HllSketchImpl<A>* couponUpdate(uint32_t coupon) final;
44-
void mergeHll(const HllArray<A>& src);
4545

4646
virtual uint32_t getHllByteArrBytes() const;
4747

hll/include/Hll8Array-internal.hpp

Lines changed: 94 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,25 @@ HllArray<A>(lgConfigK, target_hll_type::HLL_8, startFullSize, allocator)
3232
this->hllByteArr_.resize(numBytes, 0);
3333
}
3434

35+
template<typename A>
36+
Hll8Array<A>::Hll8Array(const HllArray<A>& other):
37+
HllArray<A>(other.getLgConfigK(), target_hll_type::HLL_8, other.isStartFullSize(), other.getAllocator())
38+
{
39+
const int numBytes = this->hll8ArrBytes(this->lgConfigK_);
40+
this->hllByteArr_.resize(numBytes, 0);
41+
this->oooFlag_ = other.isOutOfOrderFlag();
42+
uint32_t num_zeros = 1 << this->lgConfigK_;
43+
44+
for (const auto coupon : other) { // all = false, so skip empty values
45+
num_zeros--;
46+
internalCouponUpdate(coupon); // updates KxQ registers
47+
}
48+
49+
this->numAtCurMin_ = num_zeros;
50+
this->hipAccum_ = other.getHipAccum();
51+
this->rebuild_kxq_curmin_ = false;
52+
}
53+
3554
template<typename A>
3655
std::function<void(HllSketchImpl<A>*)> Hll8Array<A>::get_deleter() const {
3756
return [](HllSketchImpl<A>* ptr) {
@@ -95,49 +114,88 @@ void Hll8Array<A>::mergeList(const CouponList<A>& src) {
95114
template<typename A>
96115
void Hll8Array<A>::mergeHll(const HllArray<A>& src) {
97116
// at this point src_k >= dst_k
98-
const uint32_t dst_mask = (1 << this->getLgConfigK()) - 1;
99-
// special treatment below to optimize performance
100-
if (src.getTgtHllType() == target_hll_type::HLL_8) {
101-
uint32_t i = 0;
102-
for (const auto value: src.getHllArray()) {
103-
processValue(i++, dst_mask, value);
104-
}
105-
} else if (src.getTgtHllType() == target_hll_type::HLL_6) {
106-
const uint32_t src_k = 1 << src.getLgConfigK();
107-
uint32_t i = 0;
108-
const uint8_t* ptr = src.getHllArray().data();
109-
while (i < src_k) {
110-
uint8_t value = *ptr & 0x3f;
111-
processValue(i++, dst_mask, value);
112-
value = *ptr++ >> 6;
113-
value |= (*ptr & 0x0f) << 2;
114-
processValue(i++, dst_mask, value);
115-
value = *ptr++ >> 4;
116-
value |= (*ptr & 3) << 4;
117-
processValue(i++, dst_mask, value);
118-
value = *ptr++ >> 2;
119-
processValue(i++, dst_mask, value);
117+
// we can optimize further when the k values are equal
118+
if (this->getLgConfigK() == src.getLgConfigK()) {
119+
if (src.getTgtHllType() == target_hll_type::HLL_8) {
120+
uint32_t i = 0;
121+
for (const auto value: src.getHllArray()) {
122+
this->hllByteArr_[i] = std::max(this->hllByteArr_[i], value);
123+
++i;
124+
}
125+
} else if (src.getTgtHllType() == target_hll_type::HLL_6) {
126+
const uint32_t src_k = 1 << src.getLgConfigK();
127+
uint32_t i = 0;
128+
const uint8_t* ptr = src.getHllArray().data();
129+
while (i < src_k) {
130+
uint8_t value = *ptr & 0x3f;
131+
this->hllByteArr_[i] = std::max(this->hllByteArr_[i], value);
132+
++i;
133+
value = *ptr++ >> 6;
134+
value |= (*ptr & 0x0f) << 2;
135+
this->hllByteArr_[i] = std::max(this->hllByteArr_[i], value);
136+
++i;
137+
value = *ptr++ >> 4;
138+
value |= (*ptr & 3) << 4;
139+
this->hllByteArr_[i] = std::max(this->hllByteArr_[i], value);
140+
++i;
141+
value = *ptr++ >> 2;
142+
this->hllByteArr_[i] = std::max(this->hllByteArr_[i], value);
143+
++i;
144+
}
145+
} else { // HLL_4
146+
const auto& src4 = static_cast<const Hll4Array<A>&>(src);
147+
uint32_t i = 0;
148+
for (const auto byte: src.getHllArray()) {
149+
this->hllByteArr_[i] = std::max(this->hllByteArr_[i], src4.adjustRawValue(i, byte & hll_constants::loNibbleMask));
150+
++i;
151+
this->hllByteArr_[i] = std::max(this->hllByteArr_[i], src4.adjustRawValue(i, byte >> 4));
152+
++i;
153+
}
120154
}
121-
} else { // HLL_4
122-
const auto& src4 = static_cast<const Hll4Array<A>&>(src);
123-
uint32_t i = 0;
124-
for (const auto byte: src.getHllArray()) {
125-
processValue(i, dst_mask, src4.adjustRawValue(i, byte & hll_constants::loNibbleMask));
126-
++i;
127-
processValue(i, dst_mask, src4.adjustRawValue(i, byte >> 4));
128-
++i;
155+
} else {
156+
// src_k > dst_k
157+
const uint32_t dst_mask = (1 << this->getLgConfigK()) - 1;
158+
// special treatment below to optimize performance
159+
if (src.getTgtHllType() == target_hll_type::HLL_8) {
160+
uint32_t i = 0;
161+
for (const auto value: src.getHllArray()) {
162+
processValue(i++, dst_mask, value);
163+
}
164+
} else if (src.getTgtHllType() == target_hll_type::HLL_6) {
165+
const uint32_t src_k = 1 << src.getLgConfigK();
166+
uint32_t i = 0;
167+
const uint8_t* ptr = src.getHllArray().data();
168+
while (i < src_k) {
169+
uint8_t value = *ptr & 0x3f;
170+
processValue(i++, dst_mask, value);
171+
value = *ptr++ >> 6;
172+
value |= (*ptr & 0x0f) << 2;
173+
processValue(i++, dst_mask, value);
174+
value = *ptr++ >> 4;
175+
value |= (*ptr & 3) << 4;
176+
processValue(i++, dst_mask, value);
177+
value = *ptr++ >> 2;
178+
processValue(i++, dst_mask, value);
179+
}
180+
} else { // HLL_4
181+
const auto& src4 = static_cast<const Hll4Array<A>&>(src);
182+
uint32_t i = 0;
183+
for (const auto byte: src.getHllArray()) {
184+
processValue(i, dst_mask, src4.adjustRawValue(i, byte & hll_constants::loNibbleMask));
185+
++i;
186+
processValue(i, dst_mask, src4.adjustRawValue(i, byte >> 4));
187+
++i;
188+
}
129189
}
130190
}
191+
this->setRebuildKxqCurminFlag(true);
131192
}
132193

194+
133195
template<typename A>
134196
void Hll8Array<A>::processValue(uint32_t slot, uint32_t mask, uint8_t new_val) {
135-
const uint8_t old_val = this->hllByteArr_[slot & mask];
136-
if (new_val > old_val) {
137-
this->hllByteArr_[slot & mask] = new_val;
138-
this->hipAndKxQIncrementalUpdate(old_val, new_val);
139-
this->numAtCurMin_ -= old_val == 0;
140-
}
197+
const size_t index = slot & mask;
198+
this->hllByteArr_[index] = std::max(this->hllByteArr_[index], new_val);
141199
}
142200

143201
}

hll/include/Hll8Array.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ template<typename A>
3131
class Hll8Array final : public HllArray<A> {
3232
public:
3333
Hll8Array(uint8_t lgConfigK, bool startFullSize, const A& allocator);
34+
explicit Hll8Array(const HllArray<A>& that);
3435

3536
virtual ~Hll8Array() = default;
3637
virtual std::function<void(HllSketchImpl<A>*)> get_deleter() const;

hll/include/HllArray-internal.hpp

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,44 @@ kxq1_(0.0),
4343
hllByteArr_(allocator),
4444
curMin_(0),
4545
numAtCurMin_(1 << lgConfigK),
46-
oooFlag_(false)
46+
oooFlag_(false),
47+
rebuild_kxq_curmin_(false)
48+
{}
49+
50+
template<typename A>
51+
HllArray<A>::HllArray(const HllArray& other, target_hll_type tgtHllType) :
52+
HllSketchImpl<A>(other.getLgConfigK(), tgtHllType, hll_mode::HLL, other.isStartFullSize()),
53+
// remaining fields are initialized to empty sketch defaults
54+
// and left to subclass constructor to populate
55+
hipAccum_(0.0),
56+
kxq0_(1 << other.getLgConfigK()),
57+
kxq1_(0.0),
58+
hllByteArr_(other.getAllocator()),
59+
curMin_(0),
60+
numAtCurMin_(1 << other.getLgConfigK()),
61+
oooFlag_(false),
62+
rebuild_kxq_curmin_(false)
4763
{}
4864

4965
template<typename A>
5066
HllArray<A>* HllArray<A>::copyAs(target_hll_type tgtHllType) const {
51-
if (tgtHllType == this->getTgtHllType()) {
67+
// we may need to recompute KxQ and curMin data for a union gadget,
68+
// so only use a direct copy if we have a valid sketch
69+
if (tgtHllType == this->getTgtHllType() && !this->isRebuildKxqCurminFlag()) {
5270
return static_cast<HllArray*>(copy());
5371
}
54-
if (tgtHllType == target_hll_type::HLL_4) {
55-
return HllSketchImplFactory<A>::convertToHll4(*this);
56-
} else if (tgtHllType == target_hll_type::HLL_6) {
57-
return HllSketchImplFactory<A>::convertToHll6(*this);
58-
} else { // tgtHllType == HLL_8
59-
return HllSketchImplFactory<A>::convertToHll8(*this);
72+
73+
// the factory methods replay the coupons and will always rebuild
74+
// the sketch in a consistent way
75+
switch (tgtHllType) {
76+
case target_hll_type::HLL_4:
77+
return HllSketchImplFactory<A>::convertToHll4(*this);
78+
case target_hll_type::HLL_6:
79+
return HllSketchImplFactory<A>::convertToHll6(*this);
80+
case target_hll_type::HLL_8:
81+
return HllSketchImplFactory<A>::convertToHll8(*this);
82+
default:
83+
throw std::invalid_argument("Invalid target HLL type");
6084
}
6185
}
6286

@@ -562,6 +586,52 @@ double HllArray<A>::getHllRawEstimate() const {
562586
return hyperEst;
563587
}
564588

589+
template<typename A>
590+
void HllArray<A>::setRebuildKxqCurminFlag(bool rebuild) {
591+
rebuild_kxq_curmin_ = rebuild;
592+
}
593+
594+
template<typename A>
595+
bool HllArray<A>::isRebuildKxqCurminFlag() const {
596+
return rebuild_kxq_curmin_;
597+
}
598+
599+
template<typename A>
600+
void HllArray<A>::check_rebuild_kxq_cur_min() {
601+
if (!rebuild_kxq_curmin_) { return; }
602+
603+
uint8_t cur_min = 64;
604+
uint32_t num_at_cur_min = 0;
605+
double kxq0 = 1 << this->lgConfigK_;
606+
double kxq1 = 0;
607+
608+
auto it = this->begin(true); // want all points to adjust cur_min
609+
const auto end = this->end();
610+
while (it != end) {
611+
uint8_t v = HllUtil<A>::getValue(*it);
612+
if (v > 0) {
613+
if (v < 32) { kxq0 += INVERSE_POWERS_OF_2[v] - 1.0; }
614+
else { kxq1 += INVERSE_POWERS_OF_2[v] - 1.0; }
615+
}
616+
if (v > cur_min) { ++it; continue; }
617+
if (v < cur_min) {
618+
cur_min = v;
619+
num_at_cur_min = 1;
620+
} else {
621+
++num_at_cur_min;
622+
}
623+
++it;
624+
}
625+
626+
kxq0_ = kxq0;
627+
kxq1_ = kxq1;
628+
curMin_ = cur_min;
629+
numAtCurMin_ = num_at_cur_min;
630+
rebuild_kxq_curmin_ = false;
631+
// HipAccum is not affected
632+
633+
}
634+
565635
template<typename A>
566636
typename HllArray<A>::const_iterator HllArray<A>::begin(bool all) const {
567637
return const_iterator(hllByteArr_.data(), 1 << this->lgConfigK_, 0, this->tgtHllType_, nullptr, 0, all);
@@ -604,6 +674,8 @@ auto HllArray<A>::const_iterator::operator*() const -> reference {
604674

605675
template<typename A>
606676
uint8_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) {
677+
// TODO: we should be able to improve efficiency here by reading multiple bytes at a time
678+
// for HLL4 and HLL6
607679
if (hll_type == target_hll_type::HLL_4) {
608680
uint8_t value = array[index >> 1];
609681
if ((index & 1) > 0) { // odd

0 commit comments

Comments
 (0)