On a modern x86-64 CPU, a & b executes in one clock cycle. So does a | b, a ^ b, and a << 3. Multiplication takes three to five cycles. Division takes twenty to eighty. This is not a compiler optimization — the CPU itself is wired this way. Understanding why requires a brief look at what is physically happening inside the ALU. Want to see the binary as you experiment? Our bitwise calculator shows the result in binary, decimal, and hex for every operation.
How the ALU Wires Up Bitwise Operations
The ALU (Arithmetic Logic Unit) is a block of combinational logic — no clock, no state, no sequencing. Feed it inputs, and the outputs stabilize after one propagation delay through a handful of logic gates. For a 64-bit AND: place 64 independent AND gates in parallel, one for each bit position. Each gate has two inputs (bit N from operand A, bit N from operand B) and one output. The signal travels through exactly one gate. That is it. No carry propagation (unlike addition), no iterative approximation (unlike division), no partial product accumulation (unlike multiplication). The gate delay for a single AND gate on a 3nm process is measured in picoseconds.
// The ALU sees this:
// Bit 63: a[63] ──┬── AND ── result[63]
// b[63] ─┘
// Bit 62: a[62] ──┬── AND ── result[62]
// b[62] ─┘
// ... 62 more identical gates in parallel ...
// All 64 results ready in one gate delay.
Instruction Latency: Bitwise vs Arithmetic
Here are approximate latencies on a modern Intel Core (Golden Cove / Skymont) and Apple M-series CPU. These are not theoretical — they come from instruction timing tables (Agner Fog, uops.info). All values are in CPU clock cycles at typical frequency.
| Operation | Latency (cycles) | Throughput |
|---|---|---|
| AND / OR / XOR | 1 | 4 per cycle |
| NOT | 1 | 4 per cycle |
| Shift (<< / >>) | 1 | 2 per cycle |
| Addition (ADD) | 1 | 4 per cycle |
| Multiplication (IMUL) | 3–4 | 1 per cycle |
| Division (IDIV, 64-bit) | 20–80 | 1 per 20–80 cycles |
Notice addition is also 1 cycle — modern adders use carry-lookahead circuits that approximate the speed of pure combinational logic. But multiplication and division are fundamentally multi-cycle. Division is the slowest integer operation by a wide margin.
Compiler Optimizations: What Gets Replaced
Compilers know these latency numbers and rewrite your arithmetic into bitwise operations automatically when the operand is a power of two. GCC and Clang perform these transformations at -O1 and above; MSVC does them at /O2. You do not need to hand-optimize these — the compiler has been doing it for decades. But understanding the mapping helps you read optimized assembly and write code that makes the optimization obvious.
// What you write // What the compiler generates
x = y * 8; // lea rax, [rdi*8] or shl rdi, 3
x = y * 16; // shl rdi, 4
x = y / 8; (unsigned) // shr rdi, 3
x = y % 8; (unsigned) // and rdi, 7
x = y % 16; (unsigned) // and rdi, 15
// The pattern: x % (2^n) = x & (2^n - 1)
The LEA (Load Effective Address) instruction on x86 is a special case — it can compute base + index * scale where scale is 1, 2, 4, or 8, all in one cycle. This is why x * 8 often becomes an LEA rather than a shift: same latency, but LEA does not modify the flags register.
SIMD: Bitwise Parallelism on Steroids
SIMD (Single Instruction, Multiple Data) registers are 128, 256, or 512 bits wide. A single VPAND (AVX bitwise AND) instruction operates on all bits in parallel — 512 independent AND gates firing simultaneously in one cycle. This is not theoretical; it is how image processing pipelines apply masks, how cryptography processes blocks, and how video codecs manipulate pixel data. A 512-bit SIMD AND on an AVX-512 capable CPU processes 64 bytes in the same time as a scalar AND processes 8. The parallel gate array scales linearly with register width.
Branchless Programming with Bit Tricks
Branch misprediction costs 15-20 cycles on modern CPUs. Bitwise operations have no branches, so they can replace conditional logic in performance-critical paths. Here are three classic examples:
// Absolute value without branching
int abs_branchless(int x) {
int mask = x >> 31; // all 1s if negative, all 0s if positive
return (x + mask) ^ mask; // flips bits if negative
}
// Min without branching
int min_branchless(int a, int b) {
return b ^ ((a ^ b) & -(a < b));
}
// Max without branching
int max_branchless(int a, int b) {
return a ^ ((a ^ b) & -(a < b));
}
// Is power of two? (single expression, no loop)
bool is_power_of_two(unsigned int x) {
return x && !(x & (x - 1));
}
When Bitwise Optimization Matters — and When It Doesn't
Matters: inner loops executed millions of times (game engine physics, video decoding, cryptographic hashing), embedded systems with tight cycle budgets, DSP (digital signal processing) pipelines where SIMD bitwise is the difference between real-time and broken. Doesn't matter: anything IO-bound (database queries, network requests, file reads), UI event handlers that fire a few times per second, startup code that runs once. If your function spends 99% of its time waiting for a network response, saving 3 cycles on a multiplication is noise. The compiler will optimize the obvious cases for you. Only reach for manual bitwise optimization when a profiler tells you the arithmetic is the bottleneck — and use our bitwise calculator to prototype and verify the bit-level behavior before you commit the optimization.