carray 3.0.1 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +520 -0
- data/README.md +2 -2
- data/carray.gemspec +1 -1
- data/ext/ca_axis_dispatch.c +33 -4
- data/ext/ca_axis_group.c +202 -96
- data/ext/ca_categorical_iterator.c +108 -54
- data/ext/ca_kernel_iterator.c +317 -51
- data/ext/ca_kernel_iterator.h +142 -35
- data/ext/ca_obj_array.c +62 -20
- data/ext/ca_obj_block.c +4 -4
- data/ext/ca_obj_const_string.c +85 -26
- data/ext/ca_obj_face.c +24 -0
- data/ext/ca_obj_face.h +15 -0
- data/ext/ca_obj_fixlen_string.c +18 -5
- data/ext/ca_obj_meld.c +123 -25
- data/ext/ca_obj_object.c +8 -0
- data/ext/ca_obj_select.c +49 -34
- data/ext/ca_obj_stack.c +3 -8
- data/ext/ca_obj_stride.c +72 -1
- data/ext/ca_obj_string.c +8 -4
- data/ext/ca_obj_window.c +8 -2
- data/ext/ca_op_ipower.c +1 -2
- data/ext/ca_rng_normal.h +42 -0
- data/ext/ca_rng_xoshiro256pp.h +105 -0
- data/ext/ca_sweep_engine.c +307 -143
- data/ext/ca_sweep_engine.h +26 -5
- data/ext/carray.h +21 -2
- data/ext/carray_access.c +32 -20
- data/ext/carray_address_basis.c +590 -0
- data/ext/carray_broadcast.c +3 -3
- data/ext/carray_call_cfunc.c +667 -483
- data/ext/carray_cast.c +115 -41
- data/ext/carray_copy.c +55 -30
- data/ext/carray_core.c +83 -3
- data/ext/carray_count.c +9 -10
- data/ext/carray_factorize.c +46 -25
- data/ext/carray_internal.h +17 -0
- data/ext/carray_kernels_reduce_aggregate.c +168 -0
- data/ext/carray_kernels_reduce_cumulative.c +270 -1
- data/ext/carray_kernels_reduce_extreme.c +554 -8
- data/ext/carray_kernels_scan.c +4 -4
- data/ext/carray_kernels_search.c +94 -14
- data/ext/carray_loop.c +7 -1
- data/ext/carray_mask.c +23 -8
- data/ext/carray_median_percentile.c +55 -0
- data/ext/carray_operator.c +4 -4
- data/ext/carray_order.c +1 -1
- data/ext/carray_random.c +384 -40
- data/ext/carray_slab.c +13 -0
- data/ext/carray_sort.c +20 -22
- data/ext/mk_call_cfunc.rb +103 -116
- data/ext/mkkernel.rb +297 -29
- data/ext/ruby_carray.c +10 -1
- data/ext/version.h +4 -4
- data/lib/carray/autoload_carray.rb +5 -3
- data/lib/carray/autoload_method_extension.rb +12 -0
- data/lib/carray/axis_group.rb +77 -0
- data/lib/carray/basics.rb +4 -0
- data/lib/carray/block_iterator.rb +92 -16
- data/lib/carray/categorical.rb +150 -33
- data/lib/carray/categorical_iterator.rb +207 -80
- data/lib/carray/const_string.rb +131 -27
- data/lib/carray/construct.rb +40 -0
- data/lib/carray/data_type_extension.rb +3 -0
- data/lib/carray/data_type_limits.rb +91 -0
- data/lib/carray/fixlen_string.rb +1 -1
- data/lib/carray/frame/csv_parser.rb +11 -4
- data/lib/carray/frame/frame.rb +81 -10
- data/lib/carray/frame/group.rb +36 -3
- data/lib/carray/frame/io.rb +67 -15
- data/lib/carray/frame/records.rb +18 -4
- data/lib/carray/frame/verbs.rb +14 -11
- data/lib/carray/inspect.rb +42 -9
- data/lib/carray/iterator.rb +143 -0
- data/lib/carray/lazy.rb +0 -37
- data/lib/carray/mask_gap_fill.rb +3 -1
- data/lib/carray/methods/discovery_along.rb +74 -0
- data/lib/carray/methods/factorize.rb +50 -0
- data/lib/carray/methods/is_in.rb +13 -2
- data/lib/carray/methods/locate_addr.rb +75 -2
- data/lib/carray/methods/mask_duplicates.rb +35 -1
- data/lib/carray/methods/nunique.rb +22 -1
- data/lib/carray/methods/repeat.rb +110 -0
- data/lib/carray/methods/unique.rb +41 -1
- data/lib/carray/rng.rb +86 -0
- data/lib/carray/slab_iterator.rb +58 -13
- data/lib/carray/string_operation_extension.rb +5 -1
- data/lib/carray/time.rb +18 -2
- data/lib/carray/window_iterator.rb +142 -20
- data/lib/carray.rb +2 -0
- data/yard-stubs/ca_obj_block.rb +2 -7
- data/yard-stubs/ca_obj_window.rb +10 -2
- data/yard-stubs/carray_access.rb +1 -1
- data/yard-stubs/carray_broadcast.rb +1 -1
- data/yard-stubs/carray_core.rb +0 -80
- data/yard-stubs/carray_count.rb +7 -2
- data/yard-stubs/carray_lazy.rb +205 -0
- data/yard-stubs/carray_math.rb +1486 -3
- data/yard-stubs/carray_median_percentile.rb +16 -2
- data/yard-stubs/carray_order.rb +9 -69
- data/yard-stubs/carray_slab.rb +9 -7
- data/yard-stubs/carray_sort.rb +7 -5
- metadata +9 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
|
|
3
|
+
ca_rng_xoshiro256pp.h -- the xoshiro256++ generator, as text
|
|
4
|
+
|
|
5
|
+
This file is not a header in the usual sense. It carries no include
|
|
6
|
+
guard and includes nothing, because it is read two ways:
|
|
7
|
+
|
|
8
|
+
- ext/carray_random.c #includes it, and the extension compiles it.
|
|
9
|
+
That is what `CArray#random!(rng: r)` runs.
|
|
10
|
+
|
|
11
|
+
- CArray::Rng::SOURCE[:xoshiro256pp] is this file's text, read at
|
|
12
|
+
runtime. carray-jit pastes it into the translation unit it
|
|
13
|
+
generates for a kernel, beside its own `static inline` helpers.
|
|
14
|
+
That is what a kernel's `random(rng:)` runs.
|
|
15
|
+
|
|
16
|
+
One text, so the two cannot drift: a kernel that draws after
|
|
17
|
+
`random!` continues the same sequence because it is running the same
|
|
18
|
+
code, not because two implementations were checked against each
|
|
19
|
+
other. Anything added here has to stay pasteable -- `static inline`,
|
|
20
|
+
no directives, and nothing beyond <stdint.h>, which both sides have.
|
|
21
|
+
|
|
22
|
+
xoshiro256++ 1.0 by David Blackman and Sebastiano Vigna, released to
|
|
23
|
+
the public domain (https://prng.di.unimi.it/xoshiro256plusplus.c).
|
|
24
|
+
The state is seeded through splitmix64, as its authors prescribe.
|
|
25
|
+
|
|
26
|
+
The state is held as int64_t[4] rather than uint64_t[4] so that it is
|
|
27
|
+
a plain CA_INT64 array a caller can look at. C says an object may be
|
|
28
|
+
read through the corresponding signed or unsigned type, so the cast
|
|
29
|
+
below is the language's own allowance and not a reinterpretation.
|
|
30
|
+
|
|
31
|
+
--------------------------------------------------------------------------- */
|
|
32
|
+
|
|
33
|
+
/* splitmix64: the state seeder. A single 64-bit seed is a poor state
|
|
34
|
+
for xoshiro on its own -- an all-but-zero state takes many draws to
|
|
35
|
+
escape -- so the seed is stretched through a generator whose output
|
|
36
|
+
is well mixed from the first call. */
|
|
37
|
+
static inline uint64_t
|
|
38
|
+
ca_splitmix64_next (uint64_t *x)
|
|
39
|
+
{
|
|
40
|
+
uint64_t z = (*x += 0x9E3779B97F4A7C15ULL);
|
|
41
|
+
z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ULL;
|
|
42
|
+
z = (z ^ (z >> 27)) * 0x94D049BB133111EBULL;
|
|
43
|
+
return z ^ (z >> 31);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Fill a state from one seed. */
|
|
47
|
+
static inline void
|
|
48
|
+
ca_xoshiro256pp_seed (int64_t *s, uint64_t seed)
|
|
49
|
+
{
|
|
50
|
+
uint64_t *u = (uint64_t *) s;
|
|
51
|
+
uint64_t x = seed;
|
|
52
|
+
u[0] = ca_splitmix64_next(&x);
|
|
53
|
+
u[1] = ca_splitmix64_next(&x);
|
|
54
|
+
u[2] = ca_splitmix64_next(&x);
|
|
55
|
+
u[3] = ca_splitmix64_next(&x);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static inline uint64_t
|
|
59
|
+
ca_xoshiro256pp_rotl (uint64_t x, int k)
|
|
60
|
+
{
|
|
61
|
+
return (x << k) | (x >> (64 - k));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* One draw: 64 random bits, the state advanced. */
|
|
65
|
+
static inline uint64_t
|
|
66
|
+
ca_xoshiro256pp_next (int64_t *s)
|
|
67
|
+
{
|
|
68
|
+
uint64_t *u = (uint64_t *) s;
|
|
69
|
+
const uint64_t result = ca_xoshiro256pp_rotl(u[0] + u[3], 23) + u[0];
|
|
70
|
+
const uint64_t t = u[1] << 17;
|
|
71
|
+
|
|
72
|
+
u[2] ^= u[0];
|
|
73
|
+
u[3] ^= u[1];
|
|
74
|
+
u[1] ^= u[2];
|
|
75
|
+
u[0] ^= u[3];
|
|
76
|
+
u[2] ^= t;
|
|
77
|
+
u[3] = ca_xoshiro256pp_rotl(u[3], 45);
|
|
78
|
+
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* A double in [0, 1). The top 53 bits are the ones taken: that is the
|
|
83
|
+
whole mantissa, so no two draws collide merely because the generator
|
|
84
|
+
handed back fewer bits than a double can hold. */
|
|
85
|
+
static inline double
|
|
86
|
+
ca_xoshiro256pp_next_real (int64_t *s)
|
|
87
|
+
{
|
|
88
|
+
return (double) (ca_xoshiro256pp_next(s) >> 11) * 0x1.0p-53;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* One standard normal, which is two draws. ca_rng_normal is what turns
|
|
92
|
+
them into one, and it is in a file of its own because it belongs to no
|
|
93
|
+
generator.
|
|
94
|
+
|
|
95
|
+
The two draws are taken into locals rather than written as two
|
|
96
|
+
arguments: C does not say which order a call's arguments are
|
|
97
|
+
evaluated in, and these two are not interchangeable -- they advance a
|
|
98
|
+
state. */
|
|
99
|
+
static inline double
|
|
100
|
+
ca_xoshiro256pp_next_normal (int64_t *s)
|
|
101
|
+
{
|
|
102
|
+
const double u1 = ca_xoshiro256pp_next_real(s);
|
|
103
|
+
const double u2 = ca_xoshiro256pp_next_real(s);
|
|
104
|
+
return ca_rng_normal(u1, u2);
|
|
105
|
+
}
|
data/ext/ca_sweep_engine.c
CHANGED
|
@@ -22,28 +22,138 @@
|
|
|
22
22
|
#include "ca_for_buffer.h"
|
|
23
23
|
#include <string.h>
|
|
24
24
|
|
|
25
|
+
/* "[2, 3]" for an operand's shape. */
|
|
26
|
+
static VALUE
|
|
27
|
+
ca_sweep_shape_str (CArray *ca)
|
|
28
|
+
{
|
|
29
|
+
volatile VALUE s = rb_str_new_cstr("[");
|
|
30
|
+
int k;
|
|
31
|
+
for (k = 0; k < ca->ndim; k++) {
|
|
32
|
+
if (k > 0) rb_str_cat_cstr(s, ", ");
|
|
33
|
+
rb_str_catf(s, "%lld", (long long) ca->dim[k]);
|
|
34
|
+
}
|
|
35
|
+
rb_str_cat_cstr(s, "]");
|
|
36
|
+
return s;
|
|
37
|
+
}
|
|
38
|
+
|
|
25
39
|
void
|
|
26
|
-
|
|
40
|
+
ca_sweep_refuse_shapes (CArray *a, CArray *b)
|
|
27
41
|
{
|
|
28
|
-
|
|
29
|
-
|
|
42
|
+
volatile VALUE sa = ca_sweep_shape_str(a);
|
|
43
|
+
volatile VALUE sb = ca_sweep_shape_str(b);
|
|
44
|
+
rb_raise(rb_eArgError,
|
|
45
|
+
"shape mismatch between operands (%s and %s); a scalar pairs "
|
|
46
|
+
"with any array, two arrays only when their shapes agree",
|
|
47
|
+
StringValueCStr(sa), StringValueCStr(sb));
|
|
48
|
+
}
|
|
30
49
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
50
|
+
int
|
|
51
|
+
ca_sweep_same_shape (CArray *a, CArray *b)
|
|
52
|
+
{
|
|
53
|
+
int k;
|
|
54
|
+
if (a->ndim != b->ndim) return 0;
|
|
55
|
+
for (k = 0; k < a->ndim; k++) {
|
|
56
|
+
if (a->dim[k] != b->dim[k]) return 0;
|
|
36
57
|
}
|
|
58
|
+
return 1;
|
|
59
|
+
}
|
|
37
60
|
|
|
61
|
+
/* Pair the operands: a scalar walks with stride 0, every other operand
|
|
62
|
+
* must have the shape of the first non-scalar one, which is returned (NULL
|
|
63
|
+
* when all operands are scalars). Sets stride[] and n_kernel. Allocates
|
|
64
|
+
* nothing, so it runs before any operand is acquired. */
|
|
65
|
+
static CArray *
|
|
66
|
+
ca_sweep_pair_operands (ca_sweep_state_t *st)
|
|
67
|
+
{
|
|
68
|
+
CArray *donor = NULL;
|
|
69
|
+
int k_op;
|
|
70
|
+
st->n_kernel = 1;
|
|
38
71
|
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
39
|
-
st->
|
|
72
|
+
CArray *ca = st->cx[k_op];
|
|
73
|
+
if (ca_is_scalar(ca)) {
|
|
74
|
+
st->stride[k_op] = 0;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
st->stride[k_op] = ca->bytes;
|
|
78
|
+
if (!donor) {
|
|
79
|
+
donor = ca;
|
|
80
|
+
st->n_kernel = ca->elements;
|
|
81
|
+
} else if (!ca_sweep_same_shape(donor, ca)) {
|
|
82
|
+
ca_sweep_refuse_shapes(donor, ca);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return donor;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Non-zero when any INPUT operand carries a mask. Asks, allocates
|
|
89
|
+
* nothing. */
|
|
90
|
+
static int
|
|
91
|
+
ca_sweep_any_input_mask (ca_sweep_state_t *st)
|
|
92
|
+
{
|
|
93
|
+
int k_op;
|
|
94
|
+
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
95
|
+
if (st->fsync[k_op] == '0' && ca_has_mask(st->cx[k_op])) return 1;
|
|
96
|
+
}
|
|
97
|
+
return 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
NORETURN(static void ca_sweep_refuse_masked_input (ca_sweep_state_t *st,
|
|
101
|
+
const char *fallback));
|
|
102
|
+
|
|
103
|
+
static void
|
|
104
|
+
ca_sweep_refuse_masked_input (ca_sweep_state_t *st, const char *fallback)
|
|
105
|
+
{
|
|
106
|
+
rb_raise(rb_eRuntimeError,
|
|
107
|
+
"%s: masked INPUT not allowed in NO_MASK form "
|
|
108
|
+
"(use the *_MASKED form to handle masked cells explicitly)",
|
|
109
|
+
st->src_label ? st->src_label : fallback);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Give back everything acquire has taken so far, without syncing: detach
|
|
113
|
+
* attached operands, free owned scratch (xmalloc on the whole-buffer path,
|
|
114
|
+
* arena on the chunked one) and the mask buffers. Called when a read
|
|
115
|
+
* raises part way through, so that the raise leaves nothing attached or
|
|
116
|
+
* allocated behind it. */
|
|
117
|
+
static void
|
|
118
|
+
ca_sweep_abort (ca_sweep_state_t *st, int chunked)
|
|
119
|
+
{
|
|
120
|
+
int k_op;
|
|
121
|
+
for (k_op = st->n_ops - 1; k_op >= 0; k_op--) {
|
|
122
|
+
if (st->attached[k_op]) {
|
|
123
|
+
st->attached[k_op] = 0;
|
|
124
|
+
ca_detach(st->cx[k_op]);
|
|
125
|
+
} else if (st->owned_buf[k_op]) {
|
|
126
|
+
if (chunked) ca_lazy_arena_release(st->owned_buf[k_op]);
|
|
127
|
+
else xfree(st->owned_buf[k_op]);
|
|
128
|
+
}
|
|
40
129
|
st->owned_buf[k_op] = NULL;
|
|
41
|
-
st->attached[k_op] = 0;
|
|
42
130
|
}
|
|
43
|
-
st->m0
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
131
|
+
if (st->m0) {
|
|
132
|
+
if (chunked) ca_lazy_arena_release(st->m0);
|
|
133
|
+
else xfree(st->m0);
|
|
134
|
+
st->m0 = NULL;
|
|
135
|
+
}
|
|
136
|
+
if (chunked && st->mask_scratch) {
|
|
137
|
+
ca_lazy_arena_release(st->mask_scratch);
|
|
138
|
+
st->mask_scratch = NULL;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
typedef struct {
|
|
143
|
+
ca_sweep_state_t *st;
|
|
144
|
+
int any_input_mask;
|
|
145
|
+
boolean8_t *ms; /* per-operand mask staging, whole path */
|
|
146
|
+
} ca_sweep_acquire_ctx_t;
|
|
147
|
+
|
|
148
|
+
/* The part of acquire that takes resources and reads operands, and so can
|
|
149
|
+
* raise holding them. Runs under rb_protect; every buffer is recorded in
|
|
150
|
+
* the state (or the ctx) before the read that may raise. */
|
|
151
|
+
static VALUE
|
|
152
|
+
ca_sweep_acquire_body (VALUE arg)
|
|
153
|
+
{
|
|
154
|
+
ca_sweep_acquire_ctx_t *ctx = (ca_sweep_acquire_ctx_t *) arg;
|
|
155
|
+
ca_sweep_state_t *st = ctx->st;
|
|
156
|
+
int k_op;
|
|
47
157
|
|
|
48
158
|
/* Per-operand acquire:
|
|
49
159
|
* OUTPUT (fsync == '1') -> ca_attach + base = ca->ptr
|
|
@@ -53,14 +163,10 @@ ca_sweep_acquire (ca_sweep_state_t *st)
|
|
|
53
163
|
* copy carries the values into the kernel. */
|
|
54
164
|
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
55
165
|
CArray *ca = st->cx[k_op];
|
|
56
|
-
if (st->fsync[k_op] == '1') {
|
|
166
|
+
if (st->fsync[k_op] == '1' || ca_attach_is_alias(ca)) {
|
|
57
167
|
ca_attach(ca);
|
|
58
|
-
st->base[k_op] = (char *) ca->ptr;
|
|
59
168
|
st->attached[k_op] = 1;
|
|
60
|
-
} else if (ca_attach_is_alias(ca)) {
|
|
61
|
-
ca_attach(ca);
|
|
62
169
|
st->base[k_op] = (char *) ca->ptr;
|
|
63
|
-
st->attached[k_op] = 1;
|
|
64
170
|
} else {
|
|
65
171
|
ca_size_t bytes_total = ca->elements * ca->bytes;
|
|
66
172
|
st->owned_buf[k_op] = xmalloc(bytes_total);
|
|
@@ -69,36 +175,9 @@ ca_sweep_acquire (ca_sweep_state_t *st)
|
|
|
69
175
|
}
|
|
70
176
|
}
|
|
71
177
|
|
|
72
|
-
/* compute n_kernel (= broadcast shape) and per-cell strides */
|
|
73
|
-
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
74
|
-
CArray *ca = st->cx[k_op];
|
|
75
|
-
if (ca_is_scalar(ca)) {
|
|
76
|
-
st->stride[k_op] = 0;
|
|
77
|
-
} else {
|
|
78
|
-
st->stride[k_op] = ca->bytes;
|
|
79
|
-
if (st->n_kernel == 1) {
|
|
80
|
-
st->n_kernel = ca->elements;
|
|
81
|
-
} else if (ca->elements != st->n_kernel) {
|
|
82
|
-
rb_raise(rb_eRuntimeError, "data size mismatch in operation");
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
178
|
/* iter mask m0 = OR of INPUT operand masks, gathered via ca_xfer_all
|
|
88
179
|
so no operand mask attach happens. Stays NULL if no INPUT masks. */
|
|
89
|
-
|
|
90
|
-
if (st->fsync[k_op] == '0' && ca_has_mask(st->cx[k_op])) {
|
|
91
|
-
any_input_mask = 1;
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (any_input_mask && st->no_mask) {
|
|
96
|
-
rb_raise(rb_eRuntimeError,
|
|
97
|
-
"%s: masked INPUT not allowed in NO_MASK form "
|
|
98
|
-
"(use the *_MASKED form to handle masked cells explicitly)",
|
|
99
|
-
st->src_label ? st->src_label : "ca_sweep_acquire");
|
|
100
|
-
}
|
|
101
|
-
if (any_input_mask) {
|
|
180
|
+
if (ctx->any_input_mask) {
|
|
102
181
|
st->m0 = xmalloc(st->n_kernel);
|
|
103
182
|
memset(st->m0, 0, st->n_kernel);
|
|
104
183
|
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
@@ -111,11 +190,10 @@ ca_sweep_acquire (ca_sweep_state_t *st)
|
|
|
111
190
|
ca_xfer_all(ca->mask, &bit, CA_XFER_GET);
|
|
112
191
|
if (bit) memset(st->m0, 1, st->n_kernel);
|
|
113
192
|
} else {
|
|
114
|
-
boolean8_t *ms = xmalloc(st->n_kernel);
|
|
115
193
|
ca_size_t j;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
194
|
+
if (!ctx->ms) ctx->ms = xmalloc(st->n_kernel);
|
|
195
|
+
ca_xfer_all(ca->mask, ctx->ms, CA_XFER_GET);
|
|
196
|
+
for (j = 0; j < st->n_kernel; j++) st->m0[j] |= ctx->ms[j];
|
|
119
197
|
}
|
|
120
198
|
}
|
|
121
199
|
}
|
|
@@ -131,6 +209,75 @@ ca_sweep_acquire (ca_sweep_state_t *st)
|
|
|
131
209
|
memcpy(ca->mask->ptr, st->m0, st->n_kernel);
|
|
132
210
|
}
|
|
133
211
|
}
|
|
212
|
+
return Qnil;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
void
|
|
216
|
+
ca_sweep_acquire (ca_sweep_state_t *st)
|
|
217
|
+
{
|
|
218
|
+
ca_sweep_acquire_ctx_t ctx;
|
|
219
|
+
int k_op;
|
|
220
|
+
int tag = 0;
|
|
221
|
+
|
|
222
|
+
if ((int) strlen(st->fsync) != st->n_ops) {
|
|
223
|
+
rb_raise(rb_eRuntimeError,
|
|
224
|
+
"[BUG] invalid length of fsync arg in %s (expected %d)",
|
|
225
|
+
st->src_label ? st->src_label : "ca_sweep_acquire",
|
|
226
|
+
st->n_ops);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
230
|
+
st->base[k_op] = NULL;
|
|
231
|
+
st->owned_buf[k_op] = NULL;
|
|
232
|
+
st->attached[k_op] = 0;
|
|
233
|
+
}
|
|
234
|
+
st->m0 = NULL;
|
|
235
|
+
/* CAREFUL: do not reset st->no_mask here — caller sets it before
|
|
236
|
+
* acquire and the NO_MASK guard below consumes it. */
|
|
237
|
+
|
|
238
|
+
/* Refusals come first, while nothing is held. */
|
|
239
|
+
ca_sweep_pair_operands(st);
|
|
240
|
+
ctx.st = st;
|
|
241
|
+
ctx.ms = NULL;
|
|
242
|
+
ctx.any_input_mask = ca_sweep_any_input_mask(st);
|
|
243
|
+
if (ctx.any_input_mask && st->no_mask) {
|
|
244
|
+
ca_sweep_refuse_masked_input(st, "ca_sweep_acquire");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
rb_protect(ca_sweep_acquire_body, (VALUE) &ctx, &tag);
|
|
248
|
+
if (ctx.ms) xfree(ctx.ms);
|
|
249
|
+
if (tag) {
|
|
250
|
+
ca_sweep_abort(st, 0);
|
|
251
|
+
rb_jump_tag(tag);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
void
|
|
256
|
+
ca_sweep_run (ca_sweep_state_t *st, VALUE (*walk)(VALUE), VALUE arg)
|
|
257
|
+
{
|
|
258
|
+
int tag = 0;
|
|
259
|
+
rb_protect(walk, arg, &tag);
|
|
260
|
+
if (tag) {
|
|
261
|
+
ca_sweep_abort(st, 0);
|
|
262
|
+
rb_jump_tag(tag);
|
|
263
|
+
}
|
|
264
|
+
ca_sweep_release(st);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
void
|
|
268
|
+
ca_sweep_run_chunked (ca_sweep_state_t *st, VALUE (*walk)(VALUE), VALUE arg)
|
|
269
|
+
{
|
|
270
|
+
int tag = 0;
|
|
271
|
+
rb_protect(walk, arg, &tag);
|
|
272
|
+
if (tag) {
|
|
273
|
+
/* a raise inside ca_sweep_next_chunk has already given back */
|
|
274
|
+
if (st->chunked_state != 3) {
|
|
275
|
+
ca_sweep_abort(st, 1);
|
|
276
|
+
st->chunked_state = 3;
|
|
277
|
+
}
|
|
278
|
+
rb_jump_tag(tag);
|
|
279
|
+
}
|
|
280
|
+
ca_sweep_release_chunked(st);
|
|
134
281
|
}
|
|
135
282
|
|
|
136
283
|
void
|
|
@@ -158,65 +305,12 @@ ca_sweep_release (ca_sweep_state_t *st)
|
|
|
158
305
|
|
|
159
306
|
/* ===== Chunked path implementation ===== */
|
|
160
307
|
|
|
161
|
-
|
|
162
|
-
|
|
308
|
+
static VALUE
|
|
309
|
+
ca_sweep_acquire_chunked_body (VALUE arg)
|
|
163
310
|
{
|
|
311
|
+
ca_sweep_acquire_ctx_t *ctx = (ca_sweep_acquire_ctx_t *) arg;
|
|
312
|
+
ca_sweep_state_t *st = ctx->st;
|
|
164
313
|
int k_op;
|
|
165
|
-
int any_input_mask = 0;
|
|
166
|
-
CArray *shape_donor = NULL;
|
|
167
|
-
|
|
168
|
-
if ((int) strlen(st->fsync) != st->n_ops) {
|
|
169
|
-
rb_raise(rb_eRuntimeError,
|
|
170
|
-
"[BUG] invalid length of fsync arg in %s (expected %d)",
|
|
171
|
-
st->src_label ? st->src_label : "ca_sweep_acquire_chunked",
|
|
172
|
-
st->n_ops);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
176
|
-
st->base[k_op] = NULL;
|
|
177
|
-
st->base_orig[k_op] = NULL;
|
|
178
|
-
st->owned_buf[k_op] = NULL;
|
|
179
|
-
st->attached[k_op] = 0;
|
|
180
|
-
}
|
|
181
|
-
st->m0 = NULL;
|
|
182
|
-
st->mask_scratch = NULL;
|
|
183
|
-
st->n_kernel = 1;
|
|
184
|
-
st->chunk_off = 0;
|
|
185
|
-
st->chunk_n = 0;
|
|
186
|
-
st->chunk_n_max = 0;
|
|
187
|
-
st->inner = 1;
|
|
188
|
-
st->chunked_state = 0;
|
|
189
|
-
|
|
190
|
-
/* compute broadcast shape (n_kernel + strides) from operand shapes.
|
|
191
|
-
* scalar operands collapse to stride 0; non-scalar operands must agree
|
|
192
|
-
* on element count. shape_donor is the first non-scalar operand and
|
|
193
|
-
* defines the chunking inner-axis size. */
|
|
194
|
-
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
195
|
-
CArray *ca = st->cx[k_op];
|
|
196
|
-
if (ca_is_scalar(ca)) {
|
|
197
|
-
st->stride[k_op] = 0;
|
|
198
|
-
} else {
|
|
199
|
-
st->stride[k_op] = ca->bytes;
|
|
200
|
-
if (st->n_kernel == 1) {
|
|
201
|
-
st->n_kernel = ca->elements;
|
|
202
|
-
shape_donor = ca;
|
|
203
|
-
} else if (ca->elements != st->n_kernel) {
|
|
204
|
-
rb_raise(rb_eRuntimeError, "data size mismatch in operation");
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/* chunk-size policy: inner = donor's product of dims[1..]; chunk_n_max
|
|
210
|
-
* = compute_n on donor->bytes (= type-dependent 32KB target). */
|
|
211
|
-
if (shape_donor) {
|
|
212
|
-
st->inner = ca_chunk_inner_size(shape_donor);
|
|
213
|
-
st->chunk_n_max = ca_chunk_compute_n(st->n_kernel, st->inner,
|
|
214
|
-
shape_donor->bytes);
|
|
215
|
-
} else {
|
|
216
|
-
/* all-scalar: single 1-cell chunk */
|
|
217
|
-
st->inner = 1;
|
|
218
|
-
st->chunk_n_max = 1;
|
|
219
|
-
}
|
|
220
314
|
|
|
221
315
|
/* per-operand acquire:
|
|
222
316
|
* OUTPUT (fsync == '1') : ca_attach + base_orig = ca->ptr (legitimate)
|
|
@@ -226,14 +320,11 @@ ca_sweep_acquire_chunked (ca_sweep_state_t *st)
|
|
|
226
320
|
*/
|
|
227
321
|
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
228
322
|
CArray *ca = st->cx[k_op];
|
|
229
|
-
if (st->fsync[k_op] == '1')
|
|
323
|
+
if (st->fsync[k_op] == '1' || ca_is_scalar(ca) ||
|
|
324
|
+
ca_attach_is_alias(ca)) {
|
|
230
325
|
ca_attach(ca);
|
|
231
|
-
st->base_orig[k_op] = (char *) ca->ptr;
|
|
232
326
|
st->attached[k_op] = 1;
|
|
233
|
-
} else if (ca_is_scalar(ca) || ca_attach_is_alias(ca)) {
|
|
234
|
-
ca_attach(ca);
|
|
235
327
|
st->base_orig[k_op] = (char *) ca->ptr;
|
|
236
|
-
st->attached[k_op] = 1;
|
|
237
328
|
} else {
|
|
238
329
|
/* non-alias non-scalar INPUT: arena scratch sized for chunk_n_max */
|
|
239
330
|
ca_size_t scratch_bytes = st->chunk_n_max * ca->bytes;
|
|
@@ -251,19 +342,7 @@ ca_sweep_acquire_chunked (ca_sweep_state_t *st)
|
|
|
251
342
|
* scaling with the operand, which is the thing this path exists to avoid:
|
|
252
343
|
* at 1 byte per cell it is an eighth of an f64 operand, but an eighth of
|
|
253
344
|
* unbounded is still unbounded. */
|
|
254
|
-
|
|
255
|
-
if (st->fsync[k_op] == '0' && ca_has_mask(st->cx[k_op])) {
|
|
256
|
-
any_input_mask = 1;
|
|
257
|
-
break;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
if (any_input_mask && st->no_mask) {
|
|
261
|
-
rb_raise(rb_eRuntimeError,
|
|
262
|
-
"%s: masked INPUT not allowed in NO_MASK form "
|
|
263
|
-
"(use the *_MASKED form to handle masked cells explicitly)",
|
|
264
|
-
st->src_label ? st->src_label : "ca_sweep_acquire_chunked");
|
|
265
|
-
}
|
|
266
|
-
if (any_input_mask) {
|
|
345
|
+
if (ctx->any_input_mask) {
|
|
267
346
|
st->m0 = (boolean8_t *) ca_lazy_arena_acquire(st->chunk_n_max);
|
|
268
347
|
st->mask_scratch = (boolean8_t *) ca_lazy_arena_acquire(st->chunk_n_max);
|
|
269
348
|
memset(st->m0, 0, st->chunk_n_max);
|
|
@@ -283,6 +362,68 @@ ca_sweep_acquire_chunked (ca_sweep_state_t *st)
|
|
|
283
362
|
* once more in release), never at acquire. Author per-cell m_out writes
|
|
284
363
|
* land in m0 during the chunk loop and must be captured after that loop
|
|
285
364
|
* has run, not before it. */
|
|
365
|
+
return Qnil;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
void
|
|
369
|
+
ca_sweep_acquire_chunked (ca_sweep_state_t *st)
|
|
370
|
+
{
|
|
371
|
+
ca_sweep_acquire_ctx_t ctx;
|
|
372
|
+
int k_op;
|
|
373
|
+
int tag = 0;
|
|
374
|
+
CArray *shape_donor = NULL;
|
|
375
|
+
|
|
376
|
+
if ((int) strlen(st->fsync) != st->n_ops) {
|
|
377
|
+
rb_raise(rb_eRuntimeError,
|
|
378
|
+
"[BUG] invalid length of fsync arg in %s (expected %d)",
|
|
379
|
+
st->src_label ? st->src_label : "ca_sweep_acquire_chunked",
|
|
380
|
+
st->n_ops);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
384
|
+
st->base[k_op] = NULL;
|
|
385
|
+
st->base_orig[k_op] = NULL;
|
|
386
|
+
st->owned_buf[k_op] = NULL;
|
|
387
|
+
st->attached[k_op] = 0;
|
|
388
|
+
}
|
|
389
|
+
st->m0 = NULL;
|
|
390
|
+
st->mask_scratch = NULL;
|
|
391
|
+
st->chunk_off = 0;
|
|
392
|
+
st->chunk_n = 0;
|
|
393
|
+
st->chunk_n_max = 0;
|
|
394
|
+
st->inner = 1;
|
|
395
|
+
st->chunked_state = 0;
|
|
396
|
+
|
|
397
|
+
/* shape_donor is the first non-scalar operand and defines the chunking
|
|
398
|
+
* inner-axis size. */
|
|
399
|
+
shape_donor = ca_sweep_pair_operands(st);
|
|
400
|
+
|
|
401
|
+
/* chunk-size policy: inner = donor's product of dims[1..]; chunk_n_max
|
|
402
|
+
* = compute_n on donor->bytes (= type-dependent 32KB target). */
|
|
403
|
+
if (shape_donor) {
|
|
404
|
+
st->inner = ca_chunk_inner_size(shape_donor);
|
|
405
|
+
st->chunk_n_max = ca_chunk_compute_n(st->n_kernel, st->inner,
|
|
406
|
+
shape_donor->bytes);
|
|
407
|
+
} else {
|
|
408
|
+
/* all-scalar: single 1-cell chunk */
|
|
409
|
+
st->inner = 1;
|
|
410
|
+
st->chunk_n_max = 1;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/* Refusals come first, while nothing is held. */
|
|
414
|
+
ctx.st = st;
|
|
415
|
+
ctx.ms = NULL;
|
|
416
|
+
ctx.any_input_mask = ca_sweep_any_input_mask(st);
|
|
417
|
+
if (ctx.any_input_mask && st->no_mask) {
|
|
418
|
+
ca_sweep_refuse_masked_input(st, "ca_sweep_acquire_chunked");
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
rb_protect(ca_sweep_acquire_chunked_body, (VALUE) &ctx, &tag);
|
|
422
|
+
if (tag) {
|
|
423
|
+
ca_sweep_abort(st, 1);
|
|
424
|
+
st->chunked_state = 3;
|
|
425
|
+
rb_jump_tag(tag);
|
|
426
|
+
}
|
|
286
427
|
}
|
|
287
428
|
|
|
288
429
|
/* OR one INPUT operand's mask for the current chunk into m0. Gathered via
|
|
@@ -329,11 +470,40 @@ ca_sweep_flush_chunk_mask (ca_sweep_state_t *st)
|
|
|
329
470
|
}
|
|
330
471
|
}
|
|
331
472
|
|
|
473
|
+
/* Set up base[] for the chunk at chunk_off / chunk_n: gather each
|
|
474
|
+
non-alias INPUT into its arena scratch, then the chunk's mask. */
|
|
475
|
+
static VALUE
|
|
476
|
+
ca_sweep_next_chunk_body (VALUE arg)
|
|
477
|
+
{
|
|
478
|
+
ca_sweep_state_t *st = (ca_sweep_state_t *) arg;
|
|
479
|
+
ca_size_t off = st->chunk_off;
|
|
480
|
+
ca_size_t n = st->chunk_n;
|
|
481
|
+
int k_op;
|
|
482
|
+
|
|
483
|
+
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
484
|
+
CArray *ca = st->cx[k_op];
|
|
485
|
+
if (st->stride[k_op] == 0) {
|
|
486
|
+
/* scalar: stride 0, base is the single-cell ptr (base_orig) */
|
|
487
|
+
st->base[k_op] = st->base_orig[k_op];
|
|
488
|
+
} else if (st->base_orig[k_op]) {
|
|
489
|
+
/* alias INPUT or OUTPUT: walk through ca->ptr by chunk_off */
|
|
490
|
+
st->base[k_op] = st->base_orig[k_op] + off * st->stride[k_op];
|
|
491
|
+
} else {
|
|
492
|
+
/* non-alias INPUT: per-chunk gather into owned_buf (arena) */
|
|
493
|
+
ca_chunked_gather(ca, off, n, st->owned_buf[k_op]);
|
|
494
|
+
st->base[k_op] = st->owned_buf[k_op];
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (st->m0) ca_sweep_gather_chunk_mask(st, off, n);
|
|
499
|
+
return Qnil;
|
|
500
|
+
}
|
|
501
|
+
|
|
332
502
|
int
|
|
333
503
|
ca_sweep_next_chunk (ca_sweep_state_t *st)
|
|
334
504
|
{
|
|
335
|
-
int k_op;
|
|
336
505
|
ca_size_t off, n;
|
|
506
|
+
int tag = 0;
|
|
337
507
|
|
|
338
508
|
if (st->chunked_state == 0) {
|
|
339
509
|
/* first chunk */
|
|
@@ -356,24 +526,16 @@ ca_sweep_next_chunk (ca_sweep_state_t *st)
|
|
|
356
526
|
if (off + n > st->n_kernel) n = st->n_kernel - off;
|
|
357
527
|
st->chunk_n = n;
|
|
358
528
|
|
|
359
|
-
/*
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
st->base[k_op] = st->base_orig[k_op] + off * st->stride[k_op];
|
|
368
|
-
} else {
|
|
369
|
-
/* non-alias INPUT: per-chunk gather into owned_buf (arena) */
|
|
370
|
-
ca_chunked_gather(ca, off, n, st->owned_buf[k_op]);
|
|
371
|
-
st->base[k_op] = st->owned_buf[k_op];
|
|
372
|
-
}
|
|
529
|
+
/* The gathers read the operands and so can raise; a raise gives back
|
|
530
|
+
the walk's resources before it propagates, since the caller's release
|
|
531
|
+
is never reached. */
|
|
532
|
+
rb_protect(ca_sweep_next_chunk_body, (VALUE) st, &tag);
|
|
533
|
+
if (tag) {
|
|
534
|
+
ca_sweep_abort(st, 1);
|
|
535
|
+
st->chunked_state = 3;
|
|
536
|
+
rb_jump_tag(tag);
|
|
373
537
|
}
|
|
374
538
|
|
|
375
|
-
if (st->m0) ca_sweep_gather_chunk_mask(st, off, n);
|
|
376
|
-
|
|
377
539
|
return 1;
|
|
378
540
|
}
|
|
379
541
|
|
|
@@ -381,6 +543,8 @@ void
|
|
|
381
543
|
ca_sweep_release_chunked (ca_sweep_state_t *st)
|
|
382
544
|
{
|
|
383
545
|
int k_op;
|
|
546
|
+
/* a walk that raised has already given everything back */
|
|
547
|
+
if (st->chunked_state == 3) return;
|
|
384
548
|
/* The final chunk has no next_chunk call to flush it, so it is flushed
|
|
385
549
|
* here. For INOUT_MASKED forms this is what captures the author's
|
|
386
550
|
* per-cell m_out writes over that last chunk. */
|