nrposner

scipy scratch

g = a * b / c **d`
a = [...]
b = [...]
c = [...]
d = [...]

[((a*b)/c)**d for a, b, c, d in zip(a, b, c, d)]
L2:     FOR_ITER                16 (to L3)
        UNPACK_SEQUENCE          4
        STORE_FAST_STORE_FAST    1 (a, b)
        STORE_FAST_STORE_FAST   35 (c, d)
        LOAD_FAST_LOAD_FAST      1 (a, b)
        BINARY_OP                5 (*)
        LOAD_FAST                2 (c)
        BINARY_OP               11 (/)
        LOAD_FAST                3 (d)
        BINARY_OP                8 (**)
        LIST_APPEND              2
        JUMP_BACKWARD           18 (to L2)
import numpy as np

a = np.array([...])
b = np.array([...])
c = np.array([...])
d = np.array([...])

g = ((a*b)/c)**d
  9         178 LOAD_FAST                0 (a)
            180 LOAD_FAST                1 (b)
            182 BINARY_OP                5 (*)
            186 LOAD_FAST                2 (c)
            188 BINARY_OP               11 (/)
            192 LOAD_FAST                3 (d)
            194 BINARY_OP                8 (**)
            198 STORE_FAST               4 (g)
out_arr = np.zeros(n)

for i, (a, b, c, d), in enumerate(zip(a_arr, b_arr, c_arr, d_arr)):
    out_arr[i] = ((a*b)/c)**d
fn abcd(
    /// Four read-only arrays of 64-bit floating point numbers
    a_arr: &[f64],
    b_arr: &[f64],
    c_arr: &[f64],
    d_arr: &[f64],
// we return a Vec, an owned array
) -> Vec<f64> {

    // allocate just the output array
    let mut out = vec![0.0; a_arr.len()];

    // take each element from the zipped arrays...
    for (i, (((a, b), c), d)) in a_arr.iter()
        .zip(b_arr)
        .zip(c_arr)
        .zip(d_arr)
        .enumerate() {

        // do the math, and put the output directly in place
        out[i] = ((a*b)/c).powf(*d);
    }
    out
}
"SIMD Extensions": {
    "baseline": [
      "NEON",
      "NEON_FP16",
      "NEON_VFPV4",
      "ASIMD"
    ],
    "found": [
      "ASIMDHP",
      "ASIMDDP"
    ],
    "not found": [
      "ASIMDFHM"
    ]
  }
movupd  xmm1, xmmword ptr [r13 + r15]   ; load 2 doubles from A
movupd  xmm0, xmmword ptr [rbx + r15]   ; load 2 doubles from B
mulpd   xmm0, xmm1                      ; xmm0 = a * b (packed)
movupd  xmm1, xmmword ptr [rcx + r15]   ; load 2 doubles from C
divpd   xmm0, xmm1                      ; xmm0 /= c (packed)
mov     rbp, qword ptr [rip + pow@GOTPCREL]
movapd  xmmword ptr [rsp + 80], xmm0    ; xmm0 to local stack (16b)
movups  xmm1, xmmword ptr [rdx + r15]   ; load 2 doubles from D
movaps  xmmword ptr [rsp + 96], xmm1    ; ... then back on stack,
                                        ; no mem-to-mem moves!
mov     r12, rbx                        ; more juggling to save into 
mov     rbx, rdx                        ; callee-managed registers
call    rbp                             ; xmm0[0] ** xmm1[0]
movapd  xmmword ptr [rsp + 32], xmm0    ; store result[0] on stack
movapd  xmm0, xmmword ptr [rsp + 80]    ; move temp back
unpckhpd        xmm0, xmm0              ; shift high bits to low
movaps  xmm1, xmmword ptr [rsp + 96]    ; do the same for xmm0
movhlps xmm1, xmm1
call    rbp                             ; xmm0[1] ** xmm1[1]
//         out,      exponents,      bases,          count
void vvpow(double *, const double *, const double *, const int *);