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¶
What it prints¶
python ranges.ppy, ppy run ranges.ppy
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.
dotprints twelve digits and they are the same on every path. @ppy.fastmathpermits the reassociation, which is whydot_relaxedprints 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:
threadsserialsimdopenmp(forppy 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.