Skip to content
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

RFC : Implementation of rol! and ror! #9822

Merged
merged 1 commit into from
Feb 7, 2015
Merged

RFC : Implementation of rol! and ror! #9822

merged 1 commit into from
Feb 7, 2015

Conversation

amitjamadagni
Copy link
Contributor

I am new to the community. I have worked on the addition of rol! and ror!. It would be really helpful to hear some comments from the community. Thanks.

@ViralBShah
Copy link
Member

Cc: @carlobaldassi

@carlobaldassi
Copy link
Member

These definitions do the job, however the usual definitions of in-place mutating operators take advantage of the fact that you can use them to avoid allocating memory.

The versions as written in this PR instead offer a very small advantage over writing A = rol(A, i), since they basically are syntactic sugar for that.

So I think we'd rather have functions rol!(dest::BitVector, src::BitVector, i::Integer) which use a single UInt64 buffer to do their job.

However, out of curiosity: @amitjamadagni, do you have a need for these functions? I don't think I've ever seen them come up in code, and they are not defined for anything but BitArrays.

(A minor comment on the code is that the copy_chunks! calls can become simply B.chunks = A.chunks, which is simpler and faster, although it makes it even more clear that there's wasted allocation there.)

In any case, tests and documentation are also always needed whenever adding functionality.

@amitjamadagni
Copy link
Contributor Author

@carlobaldassi I had no direct requirement of function. I was going through some of the TODO tasks and this was one of them.
I thought retaining the parameters of ror, rol for ror!, rol! was more relevant. I do get the point that is extra usage of memory. Anyways please do let me know about the format of such functions. I would add the tests and documentation as soon as possible. Thanks.

@carlobaldassi
Copy link
Member

Upon thinking about it a little more, I think we'd need 2 signatures. The first one would be the in-place one, rol!(B::BitArray, i::Integer).

The second one would be rol!(dest::BitVector, src::BitVector, i::Integer) and would be implemented in such a way that dest = rol(src, i), except that dest would not be a newly allocated BitArray but rather an existing one, just like e.g. copy!(dest::BitArray, src::BitArray) vs dest = copy(src::BitArray). The length of dest and src should be the same. The code would check if dest === src, and call the in-place method in such case; otherwise it could reuse the same code which we have now.

The current rol and ror could then be reimplemented in terms of the new mutating versions, i.e. rol(B, i) via rol!(similar(B), B, i) etc.

Actually, one potential problem with this is that even if dest !== src, they could still be aliasing the same underlying memory, or overlapping portions (e.g. via mmapping). I'm not sure if we can deal with that.

Also, the actual implementation of the in-place version can be quite tricky, as it should cycle through the chunks jumping around and deal with situations of potential overlap. Take the simplest situation, in which we want to rotate a BitArray B, whose length is a multiple of 64, by i=64n bits with n != 0; then we should do something like:

Bc = B.chunks
lBc = length(B.chunks)
for j0 = 1:(lBc % n == 0 ? div(lBc, n) - 1 : 1)
    j = j0
    x = Bc[j]
    while true
        k = mod(j - n - 1, length(Bc)) + 1
        y = Bc[k]
        Bc[k] = x
        x, j = y, k
        j == j0 && break
    end
end

i.e. cover the whole array with cycles of step n (we need div(lBc,n)-1 such cycles if lBc is divisible by n, just one otherwise).

The general case where the rotation parameter and the length are not a multiple of 64 is more complicated and should use copy_chunks! probably, using a couple of auxiliary chunk vectors of length 1 for x and y (see e.g. the reverse!(::BitVector) function) and paying attention at what happens at the end of the BitArray.

(note I didn't test the above code)

@amitjamadagni
Copy link
Contributor Author

@carlobaldassi Thanks for the inputs on this. I will work on it and send in some commits with my understanding.

@amitjamadagni
Copy link
Contributor Author

@carlobaldassi Here is an implementation I have attempted. Please do let me know your thoughts on it.
https://gist.github.com/amitjamadagni/b27ecc63f043b40678f4
The case of memory overlap, I am still thinking through and hopefully I can make some progress at it.

@carlobaldassi
Copy link
Member

The ror! method should check that length(dest) == length(src).
Some simpler logic could be used in the branching, i.e. something like Bc == (src === dest ? copy(src.chunks) : src.chunks) and then you'd call the copy_chunks! methods with Bc in the third argument instead of src.chunks or Bc.chunks which you have now.

What is this about:

#edge-case failure if B is all false in the case of ror.

?

@amitjamadagni
Copy link
Contributor Author

@carlobaldassi thanks. I will work on the inputs. My apologies for the comment. I had a doubt but it stands clarified.

@amitjamadagni
Copy link
Contributor Author

@carlobaldassi I have been looking at the failing tests. Should they be based on the idea of how splice! is being tested ??

@@ -1272,29 +1272,45 @@ end
(>>)(B::BitVector, i::Int32) = B >>> i
(>>)(B::BitVector, i::Integer) = B >>> i

function rol!(dest::BitVector, src::BitVector, i::Integer)
if length(dest) == length(src)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check should throw an error in case it's false, rather then continuing silently

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's more readable if you write it as length(dest) == length(src) || trhrow(...) and then de-indent the following lines.

@amitjamadagni
Copy link
Contributor Author

@carlobaldassi could you please review this. The test cases seem to work. Thanks.

@@ -637,13 +637,29 @@ BitArrays

Performs a bitwise not operation on B. See :ref:`~ operator <~>`.

.. function:: rol!(dest::BitArray{1},src::BitArray{1},i::Integer) -> BitArray{1}

Performs a left rotation operation on dest.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not very clear, even if t's self-evident from the argument names the description should state that the rotation is performed on src and put into dest.

Also a very minor nitpick: please put a space between the function arguments after the commas - here and below.

@carlobaldassi
Copy link
Member

@amitjamadagni
I added a couple of minor comments. Besides that, the new functionality should include specific tests, e.g. application in-place to some BitVector etc.

Are you planning to do an attempt at the non-copying in-place version? If not, just add a TODO note in a comment and I'll probably try that when I have some time, so we can merge this in the meanwhile. Otherwise we can wait.

@amitjamadagni
Copy link
Contributor Author

@carlobaldassi seems a time out on the build, therefore failing. Anyways a review on this would be helpful.

@amitjamadagni
Copy link
Contributor Author

@carlobaldassi done.

@ViralBShah
Copy link
Member

It would be nice to squash all the commits.

commit 28bcfc0
Author: amitjamadagni <[email protected]>
Date:   Wed Jan 28 16:31:08 2015 +0530

    Further edits.

commit 7bbafd9
Author: amitjamadagni <[email protected]>
Date:   Wed Jan 28 01:17:16 2015 +0530

    RFC : Test cases added. Docs edited.

commit 587ee84
Author: amitjamadagni <[email protected]>
Date:   Thu Jan 22 02:46:28 2015 +0530

    Edits.

commit 69e19e1
Author: amitjamadagni <[email protected]>
Date:   Wed Jan 21 02:06:35 2015 +0530

    Docs added. WIP : Further cases, tests.

commit 8397aac
Author: amitjamadagni <[email protected]>
Date:   Wed Jan 21 01:32:16 2015 +0530

    WIP : Further Edits.

commit c84cb6e
Author: amitjamadagni <[email protected]>
Date:   Sun Jan 18 01:22:16 2015 +0530

    RFC : Implementation of rol! and ror!
Sqaushed : RFC :: Implementation of rol! and ror!.
@amitjamadagni
Copy link
Contributor Author

@ViralBShah done.

@amitjamadagni
Copy link
Contributor Author

@carlobaldassi : any comments on the commit would be really helpful.

@carlobaldassi
Copy link
Member

Sorry for the delay, I've been away with no internet connection.

carlobaldassi added a commit that referenced this pull request Feb 7, 2015
RFC : Implementation of rol! and ror!
@carlobaldassi carlobaldassi merged commit 9c85b25 into JuliaLang:master Feb 7, 2015
@amitjamadagni
Copy link
Contributor Author

@carlobaldassi : Thanks for all the help and also for the merge.

@stevengj
Copy link
Member

stevengj commented Apr 22, 2016

I just noticed these functions. As @carlobaldassi mentioned, it would be better if these truly operated in-place for src === dest, in which case you could have a single-argument version of the function.

I don't know if you are aware of this, but there is actually a relatively simple trick (which I learned from @matteo-frigo) for cyclically shifting an array A (in this case the chunks array) by an arbitrary amount 0 < i < length(A):

  • First: reverse!(A, 1, i) and reverse!(A, i+1, length(A)).
  • Second: reverse!(A)

This requires two passes over the array. Theoretically, you can do it in a single pass by chasing the cycles of the permutation, but in practice the two-pass trick is far more efficient, both because chasing the cycles involves a lot of auxiliary computation and because reverse! has much better spatial locality (hence better cache-line utilization).

Might be nice to apply this trick define ror!(a) and rol!(a) functions.

@stevengj
Copy link
Member

(Similarly, one could define circshift! as an in-place version of our current circshift function.)

@matteo-frigo
Copy link

(See also Knuth Vol. 1 3rd ed., Section 1.3.3 Exercises 34, 35, and 36. The trick is in exercise 36.)

@carlobaldassi
Copy link
Member

@stevengj good idea, I'll get to it soon-ish (if no one beats me to it).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants