Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Computing modulus in parallel using bit manipulation

The following link is to a bit hack that shows how to compute the modulus by 2^n - 1 in parallel: ModulusDivisionParallel

Can you explain how this bit manipulation works, and how to unroll the loop shown given a specific denominator (see example below, where do the bit masks come from)?

Example of unrolling the loop for 0xF:

y = x mod 0xF
y = x & 0x0F0F0F0F + ((x & 0xF0F0F0F0) >> 4)  
y = y & 0x00FF00FF + ((y & 0xFF00FF00) >> 8)  
y = y & 0x0000FFFF + ((y & 0xFFFF0000) >> 16)  
y = y & 0xF
like image 933
Tony Abboud Avatar asked Nov 13 '14 22:11

Tony Abboud


People also ask

What is bit manipulation in data structure?

Bit manipulation is the act of algorithmically manipulating bits or other pieces of data shorter than a word. Computer programming tasks that require bit manipulation include low-level device control, error detection and correction algorithms, data compression, encryption algorithms, and optimization.

Is bit manipulation hard?

Bit manipulation, in some cases, can obviate or reduce the need to loop over a data structure and can give many-fold speed-ups, as bit manipulations are processed in parallel, but the code can become more difficult to write and maintain.

What is bit manipulation in CPP?

Bit is a binary digit. It is the smallest unit of data that is understandable by the computer. In can have only one of the two values 0 (denotes OFF) and 1 (denotes ON). Bitwise operators are the operators that work a bit level in the program. These operators are used to manipulate bits in the program.


1 Answers

First, a clarification:

When s = 4 (that is, the modulus is equal to 0xF) I get the following unrolling:

m = (n & 0x0F0F0F0F) + ((n >> 4) & 0x0F0F0F0F)
m = ((n >> 16) + (n & 0x0000FFFF)
m = ((n >>  8) + (n & 0x000000FF)
m = ((n >>  4) + (n & 0x0000000F)
m = ((n >>  4) + (n & 0x0000000F)
m = ((n >>  4) + (n & 0x0000000F)
m = ((n >>  4) + (n & 0x0000000F)
m = m == 0xF ? 0 : m;

This is very different than what you have in your question. To explain why this works:

Ever heard of the math trick where if you add up all the digits of a number and it's divisible by 9 then the original number is, too? That works because the remainders of dividing both the original and the sum by 9 is the same. Actually, that's what we're doing here, just in a different base -- in your example, with hexadecimal base.

The math kung-fu is this:

Each hexadecimal digit's contribution to the final value can be represented as V * 16 ^ P. Note that 16 ^ P = 1 (mod 15), so each hexadecimal digit's contribution to the final value is simply V (mod 15). In other words, to get the total contribution from all the digits, add them all up (mod 15).

The bitwise operations are just a clever way of doing this in logarithmic number of steps: repeatedly add the first half of the hexadecimal digits to the second half.

The problem with the 9's trick is you might end up with a two digit number: 99 = 9 + 9 = 18 (mod 10)! Then you just do the trick again: 18 = 1 + 8 = 9 (mod 10).

Likewise, we follow up with 'extra' iterations of m = ((n >> 4) + (n & 0x0000000F) until the remaining number is one digit.

Now the only remaining detail is if we get 0xF as the result in which case we want 0x0 instead.

like image 109
Kaganar Avatar answered Oct 23 '22 19:10

Kaganar