Skip to content

Parallel ranges

for i in parallel.range(n) says the iterations may run at once. Under CPython they run in order, which is one of the orders allowed. Natively the body becomes a function over a chunk of the range, and the build's parallel backend decides how the chunks run.

Run it

python  ranges.ppy
ppy run ranges.ppy
ppy build ranges.ppy --report-opt
ppy emit c ranges.ppy

What it prints

python ranges.ppy, ppy run ranges.ppy

9999800001
99.987909853872
99.987910
99999
500000.0

ppy build ranges.ppy --report-opt

166 lines
optimization report: PPy (O2, ir road)
module ranges
  ranges.count_odd: native, bound to Python
  ranges.dot: native, bound to Python
  ranges.dot_relaxed: native, bound to Python
  ranges.fill: native, native callers only
  ranges.squares: native, native callers only
  ranges.main: Python -- has effects that must run on CPython: IO
  block merged: 18
  dead code removed: 114
  note: 10
  parallel loop emitted: 12
    - `ranges.squares`: the loop over `i` is a parallel loop
    - `ranges.dot`: the loop over `i` is a parallel add reduction into `total` (kept in order: `@ppy.fastmath` would let it split)
    - `ranges.dot_relaxed`: the loop over `i` is a parallel add reduction into `total`
    - `ranges.count_odd`: the loop over `i` is a parallel add reduction into `hits`
    - `ranges.fill`: the loop over `i` is a parallel loop
    - `ranges.fill`: the loop over `j` is a parallel add reduction into `acc` (kept in order: `@ppy.fastmath` would let it split)
    - @ranges_count_odd__par4: core.cmp: folded 2 and 0
    - @ranges_count_odd__par4: core.guard: condition always holds
    - simplify-cfg: ^for.guards1 merged into ^entry
    - simplify-cfg: ^for.setup2 merged into ^entry
    - simplify-cfg: ^for.latch5 merged into ^for.body4
    - simplify-cfg: ^for.guards1 merged into ^entry
    - simplify-cfg: ^for.setup2 merged into ^entry
    - simplify-cfg: ^for.latch5 merged into ^for.body4
    - simplify-cfg: ^for.guards1 merged into ^entry
    - simplify-cfg: ^for.setup2 merged into ^entry
    - simplify-cfg: ^for.latch5 merged into ^for.body4
    - simplify-cfg: ^for.guards1 merged into ^entry
    - simplify-cfg: ^for.setup2 merged into ^entry
    - simplify-cfg: ^for.latch5 merged into ^endif9
    - simplify-cfg: ^for.guards1 merged into ^entry
    - simplify-cfg: ^for.setup2 merged into ^entry
    - simplify-cfg: ^for.latch5 merged into ^for.body4
    - simplify-cfg: ^for.guards1 merged into ^entry
    - simplify-cfg: ^for.setup2 merged into ^entry
    - simplify-cfg: ^for.latch5 merged into ^for.body4
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.select removed
    - dce: unused core.cmp removed
    - dce: unused core.select removed
    - dce: unused core.cmp removed
    - dce: unused core.select removed
    - dce: unused core.cmp removed
    - dce: unused core.select removed
    - dce: unused core.cmp removed
    - dce: unused core.select removed
    - dce: unused core.cmp removed
    - dce: unused core.select removed
    - dce: unused core.cmp removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.sub removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.sub removed
    - dce: unused core.const removed
    - parallel loop over @ranges_squares__par1 lowered to 16 chunks
    - parallel reduction over @ranges_dot__par2 lowered to one chunk
    - parallel reduction over @ranges_dot_relaxed__par3 lowered to 16 chunks
    - parallel reduction over @ranges_count_odd__par4 lowered to 16 chunks
    - parallel loop over @ranges_fill__par5 lowered to 16 chunks
    - parallel reduction over @ranges_fill__par6 lowered to one chunk
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.mul: identity element removed
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.mul: multiplied by zero
    - @ranges_squares: core.add: identity element removed
    - @ranges_squares: core.sub: folded 16 and 1
    - @ranges_squares: core.sub: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.mul: identity element removed
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.mul: multiplied by zero
    - @ranges_dot_relaxed: core.add: identity element removed
    - @ranges_dot_relaxed: core.sub: folded 16 and 1
    - @ranges_dot_relaxed: core.sub: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.mul: identity element removed
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.mul: multiplied by zero
    - @ranges_count_odd: core.add: identity element removed
    - @ranges_count_odd: core.sub: folded 16 and 1
    - @ranges_count_odd: core.sub: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.mul: identity element removed
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.mul: multiplied by zero
    - @ranges_fill: core.add: identity element removed
    - @ranges_fill: core.sub: folded 16 and 1
    - @ranges_fill: core.sub: identity element removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed
    - dce: unused core.const removed

ppy emit c ranges.ppy

1267 lines
/* ranges: generated by ppy, C11 */
/* link with: -lpthread */
#include <math.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

#if defined(__x86_64__) || defined(__i386__)
#define PPY_PAUSE() __builtin_ia32_pause()
#elif defined(__aarch64__)
#define PPY_PAUSE() __asm__ __volatile__("yield")
#else
#define PPY_PAUSE() ((void)0)
#endif

#if !defined(__GNUC__) && !defined(__clang__)
#error "the atomic operations in this unit need the __atomic builtins of GCC or Clang"
#endif

typedef struct ppy_ctx_ranges_squares__par1 {
    int64_t *a0;
    int64_t a1;
    int64_t a2;
} ppy_ctx_ranges_squares__par1;

typedef struct ppy_ctx_ranges_dot_relaxed__par3__task {
    double *a0;
    int64_t a1;
    double *a2;
    int64_t a3;
    int64_t a4;
    int64_t a5;
    double a6;
    double *a7;
} ppy_ctx_ranges_dot_relaxed__par3__task;

typedef struct ppy_ctx_ranges_count_odd__par4__task {
    int64_t a0;
    int64_t *a1;
    int64_t a2;
    int64_t a3;
    int64_t a4;
    int64_t a5;
    int64_t *a6;
} ppy_ctx_ranges_count_odd__par4__task;

typedef struct ppy_ctx_ranges_fill__par5 {
    double a0;
    double *a1;
    int64_t a2;
    int64_t a3;
} ppy_ctx_ranges_fill__par5;

static inline int ppy_ovf_sub_i64(int64_t a, int64_t b, int64_t *out) {
#if defined(__GNUC__) || defined(__clang__)
    return __builtin_sub_overflow(a, b, out);
#else
    if ((b < 0 && a > INT64_MAX + b) || (b > 0 && a < INT64_MIN + b)) {
        return 1;
    }
    *out = a - b;
    return 0;
#endif
}

static inline int ppy_ovf_mul_i64(int64_t a, int64_t b, int64_t *out) {
#if defined(__GNUC__) || defined(__clang__)
    return __builtin_mul_overflow(a, b, out);
#else
    if (a > 0) {
        if (b > 0) { if (a > INT64_MAX / b) return 1; }
        else if (b < INT64_MIN / a) return 1;
    } else if (b > 0) {
        if (a < INT64_MIN / b) return 1;
    } else if (a != 0 && b < INT64_MAX / a) return 1;
    *out = a * b;
    return 0;
#endif
}

static inline int ppy_ovf_add_i64(int64_t a, int64_t b, int64_t *out) {
#if defined(__GNUC__) || defined(__clang__)
    return __builtin_add_overflow(a, b, out);
#else
    if ((b > 0 && a > INT64_MAX - b) || (b < 0 && a < INT64_MIN - b)) {
        return 1;
    }
    *out = a + b;
    return 0;
#endif
}

int32_t ppy_ranges_squares(int64_t *out, int64_t n, int64_t *out_2);
int32_t ppy_ranges_dot(double *a, int64_t a_len, double *b, int64_t b_len, double *out);
int32_t ppy_ranges_dot_relaxed(double *a, int64_t a_len, double *b, int64_t b_len, double *out);
int32_t ppy_ranges_count_odd(int64_t *xs, int64_t xs_len, int64_t scale, int64_t *out);
int32_t ppy_ranges_fill(double *out, int64_t n, double base, double *out_2);
int32_t ranges_squares__par1(int64_t *out, int64_t __ppy_begin_, int64_t __ppy_end_, int64_t *out_2);
int32_t ranges_dot__par2(double *a, int64_t a_len, double *b, int64_t b_len, int64_t __ppy_begin_, int64_t __ppy_end_, double total, double *out);
int32_t ranges_dot_relaxed__par3(double *a, int64_t a_len, double *b, int64_t b_len, int64_t __ppy_begin_, int64_t __ppy_end_, double total, double *out);
int32_t ranges_count_odd__par4(int64_t scale, int64_t *xs, int64_t xs_len, int64_t __ppy_begin_, int64_t __ppy_end_, int64_t hits, int64_t *out);
int32_t ranges_fill__par5(double base, double *out, int64_t __ppy_begin_, int64_t __ppy_end_, int64_t *out_2);
int32_t ranges_fill__par6(double *out, int64_t __ppy_begin_, int64_t __ppy_end_, double acc, double *out_2);
int32_t ranges_dot_relaxed__par3__task(double *a, int64_t a_len, double *b, int64_t b_len, int64_t begin, int64_t end, double start, double *out, int64_t *out_2);
int32_t ranges_count_odd__par4__task(int64_t scale, int64_t *xs, int64_t xs_len, int64_t begin, int64_t end, int64_t start, int64_t *out, int64_t *out_2);

static void *ppy_thread_ranges_squares__par1(void *raw) {
    ppy_ctx_ranges_squares__par1 *c = (ppy_ctx_ranges_squares__par1 *)raw;
    int64_t out = 0;
    int32_t status = ranges_squares__par1(c->a0, c->a1, c->a2, &out);
    free(raw);
    return (void *)(intptr_t)status;
}

static void *ppy_thread_ranges_dot_relaxed__par3__task(void *raw) {
    ppy_ctx_ranges_dot_relaxed__par3__task *c = (ppy_ctx_ranges_dot_relaxed__par3__task *)raw;
    int64_t out = 0;
    int32_t status = ranges_dot_relaxed__par3__task(c->a0, c->a1, c->a2, c->a3, c->a4, c->a5, c->a6, c->a7, &out);
    free(raw);
    return (void *)(intptr_t)status;
}

static void *ppy_thread_ranges_count_odd__par4__task(void *raw) {
    ppy_ctx_ranges_count_odd__par4__task *c = (ppy_ctx_ranges_count_odd__par4__task *)raw;
    int64_t out = 0;
    int32_t status = ranges_count_odd__par4__task(c->a0, c->a1, c->a2, c->a3, c->a4, c->a5, c->a6, &out);
    free(raw);
    return (void *)(intptr_t)status;
}

static void *ppy_thread_ranges_fill__par5(void *raw) {
    ppy_ctx_ranges_fill__par5 *c = (ppy_ctx_ranges_fill__par5 *)raw;
    int64_t out = 0;
    int32_t status = ranges_fill__par5(c->a0, c->a1, c->a2, c->a3, &out);
    free(raw);
    return (void *)(intptr_t)status;
}

int32_t ppy_ranges_squares(int64_t *out, int64_t n, int64_t *out_2) {
    if (n < 4096) {
        int64_t res;
        if (ranges_squares__par1(out, 0, n, &res) != 0) return 1; /* call.ok */
    } else {
        int64_t t1 = (int64_t)((uint64_t)n + 15u) / 16;
        ppy_ctx_ranges_squares__par1 *ctx = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx == NULL) return 1; /* spawn.alloc */
        ctx->a0 = out;
        ctx->a1 = 0;
        ctx->a2 = t1 < n ? t1 : n;
        pthread_t thread;
        if (pthread_create(&thread, NULL, ppy_thread_ranges_squares__par1, ctx) != 0) return 1; /* spawn.ok */
        int64_t t2 = (int64_t)thread;
        int64_t t3 = (int64_t)((uint64_t)t1 + (uint64_t)t1);
        ppy_ctx_ranges_squares__par1 *ctx_2 = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx_2 == NULL) return 1; /* spawn.alloc */
        ctx_2->a0 = out;
        ctx_2->a1 = t1;
        ctx_2->a2 = t3 < n ? t3 : n;
        pthread_t thread_2;
        if (pthread_create(&thread_2, NULL, ppy_thread_ranges_squares__par1, ctx_2) != 0) return 1; /* spawn.ok */
        int64_t t4 = (int64_t)thread_2;
        int64_t t5 = (int64_t)(2u * (uint64_t)t1);
        int64_t t6 = (int64_t)((uint64_t)t5 + (uint64_t)t1);
        ppy_ctx_ranges_squares__par1 *ctx_3 = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx_3 == NULL) return 1; /* spawn.alloc */
        ctx_3->a0 = out;
        ctx_3->a1 = t5;
        ctx_3->a2 = t6 < n ? t6 : n;
        pthread_t thread_3;
        if (pthread_create(&thread_3, NULL, ppy_thread_ranges_squares__par1, ctx_3) != 0) return 1; /* spawn.ok */
        int64_t t7 = (int64_t)thread_3;
        int64_t t8 = (int64_t)(3u * (uint64_t)t1);
        int64_t t9 = (int64_t)((uint64_t)t8 + (uint64_t)t1);
        ppy_ctx_ranges_squares__par1 *ctx_4 = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx_4 == NULL) return 1; /* spawn.alloc */
        ctx_4->a0 = out;
        ctx_4->a1 = t8;
        ctx_4->a2 = t9 < n ? t9 : n;
        pthread_t thread_4;
        if (pthread_create(&thread_4, NULL, ppy_thread_ranges_squares__par1, ctx_4) != 0) return 1; /* spawn.ok */
        int64_t t10 = (int64_t)thread_4;
        int64_t t11 = (int64_t)(4u * (uint64_t)t1);
        int64_t t12 = (int64_t)((uint64_t)t11 + (uint64_t)t1);
        ppy_ctx_ranges_squares__par1 *ctx_5 = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx_5 == NULL) return 1; /* spawn.alloc */
        ctx_5->a0 = out;
        ctx_5->a1 = t11;
        ctx_5->a2 = t12 < n ? t12 : n;
        pthread_t thread_5;
        if (pthread_create(&thread_5, NULL, ppy_thread_ranges_squares__par1, ctx_5) != 0) return 1; /* spawn.ok */
        int64_t t13 = (int64_t)thread_5;
        int64_t t14 = (int64_t)(5u * (uint64_t)t1);
        int64_t t15 = (int64_t)((uint64_t)t14 + (uint64_t)t1);
        ppy_ctx_ranges_squares__par1 *ctx_6 = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx_6 == NULL) return 1; /* spawn.alloc */
        ctx_6->a0 = out;
        ctx_6->a1 = t14;
        ctx_6->a2 = t15 < n ? t15 : n;
        pthread_t thread_6;
        if (pthread_create(&thread_6, NULL, ppy_thread_ranges_squares__par1, ctx_6) != 0) return 1; /* spawn.ok */
        int64_t t16 = (int64_t)thread_6;
        int64_t t17 = (int64_t)(6u * (uint64_t)t1);
        int64_t t18 = (int64_t)((uint64_t)t17 + (uint64_t)t1);
        ppy_ctx_ranges_squares__par1 *ctx_7 = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx_7 == NULL) return 1; /* spawn.alloc */
        ctx_7->a0 = out;
        ctx_7->a1 = t17;
        ctx_7->a2 = t18 < n ? t18 : n;
        pthread_t thread_7;
        if (pthread_create(&thread_7, NULL, ppy_thread_ranges_squares__par1, ctx_7) != 0) return 1; /* spawn.ok */
        int64_t t19 = (int64_t)thread_7;
        int64_t t20 = (int64_t)(7u * (uint64_t)t1);
        int64_t t21 = (int64_t)((uint64_t)t20 + (uint64_t)t1);
        ppy_ctx_ranges_squares__par1 *ctx_8 = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx_8 == NULL) return 1; /* spawn.alloc */
        ctx_8->a0 = out;
        ctx_8->a1 = t20;
        ctx_8->a2 = t21 < n ? t21 : n;
        pthread_t thread_8;
        if (pthread_create(&thread_8, NULL, ppy_thread_ranges_squares__par1, ctx_8) != 0) return 1; /* spawn.ok */
        int64_t t22 = (int64_t)thread_8;
        int64_t t23 = (int64_t)(8u * (uint64_t)t1);
        int64_t t24 = (int64_t)((uint64_t)t23 + (uint64_t)t1);
        ppy_ctx_ranges_squares__par1 *ctx_9 = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx_9 == NULL) return 1; /* spawn.alloc */
        ctx_9->a0 = out;
        ctx_9->a1 = t23;
        ctx_9->a2 = t24 < n ? t24 : n;
        pthread_t thread_9;
        if (pthread_create(&thread_9, NULL, ppy_thread_ranges_squares__par1, ctx_9) != 0) return 1; /* spawn.ok */
        int64_t t25 = (int64_t)thread_9;
        int64_t t26 = (int64_t)(9u * (uint64_t)t1);
        int64_t t27 = (int64_t)((uint64_t)t26 + (uint64_t)t1);
        ppy_ctx_ranges_squares__par1 *ctx_10 = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx_10 == NULL) return 1; /* spawn.alloc */
        ctx_10->a0 = out;
        ctx_10->a1 = t26;
        ctx_10->a2 = t27 < n ? t27 : n;
        pthread_t thread_10;
        if (pthread_create(&thread_10, NULL, ppy_thread_ranges_squares__par1, ctx_10) != 0) return 1; /* spawn.ok */
        int64_t t28 = (int64_t)thread_10;
        int64_t t29 = (int64_t)(10u * (uint64_t)t1);
        int64_t t30 = (int64_t)((uint64_t)t29 + (uint64_t)t1);
        ppy_ctx_ranges_squares__par1 *ctx_11 = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx_11 == NULL) return 1; /* spawn.alloc */
        ctx_11->a0 = out;
        ctx_11->a1 = t29;
        ctx_11->a2 = t30 < n ? t30 : n;
        pthread_t thread_11;
        if (pthread_create(&thread_11, NULL, ppy_thread_ranges_squares__par1, ctx_11) != 0) return 1; /* spawn.ok */
        int64_t t31 = (int64_t)thread_11;
        int64_t t32 = (int64_t)(11u * (uint64_t)t1);
        int64_t t33 = (int64_t)((uint64_t)t32 + (uint64_t)t1);
        ppy_ctx_ranges_squares__par1 *ctx_12 = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx_12 == NULL) return 1; /* spawn.alloc */
        ctx_12->a0 = out;
        ctx_12->a1 = t32;
        ctx_12->a2 = t33 < n ? t33 : n;
        pthread_t thread_12;
        if (pthread_create(&thread_12, NULL, ppy_thread_ranges_squares__par1, ctx_12) != 0) return 1; /* spawn.ok */
        int64_t t34 = (int64_t)thread_12;
        int64_t t35 = (int64_t)(12u * (uint64_t)t1);
        int64_t t36 = (int64_t)((uint64_t)t35 + (uint64_t)t1);
        ppy_ctx_ranges_squares__par1 *ctx_13 = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx_13 == NULL) return 1; /* spawn.alloc */
        ctx_13->a0 = out;
        ctx_13->a1 = t35;
        ctx_13->a2 = t36 < n ? t36 : n;
        pthread_t thread_13;
        if (pthread_create(&thread_13, NULL, ppy_thread_ranges_squares__par1, ctx_13) != 0) return 1; /* spawn.ok */
        int64_t t37 = (int64_t)thread_13;
        int64_t t38 = (int64_t)(13u * (uint64_t)t1);
        int64_t t39 = (int64_t)((uint64_t)t38 + (uint64_t)t1);
        ppy_ctx_ranges_squares__par1 *ctx_14 = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx_14 == NULL) return 1; /* spawn.alloc */
        ctx_14->a0 = out;
        ctx_14->a1 = t38;
        ctx_14->a2 = t39 < n ? t39 : n;
        pthread_t thread_14;
        if (pthread_create(&thread_14, NULL, ppy_thread_ranges_squares__par1, ctx_14) != 0) return 1; /* spawn.ok */
        int64_t t40 = (int64_t)thread_14;
        int64_t t41 = (int64_t)(14u * (uint64_t)t1);
        int64_t t42 = (int64_t)((uint64_t)t41 + (uint64_t)t1);
        ppy_ctx_ranges_squares__par1 *ctx_15 = (ppy_ctx_ranges_squares__par1 *)malloc(sizeof(ppy_ctx_ranges_squares__par1));
        if (ctx_15 == NULL) return 1; /* spawn.alloc */
        ctx_15->a0 = out;
        ctx_15->a1 = t41;
        ctx_15->a2 = t42 < n ? t42 : n;
        pthread_t thread_15;
        if (pthread_create(&thread_15, NULL, ppy_thread_ranges_squares__par1, ctx_15) != 0) return 1; /* spawn.ok */
        int64_t t43 = (int64_t)thread_15;
        int64_t t44 = (int64_t)(15u * (uint64_t)t1);
        int64_t res_2;
        int64_t t45 = (int64_t)ranges_squares__par1(out, t44 < n ? t44 : n, n, &res_2);
        void *joined;
        if (pthread_join((pthread_t)t2, &joined) != 0) return 1; /* join.ok */
        int64_t t46 = (int64_t)(intptr_t)joined;
        void *joined_2;
        if (pthread_join((pthread_t)t4, &joined_2) != 0) return 1; /* join.ok */
        int64_t t47 = (int64_t)(intptr_t)joined_2;
        void *joined_3;
        if (pthread_join((pthread_t)t7, &joined_3) != 0) return 1; /* join.ok */
        int64_t t48 = (int64_t)(intptr_t)joined_3;
        void *joined_4;
        if (pthread_join((pthread_t)t10, &joined_4) != 0) return 1; /* join.ok */
        int64_t t49 = (int64_t)(intptr_t)joined_4;
        void *joined_5;
        if (pthread_join((pthread_t)t13, &joined_5) != 0) return 1; /* join.ok */
        int64_t t50 = (int64_t)(intptr_t)joined_5;
        void *joined_6;
        if (pthread_join((pthread_t)t16, &joined_6) != 0) return 1; /* join.ok */
        int64_t t51 = (int64_t)(intptr_t)joined_6;
        void *joined_7;
        if (pthread_join((pthread_t)t19, &joined_7) != 0) return 1; /* join.ok */
        int64_t t52 = (int64_t)(intptr_t)joined_7;
        void *joined_8;
        if (pthread_join((pthread_t)t22, &joined_8) != 0) return 1; /* join.ok */
        int64_t t53 = (int64_t)(intptr_t)joined_8;
        void *joined_9;
        if (pthread_join((pthread_t)t25, &joined_9) != 0) return 1; /* join.ok */
        int64_t t54 = (int64_t)(intptr_t)joined_9;
        void *joined_10;
        if (pthread_join((pthread_t)t28, &joined_10) != 0) return 1; /* join.ok */
        int64_t t55 = (int64_t)(intptr_t)joined_10;
        void *joined_11;
        if (pthread_join((pthread_t)t31, &joined_11) != 0) return 1; /* join.ok */
        int64_t t56 = (int64_t)(intptr_t)joined_11;
        void *joined_12;
        if (pthread_join((pthread_t)t34, &joined_12) != 0) return 1; /* join.ok */
        int64_t t57 = (int64_t)(intptr_t)joined_12;
        void *joined_13;
        if (pthread_join((pthread_t)t37, &joined_13) != 0) return 1; /* join.ok */
        int64_t t58 = (int64_t)(intptr_t)joined_13;
        void *joined_14;
        if (pthread_join((pthread_t)t40, &joined_14) != 0) return 1; /* join.ok */
        int64_t t59 = (int64_t)(intptr_t)joined_14;
        void *joined_15;
        if (pthread_join((pthread_t)t43, &joined_15) != 0) return 1; /* join.ok */
        int64_t t60 = (int64_t)(intptr_t)joined_15;
        if (!(((((((((((((((((t45 != 0) | (t46 != 0)) | (t47 != 0)) | (t48 != 0)) | (t49 != 0)) | (t50 != 0)) | (t51 != 0)) | (t52 != 0)) | (t53 != 0)) | (t54 != 0)) | (t55 != 0)) | (t56 != 0)) | (t57 != 0)) | (t58 != 0)) | (t59 != 0)) | (t60 != 0)) ^ true)) return 1; /* contract.ok */
    }
    int64_t t61;
    if (ppy_ovf_sub_i64(n, 1, &t61)) return 1; /* arith.ok */
    *out_2 = out[t61];
    return 0;
}

int32_t ppy_ranges_dot(double *a, int64_t a_len, double *b, int64_t b_len, double *out) {
    double total = 0.0;
    if (ranges_dot__par2(a, a_len, b, b_len, 0, a_len, total, &total) != 0) return 1; /* call.ok */
    *out = total;
    return 0;
}

int32_t ppy_ranges_dot_relaxed(double *a, int64_t a_len, double *b, int64_t b_len, double *out) {
    double reduced;
    double partial[1];
    double partial_2[1];
    double partial_3[1];
    double partial_4[1];
    double partial_5[1];
    double partial_6[1];
    double partial_7[1];
    double partial_8[1];
    double partial_9[1];
    double partial_10[1];
    double partial_11[1];
    double partial_12[1];
    double partial_13[1];
    double partial_14[1];
    double partial_15[1];

    double total = 0.0;
    double t1 = total;
    if (a_len < 4096) {
        double t2;
        if (ranges_dot_relaxed__par3(a, a_len, b, b_len, 0, a_len, t1, &t2) != 0) return 1; /* call.ok */
        reduced = t2;
    } else {
        int64_t t3 = (int64_t)((uint64_t)a_len + 15u) / 16;
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx == NULL) return 1; /* spawn.alloc */
        ctx->a0 = a;
        ctx->a1 = a_len;
        ctx->a2 = b;
        ctx->a3 = b_len;
        ctx->a4 = 0;
        ctx->a5 = t3 < a_len ? t3 : a_len;
        ctx->a6 = 0.0;
        ctx->a7 = partial;
        pthread_t thread;
        if (pthread_create(&thread, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx) != 0) return 1; /* spawn.ok */
        int64_t t4 = (int64_t)thread;
        int64_t t5 = (int64_t)((uint64_t)t3 + (uint64_t)t3);
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx_2 = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx_2 == NULL) return 1; /* spawn.alloc */
        ctx_2->a0 = a;
        ctx_2->a1 = a_len;
        ctx_2->a2 = b;
        ctx_2->a3 = b_len;
        ctx_2->a4 = t3;
        ctx_2->a5 = t5 < a_len ? t5 : a_len;
        ctx_2->a6 = 0.0;
        ctx_2->a7 = partial_2;
        pthread_t thread_2;
        if (pthread_create(&thread_2, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx_2) != 0) return 1; /* spawn.ok */
        int64_t t6 = (int64_t)thread_2;
        int64_t t7 = (int64_t)(2u * (uint64_t)t3);
        int64_t t8 = (int64_t)((uint64_t)t7 + (uint64_t)t3);
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx_3 = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx_3 == NULL) return 1; /* spawn.alloc */
        ctx_3->a0 = a;
        ctx_3->a1 = a_len;
        ctx_3->a2 = b;
        ctx_3->a3 = b_len;
        ctx_3->a4 = t7;
        ctx_3->a5 = t8 < a_len ? t8 : a_len;
        ctx_3->a6 = 0.0;
        ctx_3->a7 = partial_3;
        pthread_t thread_3;
        if (pthread_create(&thread_3, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx_3) != 0) return 1; /* spawn.ok */
        int64_t t9 = (int64_t)thread_3;
        int64_t t10 = (int64_t)(3u * (uint64_t)t3);
        int64_t t11 = (int64_t)((uint64_t)t10 + (uint64_t)t3);
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx_4 = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx_4 == NULL) return 1; /* spawn.alloc */
        ctx_4->a0 = a;
        ctx_4->a1 = a_len;
        ctx_4->a2 = b;
        ctx_4->a3 = b_len;
        ctx_4->a4 = t10;
        ctx_4->a5 = t11 < a_len ? t11 : a_len;
        ctx_4->a6 = 0.0;
        ctx_4->a7 = partial_4;
        pthread_t thread_4;
        if (pthread_create(&thread_4, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx_4) != 0) return 1; /* spawn.ok */
        int64_t t12 = (int64_t)thread_4;
        int64_t t13 = (int64_t)(4u * (uint64_t)t3);
        int64_t t14 = (int64_t)((uint64_t)t13 + (uint64_t)t3);
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx_5 = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx_5 == NULL) return 1; /* spawn.alloc */
        ctx_5->a0 = a;
        ctx_5->a1 = a_len;
        ctx_5->a2 = b;
        ctx_5->a3 = b_len;
        ctx_5->a4 = t13;
        ctx_5->a5 = t14 < a_len ? t14 : a_len;
        ctx_5->a6 = 0.0;
        ctx_5->a7 = partial_5;
        pthread_t thread_5;
        if (pthread_create(&thread_5, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx_5) != 0) return 1; /* spawn.ok */
        int64_t t15 = (int64_t)thread_5;
        int64_t t16 = (int64_t)(5u * (uint64_t)t3);
        int64_t t17 = (int64_t)((uint64_t)t16 + (uint64_t)t3);
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx_6 = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx_6 == NULL) return 1; /* spawn.alloc */
        ctx_6->a0 = a;
        ctx_6->a1 = a_len;
        ctx_6->a2 = b;
        ctx_6->a3 = b_len;
        ctx_6->a4 = t16;
        ctx_6->a5 = t17 < a_len ? t17 : a_len;
        ctx_6->a6 = 0.0;
        ctx_6->a7 = partial_6;
        pthread_t thread_6;
        if (pthread_create(&thread_6, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx_6) != 0) return 1; /* spawn.ok */
        int64_t t18 = (int64_t)thread_6;
        int64_t t19 = (int64_t)(6u * (uint64_t)t3);
        int64_t t20 = (int64_t)((uint64_t)t19 + (uint64_t)t3);
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx_7 = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx_7 == NULL) return 1; /* spawn.alloc */
        ctx_7->a0 = a;
        ctx_7->a1 = a_len;
        ctx_7->a2 = b;
        ctx_7->a3 = b_len;
        ctx_7->a4 = t19;
        ctx_7->a5 = t20 < a_len ? t20 : a_len;
        ctx_7->a6 = 0.0;
        ctx_7->a7 = partial_7;
        pthread_t thread_7;
        if (pthread_create(&thread_7, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx_7) != 0) return 1; /* spawn.ok */
        int64_t t21 = (int64_t)thread_7;
        int64_t t22 = (int64_t)(7u * (uint64_t)t3);
        int64_t t23 = (int64_t)((uint64_t)t22 + (uint64_t)t3);
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx_8 = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx_8 == NULL) return 1; /* spawn.alloc */
        ctx_8->a0 = a;
        ctx_8->a1 = a_len;
        ctx_8->a2 = b;
        ctx_8->a3 = b_len;
        ctx_8->a4 = t22;
        ctx_8->a5 = t23 < a_len ? t23 : a_len;
        ctx_8->a6 = 0.0;
        ctx_8->a7 = partial_8;
        pthread_t thread_8;
        if (pthread_create(&thread_8, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx_8) != 0) return 1; /* spawn.ok */
        int64_t t24 = (int64_t)thread_8;
        int64_t t25 = (int64_t)(8u * (uint64_t)t3);
        int64_t t26 = (int64_t)((uint64_t)t25 + (uint64_t)t3);
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx_9 = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx_9 == NULL) return 1; /* spawn.alloc */
        ctx_9->a0 = a;
        ctx_9->a1 = a_len;
        ctx_9->a2 = b;
        ctx_9->a3 = b_len;
        ctx_9->a4 = t25;
        ctx_9->a5 = t26 < a_len ? t26 : a_len;
        ctx_9->a6 = 0.0;
        ctx_9->a7 = partial_9;
        pthread_t thread_9;
        if (pthread_create(&thread_9, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx_9) != 0) return 1; /* spawn.ok */
        int64_t t27 = (int64_t)thread_9;
        int64_t t28 = (int64_t)(9u * (uint64_t)t3);
        int64_t t29 = (int64_t)((uint64_t)t28 + (uint64_t)t3);
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx_10 = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx_10 == NULL) return 1; /* spawn.alloc */
        ctx_10->a0 = a;
        ctx_10->a1 = a_len;
        ctx_10->a2 = b;
        ctx_10->a3 = b_len;
        ctx_10->a4 = t28;
        ctx_10->a5 = t29 < a_len ? t29 : a_len;
        ctx_10->a6 = 0.0;
        ctx_10->a7 = partial_10;
        pthread_t thread_10;
        if (pthread_create(&thread_10, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx_10) != 0) return 1; /* spawn.ok */
        int64_t t30 = (int64_t)thread_10;
        int64_t t31 = (int64_t)(10u * (uint64_t)t3);
        int64_t t32 = (int64_t)((uint64_t)t31 + (uint64_t)t3);
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx_11 = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx_11 == NULL) return 1; /* spawn.alloc */
        ctx_11->a0 = a;
        ctx_11->a1 = a_len;
        ctx_11->a2 = b;
        ctx_11->a3 = b_len;
        ctx_11->a4 = t31;
        ctx_11->a5 = t32 < a_len ? t32 : a_len;
        ctx_11->a6 = 0.0;
        ctx_11->a7 = partial_11;
        pthread_t thread_11;
        if (pthread_create(&thread_11, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx_11) != 0) return 1; /* spawn.ok */
        int64_t t33 = (int64_t)thread_11;
        int64_t t34 = (int64_t)(11u * (uint64_t)t3);
        int64_t t35 = (int64_t)((uint64_t)t34 + (uint64_t)t3);
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx_12 = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx_12 == NULL) return 1; /* spawn.alloc */
        ctx_12->a0 = a;
        ctx_12->a1 = a_len;
        ctx_12->a2 = b;
        ctx_12->a3 = b_len;
        ctx_12->a4 = t34;
        ctx_12->a5 = t35 < a_len ? t35 : a_len;
        ctx_12->a6 = 0.0;
        ctx_12->a7 = partial_12;
        pthread_t thread_12;
        if (pthread_create(&thread_12, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx_12) != 0) return 1; /* spawn.ok */
        int64_t t36 = (int64_t)thread_12;
        int64_t t37 = (int64_t)(12u * (uint64_t)t3);
        int64_t t38 = (int64_t)((uint64_t)t37 + (uint64_t)t3);
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx_13 = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx_13 == NULL) return 1; /* spawn.alloc */
        ctx_13->a0 = a;
        ctx_13->a1 = a_len;
        ctx_13->a2 = b;
        ctx_13->a3 = b_len;
        ctx_13->a4 = t37;
        ctx_13->a5 = t38 < a_len ? t38 : a_len;
        ctx_13->a6 = 0.0;
        ctx_13->a7 = partial_13;
        pthread_t thread_13;
        if (pthread_create(&thread_13, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx_13) != 0) return 1; /* spawn.ok */
        int64_t t39 = (int64_t)thread_13;
        int64_t t40 = (int64_t)(13u * (uint64_t)t3);
        int64_t t41 = (int64_t)((uint64_t)t40 + (uint64_t)t3);
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx_14 = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx_14 == NULL) return 1; /* spawn.alloc */
        ctx_14->a0 = a;
        ctx_14->a1 = a_len;
        ctx_14->a2 = b;
        ctx_14->a3 = b_len;
        ctx_14->a4 = t40;
        ctx_14->a5 = t41 < a_len ? t41 : a_len;
        ctx_14->a6 = 0.0;
        ctx_14->a7 = partial_14;
        pthread_t thread_14;
        if (pthread_create(&thread_14, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx_14) != 0) return 1; /* spawn.ok */
        int64_t t42 = (int64_t)thread_14;
        int64_t t43 = (int64_t)(14u * (uint64_t)t3);
        int64_t t44 = (int64_t)((uint64_t)t43 + (uint64_t)t3);
        ppy_ctx_ranges_dot_relaxed__par3__task *ctx_15 = (ppy_ctx_ranges_dot_relaxed__par3__task *)malloc(sizeof(ppy_ctx_ranges_dot_relaxed__par3__task));
        if (ctx_15 == NULL) return 1; /* spawn.alloc */
        ctx_15->a0 = a;
        ctx_15->a1 = a_len;
        ctx_15->a2 = b;
        ctx_15->a3 = b_len;
        ctx_15->a4 = t43;
        ctx_15->a5 = t44 < a_len ? t44 : a_len;
        ctx_15->a6 = 0.0;
        ctx_15->a7 = partial_15;
        pthread_t thread_15;
        if (pthread_create(&thread_15, NULL, ppy_thread_ranges_dot_relaxed__par3__task, ctx_15) != 0) return 1; /* spawn.ok */
        int64_t t45 = (int64_t)thread_15;
        int64_t t46 = (int64_t)(15u * (uint64_t)t3);
        double t47;
        int64_t t48 = (int64_t)ranges_dot_relaxed__par3(a, a_len, b, b_len, t46 < a_len ? t46 : a_len, a_len, 0.0, &t47);
        void *joined;
        if (pthread_join((pthread_t)t4, &joined) != 0) return 1; /* join.ok */
        int64_t t49 = (int64_t)(intptr_t)joined;
        void *joined_2;
        if (pthread_join((pthread_t)t6, &joined_2) != 0) return 1; /* join.ok */
        int64_t t50 = (int64_t)(intptr_t)joined_2;
        void *joined_3;
        if (pthread_join((pthread_t)t9, &joined_3) != 0) return 1; /* join.ok */
        int64_t t51 = (int64_t)(intptr_t)joined_3;
        void *joined_4;
        if (pthread_join((pthread_t)t12, &joined_4) != 0) return 1; /* join.ok */
        int64_t t52 = (int64_t)(intptr_t)joined_4;
        void *joined_5;
        if (pthread_join((pthread_t)t15, &joined_5) != 0) return 1; /* join.ok */
        int64_t t53 = (int64_t)(intptr_t)joined_5;
        void *joined_6;
        if (pthread_join((pthread_t)t18, &joined_6) != 0) return 1; /* join.ok */
        int64_t t54 = (int64_t)(intptr_t)joined_6;
        void *joined_7;
        if (pthread_join((pthread_t)t21, &joined_7) != 0) return 1; /* join.ok */
        int64_t t55 = (int64_t)(intptr_t)joined_7;
        void *joined_8;
        if (pthread_join((pthread_t)t24, &joined_8) != 0) return 1; /* join.ok */
        int64_t t56 = (int64_t)(intptr_t)joined_8;
        void *joined_9;
        if (pthread_join((pthread_t)t27, &joined_9) != 0) return 1; /* join.ok */
        int64_t t57 = (int64_t)(intptr_t)joined_9;
        void *joined_10;
        if (pthread_join((pthread_t)t30, &joined_10) != 0) return 1; /* join.ok */
        int64_t t58 = (int64_t)(intptr_t)joined_10;
        void *joined_11;
        if (pthread_join((pthread_t)t33, &joined_11) != 0) return 1; /* join.ok */
        int64_t t59 = (int64_t)(intptr_t)joined_11;
        void *joined_12;
        if (pthread_join((pthread_t)t36, &joined_12) != 0) return 1; /* join.ok */
        int64_t t60 = (int64_t)(intptr_t)joined_12;
        void *joined_13;
        if (pthread_join((pthread_t)t39, &joined_13) != 0) return 1; /* join.ok */
        int64_t t61 = (int64_t)(intptr_t)joined_13;
        void *joined_14;
        if (pthread_join((pthread_t)t42, &joined_14) != 0) return 1; /* join.ok */
        int64_t t62 = (int64_t)(intptr_t)joined_14;
        void *joined_15;
        if (pthread_join((pthread_t)t45, &joined_15) != 0) return 1; /* join.ok */
        int64_t t63 = (int64_t)(intptr_t)joined_15;
        if (!(((((((((((((((((t48 != 0) | (t49 != 0)) | (t50 != 0)) | (t51 != 0)) | (t52 != 0)) | (t53 != 0)) | (t54 != 0)) | (t55 != 0)) | (t56 != 0)) | (t57 != 0)) | (t58 != 0)) | (t59 != 0)) | (t60 != 0)) | (t61 != 0)) | (t62 != 0)) | (t63 != 0)) ^ true)) return 1; /* contract.ok */
        reduced = t1 + (t47 + *partial + *partial_2 + *partial_3 + *partial_4 + *partial_5 + *partial_6 + *partial_7 + *partial_8 + *partial_9 + *partial_10 + *partial_11 + *partial_12 + *partial_13 + *partial_14 + *partial_15);
    }
    total = reduced;
    *out = total;
    return 0;
}

int32_t ppy_ranges_count_odd(int64_t *xs, int64_t xs_len, int64_t scale, int64_t *out) {
    int64_t reduced;
    int64_t partial[1];
    int64_t partial_2[1];
    int64_t partial_3[1];
    int64_t partial_4[1];
    int64_t partial_5[1];
    int64_t partial_6[1];
    int64_t partial_7[1];
    int64_t partial_8[1];
    int64_t partial_9[1];
    int64_t partial_10[1];
    int64_t partial_11[1];
    int64_t partial_12[1];
    int64_t partial_13[1];
    int64_t partial_14[1];
    int64_t partial_15[1];

    int64_t hits = 0;
    int64_t t1 = hits;
    if (xs_len < 4096) {
        int64_t t2;
        if (ranges_count_odd__par4(scale, xs, xs_len, 0, xs_len, t1, &t2) != 0) return 1; /* call.ok */
        reduced = t2;
    } else {
        int64_t t3 = (int64_t)((uint64_t)xs_len + 15u) / 16;
        ppy_ctx_ranges_count_odd__par4__task *ctx = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx == NULL) return 1; /* spawn.alloc */
        ctx->a0 = scale;
        ctx->a1 = xs;
        ctx->a2 = xs_len;
        ctx->a3 = 0;
        ctx->a4 = t3 < xs_len ? t3 : xs_len;
        ctx->a5 = 0;
        ctx->a6 = partial;
        pthread_t thread;
        if (pthread_create(&thread, NULL, ppy_thread_ranges_count_odd__par4__task, ctx) != 0) return 1; /* spawn.ok */
        int64_t t4 = (int64_t)thread;
        int64_t t5 = (int64_t)((uint64_t)t3 + (uint64_t)t3);
        ppy_ctx_ranges_count_odd__par4__task *ctx_2 = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx_2 == NULL) return 1; /* spawn.alloc */
        ctx_2->a0 = scale;
        ctx_2->a1 = xs;
        ctx_2->a2 = xs_len;
        ctx_2->a3 = t3;
        ctx_2->a4 = t5 < xs_len ? t5 : xs_len;
        ctx_2->a5 = 0;
        ctx_2->a6 = partial_2;
        pthread_t thread_2;
        if (pthread_create(&thread_2, NULL, ppy_thread_ranges_count_odd__par4__task, ctx_2) != 0) return 1; /* spawn.ok */
        int64_t t6 = (int64_t)thread_2;
        int64_t t7 = (int64_t)(2u * (uint64_t)t3);
        int64_t t8 = (int64_t)((uint64_t)t7 + (uint64_t)t3);
        ppy_ctx_ranges_count_odd__par4__task *ctx_3 = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx_3 == NULL) return 1; /* spawn.alloc */
        ctx_3->a0 = scale;
        ctx_3->a1 = xs;
        ctx_3->a2 = xs_len;
        ctx_3->a3 = t7;
        ctx_3->a4 = t8 < xs_len ? t8 : xs_len;
        ctx_3->a5 = 0;
        ctx_3->a6 = partial_3;
        pthread_t thread_3;
        if (pthread_create(&thread_3, NULL, ppy_thread_ranges_count_odd__par4__task, ctx_3) != 0) return 1; /* spawn.ok */
        int64_t t9 = (int64_t)thread_3;
        int64_t t10 = (int64_t)(3u * (uint64_t)t3);
        int64_t t11 = (int64_t)((uint64_t)t10 + (uint64_t)t3);
        ppy_ctx_ranges_count_odd__par4__task *ctx_4 = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx_4 == NULL) return 1; /* spawn.alloc */
        ctx_4->a0 = scale;
        ctx_4->a1 = xs;
        ctx_4->a2 = xs_len;
        ctx_4->a3 = t10;
        ctx_4->a4 = t11 < xs_len ? t11 : xs_len;
        ctx_4->a5 = 0;
        ctx_4->a6 = partial_4;
        pthread_t thread_4;
        if (pthread_create(&thread_4, NULL, ppy_thread_ranges_count_odd__par4__task, ctx_4) != 0) return 1; /* spawn.ok */
        int64_t t12 = (int64_t)thread_4;
        int64_t t13 = (int64_t)(4u * (uint64_t)t3);
        int64_t t14 = (int64_t)((uint64_t)t13 + (uint64_t)t3);
        ppy_ctx_ranges_count_odd__par4__task *ctx_5 = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx_5 == NULL) return 1; /* spawn.alloc */
        ctx_5->a0 = scale;
        ctx_5->a1 = xs;
        ctx_5->a2 = xs_len;
        ctx_5->a3 = t13;
        ctx_5->a4 = t14 < xs_len ? t14 : xs_len;
        ctx_5->a5 = 0;
        ctx_5->a6 = partial_5;
        pthread_t thread_5;
        if (pthread_create(&thread_5, NULL, ppy_thread_ranges_count_odd__par4__task, ctx_5) != 0) return 1; /* spawn.ok */
        int64_t t15 = (int64_t)thread_5;
        int64_t t16 = (int64_t)(5u * (uint64_t)t3);
        int64_t t17 = (int64_t)((uint64_t)t16 + (uint64_t)t3);
        ppy_ctx_ranges_count_odd__par4__task *ctx_6 = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx_6 == NULL) return 1; /* spawn.alloc */
        ctx_6->a0 = scale;
        ctx_6->a1 = xs;
        ctx_6->a2 = xs_len;
        ctx_6->a3 = t16;
        ctx_6->a4 = t17 < xs_len ? t17 : xs_len;
        ctx_6->a5 = 0;
        ctx_6->a6 = partial_6;
        pthread_t thread_6;
        if (pthread_create(&thread_6, NULL, ppy_thread_ranges_count_odd__par4__task, ctx_6) != 0) return 1; /* spawn.ok */
        int64_t t18 = (int64_t)thread_6;
        int64_t t19 = (int64_t)(6u * (uint64_t)t3);
        int64_t t20 = (int64_t)((uint64_t)t19 + (uint64_t)t3);
        ppy_ctx_ranges_count_odd__par4__task *ctx_7 = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx_7 == NULL) return 1; /* spawn.alloc */
        ctx_7->a0 = scale;
        ctx_7->a1 = xs;
        ctx_7->a2 = xs_len;
        ctx_7->a3 = t19;
        ctx_7->a4 = t20 < xs_len ? t20 : xs_len;
        ctx_7->a5 = 0;
        ctx_7->a6 = partial_7;
        pthread_t thread_7;
        if (pthread_create(&thread_7, NULL, ppy_thread_ranges_count_odd__par4__task, ctx_7) != 0) return 1; /* spawn.ok */
        int64_t t21 = (int64_t)thread_7;
        int64_t t22 = (int64_t)(7u * (uint64_t)t3);
        int64_t t23 = (int64_t)((uint64_t)t22 + (uint64_t)t3);
        ppy_ctx_ranges_count_odd__par4__task *ctx_8 = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx_8 == NULL) return 1; /* spawn.alloc */
        ctx_8->a0 = scale;
        ctx_8->a1 = xs;
        ctx_8->a2 = xs_len;
        ctx_8->a3 = t22;
        ctx_8->a4 = t23 < xs_len ? t23 : xs_len;
        ctx_8->a5 = 0;
        ctx_8->a6 = partial_8;
        pthread_t thread_8;
        if (pthread_create(&thread_8, NULL, ppy_thread_ranges_count_odd__par4__task, ctx_8) != 0) return 1; /* spawn.ok */
        int64_t t24 = (int64_t)thread_8;
        int64_t t25 = (int64_t)(8u * (uint64_t)t3);
        int64_t t26 = (int64_t)((uint64_t)t25 + (uint64_t)t3);
        ppy_ctx_ranges_count_odd__par4__task *ctx_9 = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx_9 == NULL) return 1; /* spawn.alloc */
        ctx_9->a0 = scale;
        ctx_9->a1 = xs;
        ctx_9->a2 = xs_len;
        ctx_9->a3 = t25;
        ctx_9->a4 = t26 < xs_len ? t26 : xs_len;
        ctx_9->a5 = 0;
        ctx_9->a6 = partial_9;
        pthread_t thread_9;
        if (pthread_create(&thread_9, NULL, ppy_thread_ranges_count_odd__par4__task, ctx_9) != 0) return 1; /* spawn.ok */
        int64_t t27 = (int64_t)thread_9;
        int64_t t28 = (int64_t)(9u * (uint64_t)t3);
        int64_t t29 = (int64_t)((uint64_t)t28 + (uint64_t)t3);
        ppy_ctx_ranges_count_odd__par4__task *ctx_10 = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx_10 == NULL) return 1; /* spawn.alloc */
        ctx_10->a0 = scale;
        ctx_10->a1 = xs;
        ctx_10->a2 = xs_len;
        ctx_10->a3 = t28;
        ctx_10->a4 = t29 < xs_len ? t29 : xs_len;
        ctx_10->a5 = 0;
        ctx_10->a6 = partial_10;
        pthread_t thread_10;
        if (pthread_create(&thread_10, NULL, ppy_thread_ranges_count_odd__par4__task, ctx_10) != 0) return 1; /* spawn.ok */
        int64_t t30 = (int64_t)thread_10;
        int64_t t31 = (int64_t)(10u * (uint64_t)t3);
        int64_t t32 = (int64_t)((uint64_t)t31 + (uint64_t)t3);
        ppy_ctx_ranges_count_odd__par4__task *ctx_11 = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx_11 == NULL) return 1; /* spawn.alloc */
        ctx_11->a0 = scale;
        ctx_11->a1 = xs;
        ctx_11->a2 = xs_len;
        ctx_11->a3 = t31;
        ctx_11->a4 = t32 < xs_len ? t32 : xs_len;
        ctx_11->a5 = 0;
        ctx_11->a6 = partial_11;
        pthread_t thread_11;
        if (pthread_create(&thread_11, NULL, ppy_thread_ranges_count_odd__par4__task, ctx_11) != 0) return 1; /* spawn.ok */
        int64_t t33 = (int64_t)thread_11;
        int64_t t34 = (int64_t)(11u * (uint64_t)t3);
        int64_t t35 = (int64_t)((uint64_t)t34 + (uint64_t)t3);
        ppy_ctx_ranges_count_odd__par4__task *ctx_12 = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx_12 == NULL) return 1; /* spawn.alloc */
        ctx_12->a0 = scale;
        ctx_12->a1 = xs;
        ctx_12->a2 = xs_len;
        ctx_12->a3 = t34;
        ctx_12->a4 = t35 < xs_len ? t35 : xs_len;
        ctx_12->a5 = 0;
        ctx_12->a6 = partial_12;
        pthread_t thread_12;
        if (pthread_create(&thread_12, NULL, ppy_thread_ranges_count_odd__par4__task, ctx_12) != 0) return 1; /* spawn.ok */
        int64_t t36 = (int64_t)thread_12;
        int64_t t37 = (int64_t)(12u * (uint64_t)t3);
        int64_t t38 = (int64_t)((uint64_t)t37 + (uint64_t)t3);
        ppy_ctx_ranges_count_odd__par4__task *ctx_13 = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx_13 == NULL) return 1; /* spawn.alloc */
        ctx_13->a0 = scale;
        ctx_13->a1 = xs;
        ctx_13->a2 = xs_len;
        ctx_13->a3 = t37;
        ctx_13->a4 = t38 < xs_len ? t38 : xs_len;
        ctx_13->a5 = 0;
        ctx_13->a6 = partial_13;
        pthread_t thread_13;
        if (pthread_create(&thread_13, NULL, ppy_thread_ranges_count_odd__par4__task, ctx_13) != 0) return 1; /* spawn.ok */
        int64_t t39 = (int64_t)thread_13;
        int64_t t40 = (int64_t)(13u * (uint64_t)t3);
        int64_t t41 = (int64_t)((uint64_t)t40 + (uint64_t)t3);
        ppy_ctx_ranges_count_odd__par4__task *ctx_14 = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx_14 == NULL) return 1; /* spawn.alloc */
        ctx_14->a0 = scale;
        ctx_14->a1 = xs;
        ctx_14->a2 = xs_len;
        ctx_14->a3 = t40;
        ctx_14->a4 = t41 < xs_len ? t41 : xs_len;
        ctx_14->a5 = 0;
        ctx_14->a6 = partial_14;
        pthread_t thread_14;
        if (pthread_create(&thread_14, NULL, ppy_thread_ranges_count_odd__par4__task, ctx_14) != 0) return 1; /* spawn.ok */
        int64_t t42 = (int64_t)thread_14;
        int64_t t43 = (int64_t)(14u * (uint64_t)t3);
        int64_t t44 = (int64_t)((uint64_t)t43 + (uint64_t)t3);
        ppy_ctx_ranges_count_odd__par4__task *ctx_15 = (ppy_ctx_ranges_count_odd__par4__task *)malloc(sizeof(ppy_ctx_ranges_count_odd__par4__task));
        if (ctx_15 == NULL) return 1; /* spawn.alloc */
        ctx_15->a0 = scale;
        ctx_15->a1 = xs;
        ctx_15->a2 = xs_len;
        ctx_15->a3 = t43;
        ctx_15->a4 = t44 < xs_len ? t44 : xs_len;
        ctx_15->a5 = 0;
        ctx_15->a6 = partial_15;
        pthread_t thread_15;
        if (pthread_create(&thread_15, NULL, ppy_thread_ranges_count_odd__par4__task, ctx_15) != 0) return 1; /* spawn.ok */
        int64_t t45 = (int64_t)thread_15;
        int64_t t46 = (int64_t)(15u * (uint64_t)t3);
        int64_t t47;
        int64_t t48 = (int64_t)ranges_count_odd__par4(scale, xs, xs_len, t46 < xs_len ? t46 : xs_len, xs_len, 0, &t47);
        void *joined;
        if (pthread_join((pthread_t)t4, &joined) != 0) return 1; /* join.ok */
        int64_t t49 = (int64_t)(intptr_t)joined;
        void *joined_2;
        if (pthread_join((pthread_t)t6, &joined_2) != 0) return 1; /* join.ok */
        int64_t t50 = (int64_t)(intptr_t)joined_2;
        void *joined_3;
        if (pthread_join((pthread_t)t9, &joined_3) != 0) return 1; /* join.ok */
        int64_t t51 = (int64_t)(intptr_t)joined_3;
        void *joined_4;
        if (pthread_join((pthread_t)t12, &joined_4) != 0) return 1; /* join.ok */
        int64_t t52 = (int64_t)(intptr_t)joined_4;
        void *joined_5;
        if (pthread_join((pthread_t)t15, &joined_5) != 0) return 1; /* join.ok */
        int64_t t53 = (int64_t)(intptr_t)joined_5;
        void *joined_6;
        if (pthread_join((pthread_t)t18, &joined_6) != 0) return 1; /* join.ok */
        int64_t t54 = (int64_t)(intptr_t)joined_6;
        void *joined_7;
        if (pthread_join((pthread_t)t21, &joined_7) != 0) return 1; /* join.ok */
        int64_t t55 = (int64_t)(intptr_t)joined_7;
        void *joined_8;
        if (pthread_join((pthread_t)t24, &joined_8) != 0) return 1; /* join.ok */
        int64_t t56 = (int64_t)(intptr_t)joined_8;
        void *joined_9;
        if (pthread_join((pthread_t)t27, &joined_9) != 0) return 1; /* join.ok */
        int64_t t57 = (int64_t)(intptr_t)joined_9;
        void *joined_10;
        if (pthread_join((pthread_t)t30, &joined_10) != 0) return 1; /* join.ok */
        int64_t t58 = (int64_t)(intptr_t)joined_10;
        void *joined_11;
        if (pthread_join((pthread_t)t33, &joined_11) != 0) return 1; /* join.ok */
        int64_t t59 = (int64_t)(intptr_t)joined_11;
        void *joined_12;
        if (pthread_join((pthread_t)t36, &joined_12) != 0) return 1; /* join.ok */
        int64_t t60 = (int64_t)(intptr_t)joined_12;
        void *joined_13;
        if (pthread_join((pthread_t)t39, &joined_13) != 0) return 1; /* join.ok */
        int64_t t61 = (int64_t)(intptr_t)joined_13;
        void *joined_14;
        if (pthread_join((pthread_t)t42, &joined_14) != 0) return 1; /* join.ok */
        int64_t t62 = (int64_t)(intptr_t)joined_14;
        void *joined_15;
        if (pthread_join((pthread_t)t45, &joined_15) != 0) return 1; /* join.ok */
        int64_t t63 = (int64_t)(intptr_t)joined_15;
        if (!(((((((((((((((((t48 != 0) | (t49 != 0)) | (t50 != 0)) | (t51 != 0)) | (t52 != 0)) | (t53 != 0)) | (t54 != 0)) | (t55 != 0)) | (t56 != 0)) | (t57 != 0)) | (t58 != 0)) | (t59 != 0)) | (t60 != 0)) | (t61 != 0)) | (t62 != 0)) | (t63 != 0)) ^ true)) return 1; /* contract.ok */
        reduced = (int64_t)((uint64_t)t1 + ((uint64_t)t47 + (uint64_t)*partial + (uint64_t)*partial_2 + (uint64_t)*partial_3 + (uint64_t)*partial_4 + (uint64_t)*partial_5 + (uint64_t)*partial_6 + (uint64_t)*partial_7 + (uint64_t)*partial_8 + (uint64_t)*partial_9 + (uint64_t)*partial_10 + (uint64_t)*partial_11 + (uint64_t)*partial_12 + (uint64_t)*partial_13 + (uint64_t)*partial_14 + (uint64_t)*partial_15));
    }
    hits = reduced;
    *out = hits;
    return 0;
}

int32_t ppy_ranges_fill(double *out, int64_t n, double base, double *out_2) {
    if (n < 4096) {
        int64_t res;
        if (ranges_fill__par5(base, out, 0, n, &res) != 0) return 1; /* call.ok */
    } else {
        int64_t t1 = (int64_t)((uint64_t)n + 15u) / 16;
        ppy_ctx_ranges_fill__par5 *ctx = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx == NULL) return 1; /* spawn.alloc */
        ctx->a0 = base;
        ctx->a1 = out;
        ctx->a2 = 0;
        ctx->a3 = t1 < n ? t1 : n;
        pthread_t thread;
        if (pthread_create(&thread, NULL, ppy_thread_ranges_fill__par5, ctx) != 0) return 1; /* spawn.ok */
        int64_t t2 = (int64_t)thread;
        int64_t t3 = (int64_t)((uint64_t)t1 + (uint64_t)t1);
        ppy_ctx_ranges_fill__par5 *ctx_2 = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx_2 == NULL) return 1; /* spawn.alloc */
        ctx_2->a0 = base;
        ctx_2->a1 = out;
        ctx_2->a2 = t1;
        ctx_2->a3 = t3 < n ? t3 : n;
        pthread_t thread_2;
        if (pthread_create(&thread_2, NULL, ppy_thread_ranges_fill__par5, ctx_2) != 0) return 1; /* spawn.ok */
        int64_t t4 = (int64_t)thread_2;
        int64_t t5 = (int64_t)(2u * (uint64_t)t1);
        int64_t t6 = (int64_t)((uint64_t)t5 + (uint64_t)t1);
        ppy_ctx_ranges_fill__par5 *ctx_3 = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx_3 == NULL) return 1; /* spawn.alloc */
        ctx_3->a0 = base;
        ctx_3->a1 = out;
        ctx_3->a2 = t5;
        ctx_3->a3 = t6 < n ? t6 : n;
        pthread_t thread_3;
        if (pthread_create(&thread_3, NULL, ppy_thread_ranges_fill__par5, ctx_3) != 0) return 1; /* spawn.ok */
        int64_t t7 = (int64_t)thread_3;
        int64_t t8 = (int64_t)(3u * (uint64_t)t1);
        int64_t t9 = (int64_t)((uint64_t)t8 + (uint64_t)t1);
        ppy_ctx_ranges_fill__par5 *ctx_4 = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx_4 == NULL) return 1; /* spawn.alloc */
        ctx_4->a0 = base;
        ctx_4->a1 = out;
        ctx_4->a2 = t8;
        ctx_4->a3 = t9 < n ? t9 : n;
        pthread_t thread_4;
        if (pthread_create(&thread_4, NULL, ppy_thread_ranges_fill__par5, ctx_4) != 0) return 1; /* spawn.ok */
        int64_t t10 = (int64_t)thread_4;
        int64_t t11 = (int64_t)(4u * (uint64_t)t1);
        int64_t t12 = (int64_t)((uint64_t)t11 + (uint64_t)t1);
        ppy_ctx_ranges_fill__par5 *ctx_5 = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx_5 == NULL) return 1; /* spawn.alloc */
        ctx_5->a0 = base;
        ctx_5->a1 = out;
        ctx_5->a2 = t11;
        ctx_5->a3 = t12 < n ? t12 : n;
        pthread_t thread_5;
        if (pthread_create(&thread_5, NULL, ppy_thread_ranges_fill__par5, ctx_5) != 0) return 1; /* spawn.ok */
        int64_t t13 = (int64_t)thread_5;
        int64_t t14 = (int64_t)(5u * (uint64_t)t1);
        int64_t t15 = (int64_t)((uint64_t)t14 + (uint64_t)t1);
        ppy_ctx_ranges_fill__par5 *ctx_6 = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx_6 == NULL) return 1; /* spawn.alloc */
        ctx_6->a0 = base;
        ctx_6->a1 = out;
        ctx_6->a2 = t14;
        ctx_6->a3 = t15 < n ? t15 : n;
        pthread_t thread_6;
        if (pthread_create(&thread_6, NULL, ppy_thread_ranges_fill__par5, ctx_6) != 0) return 1; /* spawn.ok */
        int64_t t16 = (int64_t)thread_6;
        int64_t t17 = (int64_t)(6u * (uint64_t)t1);
        int64_t t18 = (int64_t)((uint64_t)t17 + (uint64_t)t1);
        ppy_ctx_ranges_fill__par5 *ctx_7 = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx_7 == NULL) return 1; /* spawn.alloc */
        ctx_7->a0 = base;
        ctx_7->a1 = out;
        ctx_7->a2 = t17;
        ctx_7->a3 = t18 < n ? t18 : n;
        pthread_t thread_7;
        if (pthread_create(&thread_7, NULL, ppy_thread_ranges_fill__par5, ctx_7) != 0) return 1; /* spawn.ok */
        int64_t t19 = (int64_t)thread_7;
        int64_t t20 = (int64_t)(7u * (uint64_t)t1);
        int64_t t21 = (int64_t)((uint64_t)t20 + (uint64_t)t1);
        ppy_ctx_ranges_fill__par5 *ctx_8 = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx_8 == NULL) return 1; /* spawn.alloc */
        ctx_8->a0 = base;
        ctx_8->a1 = out;
        ctx_8->a2 = t20;
        ctx_8->a3 = t21 < n ? t21 : n;
        pthread_t thread_8;
        if (pthread_create(&thread_8, NULL, ppy_thread_ranges_fill__par5, ctx_8) != 0) return 1; /* spawn.ok */
        int64_t t22 = (int64_t)thread_8;
        int64_t t23 = (int64_t)(8u * (uint64_t)t1);
        int64_t t24 = (int64_t)((uint64_t)t23 + (uint64_t)t1);
        ppy_ctx_ranges_fill__par5 *ctx_9 = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx_9 == NULL) return 1; /* spawn.alloc */
        ctx_9->a0 = base;
        ctx_9->a1 = out;
        ctx_9->a2 = t23;
        ctx_9->a3 = t24 < n ? t24 : n;
        pthread_t thread_9;
        if (pthread_create(&thread_9, NULL, ppy_thread_ranges_fill__par5, ctx_9) != 0) return 1; /* spawn.ok */
        int64_t t25 = (int64_t)thread_9;
        int64_t t26 = (int64_t)(9u * (uint64_t)t1);
        int64_t t27 = (int64_t)((uint64_t)t26 + (uint64_t)t1);
        ppy_ctx_ranges_fill__par5 *ctx_10 = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx_10 == NULL) return 1; /* spawn.alloc */
        ctx_10->a0 = base;
        ctx_10->a1 = out;
        ctx_10->a2 = t26;
        ctx_10->a3 = t27 < n ? t27 : n;
        pthread_t thread_10;
        if (pthread_create(&thread_10, NULL, ppy_thread_ranges_fill__par5, ctx_10) != 0) return 1; /* spawn.ok */
        int64_t t28 = (int64_t)thread_10;
        int64_t t29 = (int64_t)(10u * (uint64_t)t1);
        int64_t t30 = (int64_t)((uint64_t)t29 + (uint64_t)t1);
        ppy_ctx_ranges_fill__par5 *ctx_11 = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx_11 == NULL) return 1; /* spawn.alloc */
        ctx_11->a0 = base;
        ctx_11->a1 = out;
        ctx_11->a2 = t29;
        ctx_11->a3 = t30 < n ? t30 : n;
        pthread_t thread_11;
        if (pthread_create(&thread_11, NULL, ppy_thread_ranges_fill__par5, ctx_11) != 0) return 1; /* spawn.ok */
        int64_t t31 = (int64_t)thread_11;
        int64_t t32 = (int64_t)(11u * (uint64_t)t1);
        int64_t t33 = (int64_t)((uint64_t)t32 + (uint64_t)t1);
        ppy_ctx_ranges_fill__par5 *ctx_12 = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx_12 == NULL) return 1; /* spawn.alloc */
        ctx_12->a0 = base;
        ctx_12->a1 = out;
        ctx_12->a2 = t32;
        ctx_12->a3 = t33 < n ? t33 : n;
        pthread_t thread_12;
        if (pthread_create(&thread_12, NULL, ppy_thread_ranges_fill__par5, ctx_12) != 0) return 1; /* spawn.ok */
        int64_t t34 = (int64_t)thread_12;
        int64_t t35 = (int64_t)(12u * (uint64_t)t1);
        int64_t t36 = (int64_t)((uint64_t)t35 + (uint64_t)t1);
        ppy_ctx_ranges_fill__par5 *ctx_13 = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx_13 == NULL) return 1; /* spawn.alloc */
        ctx_13->a0 = base;
        ctx_13->a1 = out;
        ctx_13->a2 = t35;
        ctx_13->a3 = t36 < n ? t36 : n;
        pthread_t thread_13;
        if (pthread_create(&thread_13, NULL, ppy_thread_ranges_fill__par5, ctx_13) != 0) return 1; /* spawn.ok */
        int64_t t37 = (int64_t)thread_13;
        int64_t t38 = (int64_t)(13u * (uint64_t)t1);
        int64_t t39 = (int64_t)((uint64_t)t38 + (uint64_t)t1);
        ppy_ctx_ranges_fill__par5 *ctx_14 = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx_14 == NULL) return 1; /* spawn.alloc */
        ctx_14->a0 = base;
        ctx_14->a1 = out;
        ctx_14->a2 = t38;
        ctx_14->a3 = t39 < n ? t39 : n;
        pthread_t thread_14;
        if (pthread_create(&thread_14, NULL, ppy_thread_ranges_fill__par5, ctx_14) != 0) return 1; /* spawn.ok */
        int64_t t40 = (int64_t)thread_14;
        int64_t t41 = (int64_t)(14u * (uint64_t)t1);
        int64_t t42 = (int64_t)((uint64_t)t41 + (uint64_t)t1);
        ppy_ctx_ranges_fill__par5 *ctx_15 = (ppy_ctx_ranges_fill__par5 *)malloc(sizeof(ppy_ctx_ranges_fill__par5));
        if (ctx_15 == NULL) return 1; /* spawn.alloc */
        ctx_15->a0 = base;
        ctx_15->a1 = out;
        ctx_15->a2 = t41;
        ctx_15->a3 = t42 < n ? t42 : n;
        pthread_t thread_15;
        if (pthread_create(&thread_15, NULL, ppy_thread_ranges_fill__par5, ctx_15) != 0) return 1; /* spawn.ok */
        int64_t t43 = (int64_t)thread_15;
        int64_t t44 = (int64_t)(15u * (uint64_t)t1);
        int64_t res_2;
        int64_t t45 = (int64_t)ranges_fill__par5(base, out, t44 < n ? t44 : n, n, &res_2);
        void *joined;
        if (pthread_join((pthread_t)t2, &joined) != 0) return 1; /* join.ok */
        int64_t t46 = (int64_t)(intptr_t)joined;
        void *joined_2;
        if (pthread_join((pthread_t)t4, &joined_2) != 0) return 1; /* join.ok */
        int64_t t47 = (int64_t)(intptr_t)joined_2;
        void *joined_3;
        if (pthread_join((pthread_t)t7, &joined_3) != 0) return 1; /* join.ok */
        int64_t t48 = (int64_t)(intptr_t)joined_3;
        void *joined_4;
        if (pthread_join((pthread_t)t10, &joined_4) != 0) return 1; /* join.ok */
        int64_t t49 = (int64_t)(intptr_t)joined_4;
        void *joined_5;
        if (pthread_join((pthread_t)t13, &joined_5) != 0) return 1; /* join.ok */
        int64_t t50 = (int64_t)(intptr_t)joined_5;
        void *joined_6;
        if (pthread_join((pthread_t)t16, &joined_6) != 0) return 1; /* join.ok */
        int64_t t51 = (int64_t)(intptr_t)joined_6;
        void *joined_7;
        if (pthread_join((pthread_t)t19, &joined_7) != 0) return 1; /* join.ok */
        int64_t t52 = (int64_t)(intptr_t)joined_7;
        void *joined_8;
        if (pthread_join((pthread_t)t22, &joined_8) != 0) return 1; /* join.ok */
        int64_t t53 = (int64_t)(intptr_t)joined_8;
        void *joined_9;
        if (pthread_join((pthread_t)t25, &joined_9) != 0) return 1; /* join.ok */
        int64_t t54 = (int64_t)(intptr_t)joined_9;
        void *joined_10;
        if (pthread_join((pthread_t)t28, &joined_10) != 0) return 1; /* join.ok */
        int64_t t55 = (int64_t)(intptr_t)joined_10;
        void *joined_11;
        if (pthread_join((pthread_t)t31, &joined_11) != 0) return 1; /* join.ok */
        int64_t t56 = (int64_t)(intptr_t)joined_11;
        void *joined_12;
        if (pthread_join((pthread_t)t34, &joined_12) != 0) return 1; /* join.ok */
        int64_t t57 = (int64_t)(intptr_t)joined_12;
        void *joined_13;
        if (pthread_join((pthread_t)t37, &joined_13) != 0) return 1; /* join.ok */
        int64_t t58 = (int64_t)(intptr_t)joined_13;
        void *joined_14;
        if (pthread_join((pthread_t)t40, &joined_14) != 0) return 1; /* join.ok */
        int64_t t59 = (int64_t)(intptr_t)joined_14;
        void *joined_15;
        if (pthread_join((pthread_t)t43, &joined_15) != 0) return 1; /* join.ok */
        int64_t t60 = (int64_t)(intptr_t)joined_15;
        if (!(((((((((((((((((t45 != 0) | (t46 != 0)) | (t47 != 0)) | (t48 != 0)) | (t49 != 0)) | (t50 != 0)) | (t51 != 0)) | (t52 != 0)) | (t53 != 0)) | (t54 != 0)) | (t55 != 0)) | (t56 != 0)) | (t57 != 0)) | (t58 != 0)) | (t59 != 0)) | (t60 != 0)) ^ true)) return 1; /* contract.ok */
    }
    double acc = 0.0;
    if (ranges_fill__par6(out, 0, n, acc, &acc) != 0) return 1; /* call.ok */
    *out_2 = acc;
    return 0;
}

int32_t ranges_squares__par1(int64_t *out, int64_t __ppy_begin_, int64_t __ppy_end_, int64_t *out_2) {
    int64_t t1 = (int64_t)((uint64_t)__ppy_end_ - 1u);
    int64_t t2;
    bool t3 = ppy_ovf_mul_i64(__ppy_begin_, __ppy_begin_, &t2) != 0;
    int64_t t4;
    bool t5 = ppy_ovf_mul_i64(__ppy_begin_, t1, &t4) != 0;
    int64_t t6;
    bool t7 = ppy_ovf_mul_i64(t1, __ppy_begin_, &t6) != 0;
    int64_t t8;
    bool t9 = ppy_ovf_mul_i64(t1, t1, &t8) != 0;
    if (!((((t3 | t5) | t7) | t9) ^ true)) return 1; /* overflow.ok */
    int64_t i = __ppy_begin_;
    while (i < __ppy_end_) {
        out[i] = i * i;
        if (ppy_ovf_add_i64(i, 1, &i)) return 1; /* arith.ok */
    }
    *out_2 = 0;
    return 0;
}

int32_t ranges_dot__par2(double *a, int64_t a_len, double *b, int64_t b_len, int64_t __ppy_begin_, int64_t __ppy_end_, double total, double *out) {
    int64_t t1 = (int64_t)((uint64_t)__ppy_end_ - 1u);
    if (__ppy_begin_ < 0 || t1 >= a_len) return 1; /* bounds.ok */
    if (__ppy_begin_ < 0 || t1 >= b_len) return 1; /* bounds.ok */
    int64_t i = __ppy_begin_;
    while (i < __ppy_end_) {
        total += a[i] * b[i];
        if (ppy_ovf_add_i64(i, 1, &i)) return 1; /* arith.ok */
    }
    *out = total;
    return 0;
}

int32_t ranges_dot_relaxed__par3(double *a, int64_t a_len, double *b, int64_t b_len, int64_t __ppy_begin_, int64_t __ppy_end_, double total, double *out) {
    int64_t t1 = (int64_t)((uint64_t)__ppy_end_ - 1u);
    if (__ppy_begin_ < 0 || t1 >= a_len) return 1; /* bounds.ok */
    if (__ppy_begin_ < 0 || t1 >= b_len) return 1; /* bounds.ok */
    int64_t i = __ppy_begin_;
    while (i < __ppy_end_) {
        total += a[i] * b[i];
        if (ppy_ovf_add_i64(i, 1, &i)) return 1; /* arith.ok */
    }
    *out = total;
    return 0;
}

int32_t ranges_count_odd__par4(int64_t scale, int64_t *xs, int64_t xs_len, int64_t __ppy_begin_, int64_t __ppy_end_, int64_t hits, int64_t *out) {
    if (__ppy_begin_ < 0 || (int64_t)((uint64_t)__ppy_end_ - 1u) >= xs_len) return 1; /* bounds.ok */
    int64_t i = __ppy_begin_;
    while (i < __ppy_end_) {
        int64_t r = xs[i] % 2;
        if (((r != 0 && (xs[i] < 0) != (2 < 0)) ? r + 2 : r) == 1) {
            if (ppy_ovf_add_i64(hits, scale, &hits)) return 1; /* arith.ok */
        }
        if (ppy_ovf_add_i64(i, 1, &i)) return 1; /* arith.ok */
    }
    *out = hits;
    return 0;
}

int32_t ranges_fill__par5(double base, double *out, int64_t __ppy_begin_, int64_t __ppy_end_, int64_t *out_2) {
    int64_t i = __ppy_begin_;
    while (i < __ppy_end_) {
        out[i] = base + (double)i;
        if (ppy_ovf_add_i64(i, 1, &i)) return 1; /* arith.ok */
    }
    *out_2 = 0;
    return 0;
}

int32_t ranges_fill__par6(double *out, int64_t __ppy_begin_, int64_t __ppy_end_, double acc, double *out_2) {
    int64_t j = __ppy_begin_;
    while (j < __ppy_end_) {
        acc += out[j];
        if (ppy_ovf_add_i64(j, 1, &j)) return 1; /* arith.ok */
    }
    *out_2 = acc;
    return 0;
}

int32_t ranges_dot_relaxed__par3__task(double *a, int64_t a_len, double *b, int64_t b_len, int64_t begin, int64_t end, double start, double *out, int64_t *out_2) {
    double t1;
    if (ranges_dot_relaxed__par3(a, a_len, b, b_len, begin, end, start, &t1) != 0) return 1; /* call.ok */
    *out = t1;
    *out_2 = 0;
    return 0;
}

int32_t ranges_count_odd__par4__task(int64_t scale, int64_t *xs, int64_t xs_len, int64_t begin, int64_t end, int64_t start, int64_t *out, int64_t *out_2) {
    int64_t t1;
    if (ranges_count_odd__par4(scale, xs, xs_len, begin, end, start, &t1) != 0) return 1; /* call.ok */
    *out = t1;
    *out_2 = 0;
    return 0;
}

Writes and reductions

def squares(out: native.ptr[int], n: int) -> int:
    for i in parallel.range(n):
        native.store(native.offset(out, i), i * i)
    return native.load(native.offset(out, n - 1))


def dot(a: Buffer[float], b: Buffer[float]) -> float:
    total = 0.0
    for i in parallel.range(len(a)):
        total += a[i] * b[i]
    return total

A loop that writes through a pointer or a buffer splits without ceremony. One += into an outer int or float is a reduction, lowered as a parallel.reduce:

  • An integer reduction (count_odd) splits freely.
  • A floating-point reduction keeps its order, because the answer would change otherwise. dot prints twelve digits and they are the same on every path.
  • @ppy.fastmath permits the reassociation, which is why dot_relaxed prints six.

@ppy.parallel

@ppy.parallel on a function asks the same of its outermost range loops without spelling parallel.range. A loop that does not pass the analysis stays serial and says why in an optimization remark. fill has two, and --report-opt names what happened to each.

Backends

[tool.ppy.parallel] backend chooses how the chunks run, and every choice gives the same answer:

  • threads
  • serial
  • simd
  • openmp (for ppy emit c)

Compared with Numba, Taichi, Mojo, and NumPy

The same four kernels over eight million elements, each written the way its tool wants it, in compare/: ranges_bench.ppy (this example's kernels, with a relaxed dot beside the ordered one), ranges_numba.py, ranges_taichi.py, ranges.mojo, ranges_numpy.py. Milliseconds, best of five warm calls, over five processes.

PPy: parallel.range, typed parameters, the same file on CPython. dot keeps a serial sum's order by design, and @ppy.fastmath is the relaxed one:

def dot(a: Buffer[float], b: Buffer[float]) -> float:
    total = 0.0
    for i in parallel.range(len(a)):
        total += a[i] * b[i]
    return total

Numba: @njit(parallel=True) and prange. A prange sum reassociates on its own, so there is no ordered dot to write, and integers wrap silently:

@njit(parallel=True)
def dot(a, b):
    total = 0.0
    for i in prange(len(a)):
        total += a[i] * b[i]
    return total

Taichi: arrays are ti.fields declared at module level, and the outermost loop of a kernel is parallel by itself. The defaults overflow in 32 bits and accumulate in f32 silently; ti.init(default_ip=ti.i64, default_fp=ti.f64) is what makes the answers match:

@ti.kernel
def dot() -> ti.f64:
    total = 0.0
    for i in range(N):
        total += a_f[i] * b_f[i]
    return total

Mojo: a different language. It uses List[Float64], mut parameters, Int64(i) where an index meets an element, and parallelize from max.algorithm taking a function of one index, so a reduction is partial sums per worker written by hand.

NumPy has no loop to write and no way to keep one: dot is BLAS, and count_odd builds two temporaries to count.

PPy ppy run Numba prange Taichi Mojo NumPy
squares 1.34 ± 0.03 0.93 ± 0.06 1.16 ± 0.04 1.05 ± 0.05 13.82 ± 0.52
dot, in order 6.39 ± 0.40 — — 14.93 ± 1.30 —
dot, reassociated 1.80 ± 0.13 1.34 ± 0.04 1.50 ± 0.04 1.52 ± 0.10 1.33 ± 0.05
count_odd 0.99 ± 0.04 0.70 ± 0.04 1.23 ± 0.06 1.37 ± 0.19 27.64 ± 1.35
fill, serial 6.17 ± 0.11 7.51 ± 0.04 4.02 ± 0.38 19.57 ± 1.09 15.43 ± 0.28

On the elementwise kernels the four compiled tools are within a few tenths of a millisecond, which is the memory bandwidth of the machine. NumPy's temporaries are ten times behind.

The reductions are where the models differ. PPy's ordered dot is the only one that promises a serial sum's answer, and it is a serial sum. Its relaxed dot, Numba's and Taichi's are the same vectorized tree.

The serial fill is the row that measures a plain loop with no threads, and Mojo's plain List loop is the slow one there.

Intel Core Ultra 9 386H (16 threads), threads backend; Numba 0.67.0, Taichi 1.7.4, NumPy 2.5.3 on CPython 3.12.13, Mojo 1.0.0, PPy on CPython 3.13.13, from a checkout on a native filesystem.

Limitations

The loop body may not assign a variable that lives outside the loop, break, or return. A function that does is refused with the reason (E1650).

Read on: Parallel loops · Parallel fused kernels

ranges.ppy is hand-written; there is no .py source and no conversion step.

35_parallel_range/ranges.ppy

import array

import ppy
from ppy import Buffer, native, parallel


def squares(out: native.ptr[int], n: int) -> int:
    for i in parallel.range(n):
        native.store(native.offset(out, i), i * i)
    return native.load(native.offset(out, n - 1))


def dot(a: Buffer[float], b: Buffer[float]) -> float:
    total = 0.0
    for i in parallel.range(len(a)):
        total += a[i] * b[i]
    return total


@ppy.fastmath
def dot_relaxed(a: Buffer[float], b: Buffer[float]) -> float:
    total = 0.0
    for i in parallel.range(len(a)):
        total += a[i] * b[i]
    return total


def count_odd(xs: Buffer[int], scale: int) -> int:
    hits = 0
    for i in parallel.range(0, len(xs)):
        if xs[i] % 2 == 1:
            hits += scale
    return hits


@ppy.parallel
def fill(out: native.ptr[float], n: int, base: float) -> float:
    for i in range(n):
        native.store(native.offset(out, i), base + i)
    acc = 0.0
    for j in range(n):
        acc += native.load(native.offset(out, j))
    return acc


def main() -> None:
    n = 100000
    slots = native.stack_alloc[int](n)
    print(squares(slots, n))
    a = memoryview(array.array("d", [0.001 * i for i in range(n)]))
    b = memoryview(array.array("d", [1.0 / (1 + i) for i in range(n)]))
    print(f"{dot(a, b):.12f}")
    print(f"{dot_relaxed(a, b):.6f}")
    odds = memoryview(array.array("q", [i % 3 for i in range(n)]))
    print(count_odd(odds, 3))
    room = native.stack_alloc[float](1000)
    print(fill(room, 1000, 0.5))


main()

Counterpart programs

The programs the comparison above measured, each written the way its tool expects. The PPy one is first.

ranges_bench.ppy (PPy)
"""The four parallel kernels over eight million elements, timed warm."""

import array
import time

import ppy
from ppy import Buffer, parallel

N = 8_000_000


def squares(out: Buffer[int]) -> int:
    for i in parallel.range(len(out)):
        out[i] = i * i
    return out[len(out) - 1]


def dot(a: Buffer[float], b: Buffer[float]) -> float:
    total = 0.0
    for i in parallel.range(len(a)):
        total += a[i] * b[i]
    return total


@ppy.fastmath
def dot_relaxed(a: Buffer[float], b: Buffer[float]) -> float:
    total = 0.0
    for i in parallel.range(len(a)):
        total += a[i] * b[i]
    return total


def count_odd(xs: Buffer[int], scale: int) -> int:
    hits = 0
    for i in parallel.range(0, len(xs)):
        if xs[i] % 2 == 1:
            hits += scale
    return hits


@ppy.parallel
def fill(out: Buffer[float], base: float) -> float:
    for i in range(len(out)):
        out[i] = base + i
    acc = 0.0
    for j in range(len(out)):
        acc += out[j]
    return acc


def main() -> None:
    a = memoryview(array.array("d", (0.001 * i for i in range(N))))
    b = memoryview(array.array("d", (1.0 / (1 + i) for i in range(N))))
    ints = memoryview(array.array("q", (i % 3 for i in range(N))))
    room = memoryview(array.array("q", bytes(8 * N)))
    floats = memoryview(array.array("d", bytes(8 * N)))
    ordered = round(dot(a, b), 6)
    relaxed = round(dot_relaxed(a, b), 3)
    print(squares(room), ordered, relaxed, count_odd(ints, 3), fill(floats, 0.5))
    best = [1e9, 1e9, 1e9, 1e9, 1e9]
    for _ in range(5):
        started = time.perf_counter()
        squares(room)
        best[0] = min(best[0], (time.perf_counter() - started) * 1000.0)
        started = time.perf_counter()
        dot(a, b)
        best[1] = min(best[1], (time.perf_counter() - started) * 1000.0)
        started = time.perf_counter()
        dot_relaxed(a, b)
        best[2] = min(best[2], (time.perf_counter() - started) * 1000.0)
        started = time.perf_counter()
        count_odd(ints, 3)
        best[3] = min(best[3], (time.perf_counter() - started) * 1000.0)
        started = time.perf_counter()
        fill(floats, 0.5)
        best[4] = min(best[4], (time.perf_counter() - started) * 1000.0)
    labels = ("squares", "dot", "dot_relaxed", "count_odd", "fill")
    for label, took in zip(labels, best, strict=True):
        print(f"# {label}: {took:.2f} ms")


main()
ranges.mojo (Mojo)
"""The four kernels in Mojo: `parallelize` over indices, partial sums by hand; timed warm."""

from max.algorithm import parallelize
from std.time import perf_counter_ns

comptime N = 8_000_000
comptime WORKERS = 64


def squares(mut out: List[Int64]) -> Int64:
    var p = out.unsafe_ptr()

    @parameter
    def one(i: Int):
        p[i] = Int64(i) * Int64(i)

    parallelize[one](N)
    return out[N - 1]


def dot(a: List[Float64], b: List[Float64]) -> Float64:
    var total: Float64 = 0.0
    for i in range(N):
        total += a[i] * b[i]
    return total


def dot_relaxed(a: List[Float64], b: List[Float64]) -> Float64:
    var partial = List[Float64](length=WORKERS, fill=0.0)
    var ap = a.unsafe_ptr()
    var bp = b.unsafe_ptr()
    var pp = partial.unsafe_ptr()
    var chunk = (N + WORKERS - 1) // WORKERS

    @parameter
    def piece(w: Int):
        var total: Float64 = 0.0
        var stop = min(N, (w + 1) * chunk)
        for i in range(w * chunk, stop):
            total += ap[i] * bp[i]
        pp[w] = total

    parallelize[piece](WORKERS)
    var total: Float64 = 0.0
    for w in range(WORKERS):
        total += partial[w]
    return total


def count_odd(xs: List[Int64], scale: Int64) -> Int64:
    var partial = List[Int64](length=WORKERS, fill=0)
    var xp = xs.unsafe_ptr()
    var pp = partial.unsafe_ptr()
    var chunk = (N + WORKERS - 1) // WORKERS

    @parameter
    def piece(w: Int):
        var hits: Int64 = 0
        var stop = min(N, (w + 1) * chunk)
        for i in range(w * chunk, stop):
            if xp[i] % 2 == 1:
                hits += scale
        pp[w] = hits

    parallelize[piece](WORKERS)
    var hits: Int64 = 0
    for w in range(WORKERS):
        hits += partial[w]
    return hits


def fill(mut out: List[Float64], base: Float64) -> Float64:
    for i in range(N):
        out[i] = base + Float64(i)
    var acc: Float64 = 0.0
    for j in range(N):
        acc += out[j]
    return acc


def main():
    var a = List[Float64](capacity=N)
    var b = List[Float64](capacity=N)
    var ints = List[Int64](capacity=N)
    for i in range(N):
        a.append(0.001 * Float64(i))
        b.append(1.0 / Float64(1 + i))
        ints.append(Int64(i % 3))
    var room = List[Int64](length=N, fill=0)
    var floats = List[Float64](length=N, fill=0.0)
    print(squares(room), round(dot(a, b), 6), round(dot_relaxed(a, b), 3), count_odd(ints, 3), fill(floats, 0.5))
    var labels = List[String]()
    labels.append("squares")
    labels.append("dot")
    labels.append("dot_relaxed")
    labels.append("count_odd")
    labels.append("fill")
    for k in range(5):
        var best: Float64 = 1e18
        for _ in range(6):
            var started = perf_counter_ns()
            if k == 0:
                _ = squares(room)
            elif k == 1:
                _ = dot(a, b)
            elif k == 2:
                _ = dot_relaxed(a, b)
            elif k == 3:
                _ = count_odd(ints, 3)
            else:
                _ = fill(floats, 0.5)
            var elapsed = Float64(perf_counter_ns() - started) / 1e6
            if elapsed < best:
                best = elapsed
        print("# " + labels[k] + ": " + String(best) + " ms")
ranges_numba.py (Python)
"""The four kernels with Numba: `prange` under `parallel=True`; timed warm."""

import time

import numba
import numpy as np
from numba import njit, prange

N = 8_000_000
# Pinned to the performance cores, as the PyTorch counterparts are: the
# default pool over every core makes the small rows unstable on a hybrid CPU.
numba.set_num_threads(min(8, numba.config.NUMBA_NUM_THREADS))


@njit(parallel=True)
def squares(out):
    for i in prange(len(out)):
        out[i] = i * i
    return out[len(out) - 1]


@njit(parallel=True)
def dot(a, b):
    total = 0.0
    for i in prange(len(a)):
        total += a[i] * b[i]
    return total


@njit(parallel=True, fastmath=True)
def dot_relaxed(a, b):
    total = 0.0
    for i in prange(len(a)):
        total += a[i] * b[i]
    return total


@njit(parallel=True)
def count_odd(xs, scale):
    hits = 0
    for i in prange(len(xs)):
        if xs[i] % 2 == 1:
            hits += scale
    return hits


@njit
def fill(out, base):
    for i in range(len(out)):
        out[i] = base + i
    acc = 0.0
    for j in range(len(out)):
        acc += out[j]
    return acc


def timed(label, run, repeats=5):
    run()
    best = 1e9
    for _ in range(repeats):
        started = time.perf_counter()
        run()
        best = min(best, (time.perf_counter() - started) * 1000.0)
    print(f"# {label}: {best:.2f} ms")


def main():
    i = np.arange(N, dtype=np.float64)
    a = 0.001 * i
    b = 1.0 / (1 + i)
    ints = (np.arange(N, dtype=np.int64) % 3).astype(np.int64)
    room = np.empty(N, dtype=np.int64)
    floats = np.empty(N, dtype=np.float64)
    print(squares(room), round(dot(a, b), 6), round(dot_relaxed(a, b), 3), count_odd(ints, 3), fill(floats, 0.5))
    timed("squares", lambda: squares(room))
    # A `prange` sum reassociates on its own, so there is no ordered dot here to time.
    timed("dot_relaxed", lambda: dot_relaxed(a, b))
    timed("count_odd", lambda: count_odd(ints, 3))
    timed("fill", lambda: fill(floats, 0.5))


main()
ranges_numpy.py (Python)
"""The four kernels as NumPy array expressions: no loop to write, a temporary per step."""

import os
import time

# BLAS threads pinned to the performance cores: on a hybrid CPU the default
# pool lands `dot` on the efficiency cores now and then, which doubles a
# three-millisecond row and makes the run unstable.
os.environ.setdefault("OPENBLAS_NUM_THREADS", "8")
os.environ.setdefault("OMP_NUM_THREADS", "8")

import numpy as np  # noqa: E402

N = 8_000_000


def squares(out):
    out[:] = np.arange(len(out), dtype=np.int64) ** 2
    return int(out[-1])


def dot(a, b):
    return float(np.dot(a, b))


def count_odd(xs, scale):
    return int(np.count_nonzero(xs % 2 == 1)) * scale


def fill(out, base):
    out[:] = base + np.arange(len(out), dtype=np.float64)
    return float(out.sum())


def timed(label, run, repeats=5):
    run()
    best = 1e9
    for _ in range(repeats):
        started = time.perf_counter()
        run()
        best = min(best, (time.perf_counter() - started) * 1000.0)
    print(f"# {label}: {best:.2f} ms")


def main():
    i = np.arange(N, dtype=np.float64)
    a = 0.001 * i
    b = 1.0 / (1 + i)
    ints = (np.arange(N, dtype=np.int64) % 3).astype(np.int64)
    room = np.empty(N, dtype=np.int64)
    floats = np.empty(N, dtype=np.float64)
    print(squares(room), round(dot(a, b), 6), round(dot(a, b), 3), count_odd(ints, 3), fill(floats, 0.5))
    timed("squares", lambda: squares(room))
    timed("dot_relaxed", lambda: dot(a, b))
    timed("count_odd", lambda: count_odd(ints, 3))
    timed("fill", lambda: fill(floats, 0.5))


main()
ranges_taichi.py (Python)
"""The four kernels with Taichi: kernels over fields, the outermost loop parallel; timed warm."""

import time

import numpy as np
import contextlib
import sys

with contextlib.redirect_stdout(sys.stderr):  # the banners are not answers
    import taichi as ti

    ti.init(arch=ti.cpu, default_ip=ti.i64, default_fp=ti.f64)

N = 8_000_000
a_f = ti.field(ti.f64, shape=N)
b_f = ti.field(ti.f64, shape=N)
ints = ti.field(ti.i64, shape=N)
room = ti.field(ti.i64, shape=N)
floats = ti.field(ti.f64, shape=N)


@ti.kernel
def squares() -> ti.i64:
    for i in range(N):
        room[i] = ti.i64(i) * ti.i64(i)
    return room[N - 1]


@ti.kernel
def dot() -> ti.f64:
    total = 0.0
    for i in range(N):
        total += a_f[i] * b_f[i]
    return total


@ti.kernel
def count_odd(scale: ti.i64) -> ti.i64:
    hits = 0
    for i in range(N):
        if ints[i] % 2 == 1:
            hits += scale
    return hits


@ti.kernel
def fill(base: ti.f64) -> ti.f64:
    for i in range(N):
        floats[i] = base + i
    acc = 0.0
    ti.loop_config(serialize=True)
    for j in range(N):
        acc += floats[j]
    return acc


def timed(label, run, repeats=5):
    run()
    ti.sync()
    best = 1e9
    for _ in range(repeats):
        started = time.perf_counter()
        run()
        ti.sync()
        best = min(best, (time.perf_counter() - started) * 1000.0)
    print(f"# {label}: {best:.2f} ms")


def main():
    i = np.arange(N, dtype=np.float64)
    a_f.from_numpy(0.001 * i)
    b_f.from_numpy(1.0 / (1 + i))
    ints.from_numpy((np.arange(N, dtype=np.int64) % 3).astype(np.int64))
    print(squares(), round(dot(), 6), round(dot(), 3), count_odd(3), fill(0.5))
    timed("squares", squares)
    timed("dot_relaxed", dot)
    timed("count_odd", lambda: count_odd(3))
    timed("fill", lambda: fill(0.5))


main()

Source: examples/35_parallel_range.