DIV¶
The DIV cost is the latency difference between a probe chaining two dependent divisions per
element and one chaining a single division — probes f_div_div and f_div.
What Python code counts into DIV is described in
FLOP types.
Inner-loop diff¶
--- f_div
+++ f_div_div
.L0:
ldr %d0, [%x0], #8
fcmp %d0, #0.0
b.eq .L1
fdiv %d1, %d1, %d0
+ fdiv %d1, %d1, %d0
str %d1, [%x1], #8
subs %x2, %x2, #1
b.ne .L0
Loop structure¶
f_div-- 1 innermost loop(s): 8 instructionsf_div_div-- 1 innermost loop(s): 9 instructions
The listings below are the complete compiled functions the benchmark times, raw as numba emits them (the cpython call wrappers around them are omitted -- they never run inside the timed loop). Listing lengths reflect the compiler's unrolling choices, not the probes' amount of work -- see the discussion below.
Full ASM listing: f_div
cmp x2, #1
b.lt LBB0_7
cmp x3, #1
b.lt LBB0_7
ldr x8, [sp, #56]
ldr x9, [sp]
mov x10, #22377
movk x10, #35604, lsl #16
movk x10, #48906, lsl #32
movk x10, #16389, lsl #48
fmov d0, x10
LBB0_3:
sub x10, x2, #1
mov x11, x3
mov x12, x9
mov x13, x8
mov.16b v1, v0
LBB0_4:
ldr d2, [x12], #8
fcmp d2, #0.0
b.eq LBB0_8
fdiv d1, d1, d2
str d1, [x13], #8
subs x11, x11, #1
b.ne LBB0_4
cmp x2, #1
mov x2, x10
b.gt LBB0_3
LBB0_7:
str xzr, [x0]
mov w0, #0
ret
LBB0_8:
Lloh0:
adrp x8, _.const.picklebuf.<addr>@GOTPAGE
Lloh1:
ldr x8, [x8, _.const.picklebuf.<addr>@GOTPAGEOFF]
str x8, [x1]
mov w0, #1
ret
.loh AdrpLdrGot Lloh0, Lloh1
Full ASM listing: f_div_div
cmp x2, #1
b.lt LBB0_7
cmp x3, #1
b.lt LBB0_7
ldr x8, [sp, #56]
ldr x9, [sp]
mov x10, #22377
movk x10, #35604, lsl #16
movk x10, #48906, lsl #32
movk x10, #16389, lsl #48
fmov d0, x10
LBB0_3:
sub x10, x2, #1
mov x11, x3
mov x12, x9
mov x13, x8
mov.16b v1, v0
LBB0_4:
ldr d2, [x12], #8
fcmp d2, #0.0
b.eq LBB0_8
fdiv d1, d1, d2
fdiv d1, d1, d2
str d1, [x13], #8
subs x11, x11, #1
b.ne LBB0_4
cmp x2, #1
mov x2, x10
b.gt LBB0_3
LBB0_7:
str xzr, [x0]
mov w0, #0
ret
LBB0_8:
Lloh0:
adrp x8, _.const.picklebuf.<addr>@GOTPAGE
Lloh1:
ldr x8, [x8, _.const.picklebuf.<addr>@GOTPAGEOFF]
str x8, [x1]
mov w0, #1
ret
.loh AdrpLdrGot Lloh0, Lloh1
Discussion¶
The subtraction isolates exactly one fdiv.
- Intended instruction, and nothing else: the diff is the single line
+ fdiv %d1, %d1, %d0. Thefcmp/b.eqpair visible in both loops is numba's divide-by-zero guard (Python semantics raise onx / 0.0); it is identical on both sides and cancels in the subtraction. - In the dependency chain: both divisions read and write the accumulator, so each element
pays two dependent
fdivlatencies instead of one. - Loop-structure symmetry: symmetric. Both probes compile to a single scalar loop — the guard branch keeps LLVM from unrolling either side.