-
Notifications
You must be signed in to change notification settings - Fork 269
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update flip-type operations of Roaring64Map #402
Conversation
05f92b9
to
ff8dec4
Compare
ff8dec4
to
3c060a3
Compare
ad48699
to
bd994ae
Compare
@SLieve Would you review ? |
Sure! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with adding flipClosed
to the public api, @lemire what do you think?
// Since min and max are uint32_t, highbytes(min or max) == 0. The inner | ||
// bitmap we are looking for, if it exists, will be at the first slot of | ||
// 'roarings'. | ||
if (iter == roarings.end() || iter->first != 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is correct. If I have an empty Roaring64Map
and I call flipClosed(0, 10)
, I would expect the range from 0 to 10 to be set. Let's add a test for this case too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arrrgh. Great catch!
cpp/roaring64map.hh
Outdated
roarings[start_high].setCopyOnWrite(copyOnWrite); | ||
// 2. Flip intermediate bitmaps completely... | ||
for (uint32_t i = 0; i != num_intermediate_bitmaps; ++i) { | ||
auto &bitmap = start_iter->second; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional: Could we add a small comment here which mentions why we can assume all bitmaps in the range exist? Something like:
// We can directly use the iterator for the entire range, because we made sure it was populated above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a comment above the call to ensureRangePopulated()
, making it consistent with the corresponding comment in addRangeClosed()
pending in #397. Let me know if you think this is good enough.
tests/cpp_unit.cpp
Outdated
// For example (assuming num_slots_to_test = 5), we: | ||
// create a Roaring64Map, (do nothing), flip 5 slots, and check | ||
// Then we: | ||
// create a Roaring64Map, set a bit in slot 0, flip 5 slots, and check | ||
// Then we: | ||
// create a Roaring64Map, set a bit in slot 1, flip 5 slots, and check | ||
// Then we: | ||
// create a Roaring64Map, set a bit in slots 0 and 1, flip 5 slots, and check | ||
// etc. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional: Same comment as in #397, this may read better as a numbered list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redid the comment to look like the (redone) comment in #397. Please take another look.
@kosak I think you forgot to push. |
Sorry about that. I didn't actually "forget" per se but I clicked through the comments and then I thought I could finish up the code before you got here. I will make sure to do it in the other order next time 😃 Please take another look. |
38f11d9
to
e9211ec
Compare
…alls completely into slot 0, delegate to 32-bit flipClosed() rather than 64-bit flipClosed().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me! @lemire for feedback on adding flipClosed
to the API (see above).
Sorry, I added one more thing (see 7662b3a ) The rationale here is to put half-open Let me know if you think this is going too far. Another consistent view is that neither should be a special case, and both should just delegate to the "workhorse" method
Put another way, I'm arguing that for consistency's sake they should either both have this special-case optimization, or neither should. My opinion is they both should. |
I can see the symmetry argument, but on the other hand it's also a bit more code, and with |
…e that falls" This reverts commit 7662b3a.
Yeah, I see your point. Let's revert that change. (done) |
flipClosed is fine as far as I am concerned. |
Added another commit to fix a logic typo. |
@SLieve Do you recommend merging this? |
Merging. |
Executive summary:
flip
on both the 32-bit and 64-bit side to make it uniform withaddRange
andremoveRange
emplace_hint
to minimize map lookups. The resultant code does only one lookup for the whole operation; everything else is just linear iteration through the specified map range, which is cool.flip()
does the same "remove bitmaps if they become empty" logic that the other operations do now.I like how this one turned out. Kindly let me know what you think.
Some rationale on adding the entry points:
First, provides API uniformity for the end-user, which is always nice.
Second, provides uniformity for the code reader. The style of all of these is to have a couple of "easy" methods and then a longer one that does all the work. But here, the workhorse methods are
addRangeClosed(uint64_t, uint64_t)
andremoveRangeClosed(uint64_t uint64_t)
which work with closed intervals, but then we haveflip(uint64_t, uint64_t)
working with half-open intervals. When the convention changes like that, it's a little harder to analyze.Also,
flip()
had a couple of logic errors:start_high == end_high
I feel rewriting
flip()
in a style coherent with all the others hopefully makes it easier to understand and use. Kindly let me know what you think.