-
Notifications
You must be signed in to change notification settings - Fork 241
more efficient UnsetIterator implementation #501
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
more efficient UnsetIterator implementation #501
Conversation
Also add some property-based tests with a small corpus of values. Signed-off-by: Roger Peppe <[email protected]>
c165abe to
9ebb1c3
Compare
arraycontainer.go
Outdated
| return &shortIterator{ac.content, 0} | ||
| } | ||
|
|
||
| type arrayContainerUnsetIterator struct { |
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.
So, in arraycontainer, I think we don't define iterator types, they are in shortiterator.go. I think it would be more consistent to move the code there, with the other iterators.
(Of course, this changes nothing with respect to the functionality.)
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.
done
arraycontainer.go
Outdated
|
|
||
| func newArrayContainerUnsetIterator(a *arrayContainer) *arrayContainerUnsetIterator { | ||
| acui := &arrayContainerUnsetIterator{content: a.content, pos: 0, nextVal: 0} | ||
| for acui.pos < len(acui.content) && uint16(acui.nextVal) == acui.content[acui.pos] { |
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.
| for acui.pos < len(acui.content) && uint16(acui.nextVal) == acui.content[acui.pos] { | |
| for acui.pos < len(acui.content) && uint16(acui.nextVal) >= acui.content[acui.pos] { |
I think that this would be clearer?
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.
done
arraycontainer.go
Outdated
| if acui.pos < 0 { | ||
| acui.pos = -acui.pos - 1 | ||
| } | ||
| for acui.pos < len(acui.content) && uint16(acui.nextVal) == acui.content[acui.pos] { |
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.
| for acui.pos < len(acui.content) && uint16(acui.nextVal) == acui.content[acui.pos] { | |
| for acui.pos < len(acui.content) && uint16(acui.nextVal) >= acui.content[acui.pos] { |
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.
done
arraycontainer.go
Outdated
| func (acui *arrayContainerUnsetIterator) next() uint16 { | ||
| val := acui.nextVal | ||
| acui.nextVal++ | ||
| for acui.pos < len(acui.content) && uint16(acui.nextVal) == acui.content[acui.pos] { |
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.
| for acui.pos < len(acui.content) && uint16(acui.nextVal) == acui.content[acui.pos] { | |
| for acui.pos < len(acui.content) && uint16(acui.nextVal) >= acui.content[acui.pos] { |
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.
Technically, this could be inefficient because it may force you to scan all the values in the array for naught in the user only wanted the unset bits in a range that does not contain a lot of set bits. I think that this might be fine in practice.
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.
ack
arraycontainer.go
Outdated
| type arrayContainerUnsetIterator struct { | ||
| content []uint16 | ||
| pos int | ||
| nextVal int |
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.
Could be useful to add a comment. pos is the position of a set bit that is larger than nextVal. Once nextVal gets to content[pos], we must increment pos. Something like that.
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.
done
arraycontainer.go
Outdated
| return | ||
| } | ||
| acui.nextVal = int(minval) | ||
| acui.pos = binarySearch(acui.content, minval) |
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.
Ok. So if pos is positive after this, then we have that acui.content[pos] = minval, but if not, then we have that acui.content[pos] > minval (assuming that acui.content[pos] exists).
So we have that minval >= acui.content[pos]
and
So we have that acui.nextVal >= acui.content[pos]
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.
Is this a suggestion for a change, to add a comment, or just an aside explaining your understanding of the logic?
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.
It explains my other suggestions.
runcontainer.go
Outdated
| func (rui *runUnsetIterator16) next() uint16 { | ||
| val := rui.nextVal | ||
| rui.nextVal++ | ||
| if rui.curIndex < len(rui.rc.iv) && uint16(rui.nextVal) == rui.rc.iv[rui.curIndex].start { |
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.
| if rui.curIndex < len(rui.rc.iv) && uint16(rui.nextVal) == rui.rc.iv[rui.curIndex].start { | |
| if rui.curIndex < len(rui.rc.iv) && uint16(rui.nextVal) >= rui.rc.iv[rui.curIndex].start { |
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.
done
|
This looks good to me. I have minor comments that you may consider. Otherwise, we shall merge. |
Signed-off-by: Roger Peppe <[email protected]>
This change aligns the efficiency of UnsetIterator with that of
Iterator: it runs in a time proportional to the number
of unset bits, not the number of set bits.
The benchmark added in this test ran at about 30s per iteration
previously and now runs in about 1.2ms.
Also add some property-based tests with a small corpus of values.
Description
Please provide a brief description of the changes made in this pull request.
Type of Change
Changes Made
What was changed?
Why was it changed?
How was it changed?
Performance Impact
Related Issues
Fixes #497