carray 3.0.0 → 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/.yardopts +2 -1
- data/CHANGELOG.md +845 -3
- data/{NEWS.md → CHANGELOG.v1.md} +1 -1
- data/README.md +33 -33
- data/carray.gemspec +12 -8
- data/ext/ca_axis_dispatch.c +33 -4
- data/ext/ca_axis_group.c +256 -114
- data/ext/ca_binop_dispatch.c +5 -6
- data/ext/ca_binop_dispatch.h +0 -7
- data/ext/ca_categorical_iterator.c +121 -67
- data/ext/ca_for_each_element.h +6 -8
- data/ext/ca_group_iter.c +1 -1
- data/ext/ca_kernel_iterator.c +386 -74
- data/ext/ca_kernel_iterator.h +145 -45
- data/ext/ca_obj_array.c +62 -20
- data/ext/ca_obj_bincmp.c +57 -27
- data/ext/ca_obj_binop.c +86 -30
- data/ext/ca_obj_block.c +4 -4
- data/ext/ca_obj_const_string.c +88 -27
- data/ext/ca_obj_face.c +104 -0
- data/ext/ca_obj_face.h +48 -1
- data/ext/ca_obj_fake.c +11 -0
- data/ext/ca_obj_fixlen_string.c +21 -6
- data/ext/ca_obj_grid.c +11 -3
- data/ext/ca_obj_meld.c +125 -22
- data/ext/ca_obj_moncmp.c +33 -14
- data/ext/ca_obj_monop.c +19 -7
- data/ext/ca_obj_object.c +107 -4
- data/ext/ca_obj_record.c +3 -1
- data/ext/ca_obj_refer.c +30 -6
- data/ext/ca_obj_roll.c +13 -5
- data/ext/ca_obj_select.c +49 -34
- data/ext/ca_obj_select_axis.c +16 -6
- data/ext/ca_obj_shift.c +3 -3
- data/ext/ca_obj_stack.c +3 -8
- data/ext/ca_obj_stride.c +178 -24
- data/ext/ca_obj_string.c +11 -5
- data/ext/ca_obj_tile.c +13 -5
- data/ext/ca_obj_time.c +3 -1
- data/ext/ca_obj_timedelta.c +3 -1
- data/ext/ca_obj_transpose.c +1 -1
- data/ext/ca_obj_triop.c +62 -23
- data/ext/ca_obj_window.c +94 -19
- data/ext/ca_op_cmplx64.h +123 -0
- data/ext/ca_op_ipower.c +1 -5
- data/ext/ca_rng_normal.h +42 -0
- data/ext/ca_rng_xoshiro256pp.h +105 -0
- data/ext/ca_sort_kernels.h +5 -5
- data/ext/ca_sweep_engine.c +362 -155
- data/ext/ca_sweep_engine.h +39 -9
- data/ext/ca_transform_common.c +7 -0
- data/ext/carray.h +148 -132
- data/ext/carray_access.c +83 -49
- data/ext/carray_address_basis.c +590 -0
- data/ext/carray_bincount.c +8 -8
- data/ext/carray_broadcast.c +102 -9
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +2010 -335
- data/ext/carray_call_cfunc.h +153 -0
- data/ext/carray_cast.c +184 -72
- data/ext/carray_conversion.c +22 -22
- data/ext/carray_copy.c +67 -32
- data/ext/carray_core.c +140 -13
- data/ext/carray_count.c +10 -11
- data/ext/carray_element.c +7 -7
- data/ext/carray_factorize.c +72 -51
- data/ext/carray_hold.c +1 -1
- data/ext/carray_index_classifier.c +7 -21
- data/ext/carray_internal.h +36 -3
- data/ext/carray_kernels_bincmp.c +1 -0
- data/ext/carray_kernels_binop.c +320 -298
- data/ext/carray_kernels_init.c +1095 -0
- data/ext/carray_kernels_map.c +1 -0
- data/ext/carray_kernels_moncmp.c +1 -0
- data/ext/carray_kernels_monop.c +412 -411
- data/ext/carray_kernels_reduce_aggregate.c +256 -87
- data/ext/carray_kernels_reduce_boolean.c +1 -0
- data/ext/carray_kernels_reduce_cumulative.c +324 -54
- data/ext/carray_kernels_reduce_extreme.c +605 -58
- data/ext/carray_kernels_reduce_variance.c +1 -0
- data/ext/carray_kernels_scan.c +5 -4
- data/ext/carray_kernels_search.c +187 -106
- data/ext/carray_kernels_sort.c +1 -0
- data/ext/carray_kernels_triop.c +1 -0
- data/ext/carray_lazy.c +175 -5
- data/ext/carray_loop.c +7 -1
- data/ext/carray_mask.c +29 -13
- data/ext/carray_median_percentile.c +56 -1
- data/ext/carray_memory_view.c +46 -21
- data/ext/carray_operator.c +50 -53
- data/ext/carray_order.c +1 -1
- data/ext/carray_partition.c +4 -4
- data/ext/carray_random.c +391 -47
- data/ext/carray_scatter.c +1 -1
- data/ext/carray_slab.c +13 -0
- data/ext/carray_sort.c +22 -24
- data/ext/carray_sort_kernel.c +10 -10
- data/ext/carray_test.c +1 -1
- data/ext/extconf.rb +18 -0
- data/ext/mk_call_cfunc.rb +323 -145
- data/ext/mkkernel.rb +651 -129
- data/ext/ruby_carray.c +28 -17
- data/ext/version.h +4 -4
- data/lib/carray/autoload_carray.rb +24 -3
- data/lib/carray/autoload_method_extension.rb +14 -1
- data/lib/carray/axis_group.rb +84 -7
- data/lib/carray/basics.rb +63 -59
- data/lib/carray/bincount_nd.rb +12 -12
- data/lib/carray/block_iterator.rb +111 -25
- data/lib/carray/categorical.rb +154 -37
- data/lib/carray/categorical_iterator.rb +300 -151
- data/lib/carray/conditional.rb +14 -14
- data/lib/carray/const_string.rb +131 -27
- data/lib/carray/construct.rb +61 -0
- data/lib/carray/core_extensions.rb +62 -5
- data/lib/carray/data_type_extension.rb +21 -1
- data/lib/carray/data_type_limits.rb +91 -0
- data/lib/carray/fixlen_string.rb +1 -1
- data/lib/carray/frame/concat.rb +3 -3
- data/lib/carray/frame/convert.rb +1 -1
- data/lib/carray/frame/csv_parser.rb +11 -4
- data/lib/carray/frame/frame.rb +85 -21
- data/lib/carray/frame/group.rb +36 -3
- data/lib/carray/frame/io.rb +224 -15
- data/lib/carray/frame/records.rb +18 -4
- data/lib/carray/frame/sort.rb +1 -1
- data/lib/carray/frame/verbs.rb +32 -12
- data/lib/carray/fuse_source.rb +123 -0
- data/lib/carray/fusion.rb +218 -0
- data/lib/carray/histogram.rb +16 -16
- data/lib/carray/inspect.rb +43 -16
- data/lib/carray/iterator.rb +147 -3
- data/lib/carray/lazy.rb +88 -73
- data/lib/carray/mask_gap_fill.rb +3 -1
- data/lib/carray/meld_reduce.rb +2 -2
- data/lib/carray/methods/align_addr.rb +1 -1
- data/lib/carray/methods/composition.rb +1 -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 +25 -14
- data/lib/carray/methods/locate_addr.rb +81 -3
- data/lib/carray/methods/mask_duplicates.rb +36 -2
- data/lib/carray/methods/meshgrid.rb +4 -5
- data/lib/carray/methods/mode.rb +2 -2
- data/lib/carray/methods/nunique.rb +23 -2
- data/lib/carray/methods/repeat.rb +110 -0
- data/lib/carray/methods/snap.rb +7 -2
- data/lib/carray/methods/unique.rb +44 -4
- data/lib/carray/methods/value_counts.rb +2 -2
- data/lib/carray/rng.rb +86 -0
- data/lib/carray/runtime.rb +0 -19
- data/lib/carray/slab_iterator.rb +77 -19
- data/lib/carray/string_operation_extension.rb +10 -6
- data/lib/carray/time.rb +906 -491
- data/lib/carray/window_iterator.rb +421 -27
- data/lib/carray.rb +7 -5
- data/yard-stubs/ca_obj_array.rb +385 -0
- data/yard-stubs/ca_obj_bitarray.rb +38 -0
- data/yard-stubs/ca_obj_bitfield.rb +43 -0
- data/yard-stubs/ca_obj_block.rb +68 -0
- data/yard-stubs/ca_obj_byte_swap.rb +56 -0
- data/yard-stubs/ca_obj_fake.rb +31 -0
- data/yard-stubs/ca_obj_farray.rb +32 -0
- data/yard-stubs/ca_obj_field.rb +45 -0
- data/yard-stubs/ca_obj_grid.rb +35 -0
- data/yard-stubs/ca_obj_refer.rb +72 -0
- data/yard-stubs/ca_obj_roll.rb +45 -0
- data/yard-stubs/ca_obj_shift.rb +43 -0
- data/yard-stubs/ca_obj_stride.rb +181 -0
- data/yard-stubs/ca_obj_tile.rb +29 -0
- data/yard-stubs/ca_obj_transpose.rb +40 -0
- data/yard-stubs/ca_obj_window.rb +57 -0
- data/yard-stubs/carray_access.rb +131 -0
- data/yard-stubs/carray_attribute.rb +246 -0
- data/yard-stubs/carray_broadcast.rb +37 -0
- data/yard-stubs/carray_cast.rb +489 -0
- data/yard-stubs/carray_class.rb +65 -0
- data/yard-stubs/carray_conversion.rb +76 -0
- data/yard-stubs/carray_copy.rb +79 -0
- data/yard-stubs/carray_core.rb +34 -0
- data/yard-stubs/carray_count.rb +84 -0
- data/yard-stubs/carray_element.rb +108 -0
- data/yard-stubs/carray_generate.rb +66 -0
- data/yard-stubs/carray_lazy.rb +228 -0
- data/yard-stubs/carray_loop.rb +140 -0
- data/yard-stubs/carray_mask.rb +259 -0
- data/yard-stubs/carray_math.rb +1615 -0
- data/yard-stubs/carray_mathfunc.rb +45 -0
- data/yard-stubs/carray_median_percentile.rb +103 -0
- data/yard-stubs/carray_memory_view.rb +163 -0
- data/yard-stubs/carray_order.rb +252 -0
- data/yard-stubs/carray_random.rb +89 -0
- data/yard-stubs/carray_scatter.rb +106 -0
- data/yard-stubs/carray_slab.rb +59 -0
- data/yard-stubs/carray_sort.rb +165 -0
- data/yard-stubs/carray_test.rb +85 -0
- data/yard-stubs/carray_undef.rb +64 -0
- data/yard-stubs/carray_utils.rb +97 -0
- data/yard-stubs/ruby_carray.rb +193 -0
- metadata +67 -10
- data/ext/ca_obj_unbound_repeat.c +0 -496
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,11 +305,72 @@ ca_sweep_release (ca_sweep_state_t *st)
|
|
|
158
305
|
|
|
159
306
|
/* ===== Chunked path implementation ===== */
|
|
160
307
|
|
|
308
|
+
static VALUE
|
|
309
|
+
ca_sweep_acquire_chunked_body (VALUE arg)
|
|
310
|
+
{
|
|
311
|
+
ca_sweep_acquire_ctx_t *ctx = (ca_sweep_acquire_ctx_t *) arg;
|
|
312
|
+
ca_sweep_state_t *st = ctx->st;
|
|
313
|
+
int k_op;
|
|
314
|
+
|
|
315
|
+
/* per-operand acquire:
|
|
316
|
+
* OUTPUT (fsync == '1') : ca_attach + base_orig = ca->ptr (legitimate)
|
|
317
|
+
* INPUT alias : ca_attach + base_orig = ca->ptr (zero-copy)
|
|
318
|
+
* INPUT non-alias non-scalar : arena chunk scratch of chunk_n_max bytes
|
|
319
|
+
* INPUT scalar : ca_attach + base_orig = ca->ptr (1 cell)
|
|
320
|
+
*/
|
|
321
|
+
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
322
|
+
CArray *ca = st->cx[k_op];
|
|
323
|
+
if (st->fsync[k_op] == '1' || ca_is_scalar(ca) ||
|
|
324
|
+
ca_attach_is_alias(ca)) {
|
|
325
|
+
ca_attach(ca);
|
|
326
|
+
st->attached[k_op] = 1;
|
|
327
|
+
st->base_orig[k_op] = (char *) ca->ptr;
|
|
328
|
+
} else {
|
|
329
|
+
/* non-alias non-scalar INPUT: arena scratch sized for chunk_n_max */
|
|
330
|
+
ca_size_t scratch_bytes = st->chunk_n_max * ca->bytes;
|
|
331
|
+
st->owned_buf[k_op] = (char *) ca_lazy_arena_acquire(scratch_bytes);
|
|
332
|
+
/* base_orig stays NULL -- per-chunk gather lands the data in
|
|
333
|
+
* owned_buf[k_op]; base[k_op] will be set to owned_buf[k_op] at
|
|
334
|
+
* each chunk boundary. */
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/* iter mask = OR of INPUT operand masks, chunk-sized (chunk_n_max bytes)
|
|
339
|
+
* and re-gathered per chunk in ca_sweep_next_chunk. m0 is indexed by the
|
|
340
|
+
* offset within the chunk, m0[k] for k < chunk_n -- NOT by the flat cell
|
|
341
|
+
* index. Sizing it to n_kernel instead would leave one allocation still
|
|
342
|
+
* scaling with the operand, which is the thing this path exists to avoid:
|
|
343
|
+
* at 1 byte per cell it is an eighth of an f64 operand, but an eighth of
|
|
344
|
+
* unbounded is still unbounded. */
|
|
345
|
+
if (ctx->any_input_mask) {
|
|
346
|
+
st->m0 = (boolean8_t *) ca_lazy_arena_acquire(st->chunk_n_max);
|
|
347
|
+
st->mask_scratch = (boolean8_t *) ca_lazy_arena_acquire(st->chunk_n_max);
|
|
348
|
+
memset(st->m0, 0, st->chunk_n_max);
|
|
349
|
+
/* The OUTPUT masks have to exist before the walk starts, because each
|
|
350
|
+
* chunk's m0 is flushed into them as the walk passes -- there is no
|
|
351
|
+
* whole m0 left at release time to propagate in one go. */
|
|
352
|
+
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
353
|
+
CArray *ca = st->cx[k_op];
|
|
354
|
+
if (st->fsync[k_op] != '1') continue;
|
|
355
|
+
ca_update_mask(ca);
|
|
356
|
+
if (!ca->mask) ca_create_mask(ca);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/* CAREFUL: m0 -> OUTPUT mask propagation happens per chunk, at the point
|
|
361
|
+
* the chunk is finished (= the top of the next ca_sweep_next_chunk, and
|
|
362
|
+
* once more in release), never at acquire. Author per-cell m_out writes
|
|
363
|
+
* land in m0 during the chunk loop and must be captured after that loop
|
|
364
|
+
* has run, not before it. */
|
|
365
|
+
return Qnil;
|
|
366
|
+
}
|
|
367
|
+
|
|
161
368
|
void
|
|
162
369
|
ca_sweep_acquire_chunked (ca_sweep_state_t *st)
|
|
163
370
|
{
|
|
371
|
+
ca_sweep_acquire_ctx_t ctx;
|
|
164
372
|
int k_op;
|
|
165
|
-
int
|
|
373
|
+
int tag = 0;
|
|
166
374
|
CArray *shape_donor = NULL;
|
|
167
375
|
|
|
168
376
|
if ((int) strlen(st->fsync) != st->n_ops) {
|
|
@@ -179,34 +387,19 @@ ca_sweep_acquire_chunked (ca_sweep_state_t *st)
|
|
|
179
387
|
st->attached[k_op] = 0;
|
|
180
388
|
}
|
|
181
389
|
st->m0 = NULL;
|
|
182
|
-
st->
|
|
390
|
+
st->mask_scratch = NULL;
|
|
183
391
|
st->chunk_off = 0;
|
|
184
392
|
st->chunk_n = 0;
|
|
185
393
|
st->chunk_n_max = 0;
|
|
186
394
|
st->inner = 1;
|
|
187
395
|
st->chunked_state = 0;
|
|
188
396
|
|
|
189
|
-
/*
|
|
190
|
-
*
|
|
191
|
-
|
|
192
|
-
* defines the chunking inner-axis size. */
|
|
193
|
-
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
194
|
-
CArray *ca = st->cx[k_op];
|
|
195
|
-
if (ca_is_scalar(ca)) {
|
|
196
|
-
st->stride[k_op] = 0;
|
|
197
|
-
} else {
|
|
198
|
-
st->stride[k_op] = ca->bytes;
|
|
199
|
-
if (st->n_kernel == 1) {
|
|
200
|
-
st->n_kernel = ca->elements;
|
|
201
|
-
shape_donor = ca;
|
|
202
|
-
} else if (ca->elements != st->n_kernel) {
|
|
203
|
-
rb_raise(rb_eRuntimeError, "data size mismatch in operation");
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
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);
|
|
207
400
|
|
|
208
401
|
/* chunk-size policy: inner = donor's product of dims[1..]; chunk_n_max
|
|
209
|
-
* = compute_n on donor->bytes (=
|
|
402
|
+
* = compute_n on donor->bytes (= type-dependent 32KB target). */
|
|
210
403
|
if (shape_donor) {
|
|
211
404
|
st->inner = ca_chunk_inner_size(shape_donor);
|
|
212
405
|
st->chunk_n_max = ca_chunk_compute_n(st->n_kernel, st->inner,
|
|
@@ -217,85 +410,108 @@ ca_sweep_acquire_chunked (ca_sweep_state_t *st)
|
|
|
217
410
|
st->chunk_n_max = 1;
|
|
218
411
|
}
|
|
219
412
|
|
|
220
|
-
/*
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/* OR one INPUT operand's mask for the current chunk into m0. Gathered via
|
|
430
|
+
* ca_chunked_gather rather than attached, so no operand mask is ever
|
|
431
|
+
* attached -- the same invariant the whole-buffer path keeps with
|
|
432
|
+
* ca_xfer_all. */
|
|
433
|
+
static void
|
|
434
|
+
ca_sweep_gather_chunk_mask (ca_sweep_state_t *st, ca_size_t off, ca_size_t n)
|
|
435
|
+
{
|
|
436
|
+
int k_op;
|
|
437
|
+
ca_size_t j;
|
|
438
|
+
|
|
439
|
+
memset(st->m0, 0, n);
|
|
226
440
|
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
227
441
|
CArray *ca = st->cx[k_op];
|
|
228
|
-
if (st->fsync[k_op]
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
st->
|
|
442
|
+
if (st->fsync[k_op] != '0') continue;
|
|
443
|
+
ca_update_mask(ca);
|
|
444
|
+
if (!ca->mask) continue;
|
|
445
|
+
if (ca_is_scalar(ca)) {
|
|
446
|
+
/* one cell, broadcast across the chunk */
|
|
447
|
+
boolean8_t bit = 0;
|
|
448
|
+
ca_xfer_all(ca->mask, &bit, CA_XFER_GET);
|
|
449
|
+
if (bit) memset(st->m0, 1, n);
|
|
236
450
|
} else {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
st->owned_buf[k_op] = (char *) ca_lazy_arena_acquire(scratch_bytes);
|
|
240
|
-
/* base_orig stays NULL -- per-chunk gather lands the data in
|
|
241
|
-
* owned_buf[k_op]; base[k_op] will be set to owned_buf[k_op] at
|
|
242
|
-
* each chunk boundary. */
|
|
451
|
+
ca_chunked_gather(ca->mask, off, n, st->mask_scratch);
|
|
452
|
+
for (j = 0; j < n; j++) st->m0[j] |= st->mask_scratch[j];
|
|
243
453
|
}
|
|
244
454
|
}
|
|
455
|
+
}
|
|
245
456
|
|
|
246
|
-
|
|
247
|
-
|
|
457
|
+
/* Copy the chunk just finished out to every OUTPUT operand's mask. Runs
|
|
458
|
+
* after the author's loop over that chunk, so m_out writes into m0 are
|
|
459
|
+
* carried through. */
|
|
460
|
+
static void
|
|
461
|
+
ca_sweep_flush_chunk_mask (ca_sweep_state_t *st)
|
|
462
|
+
{
|
|
463
|
+
int k_op;
|
|
464
|
+
if (!st->m0 || st->chunked_state != 1 || st->chunk_n == 0) return;
|
|
248
465
|
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
if (any_input_mask && st->no_mask) {
|
|
255
|
-
rb_raise(rb_eRuntimeError,
|
|
256
|
-
"%s: masked INPUT not allowed in NO_MASK form "
|
|
257
|
-
"(use the *_MASKED form to handle masked cells explicitly)",
|
|
258
|
-
st->src_label ? st->src_label : "ca_sweep_acquire_chunked");
|
|
466
|
+
CArray *ca = st->cx[k_op];
|
|
467
|
+
if (st->fsync[k_op] != '1') continue;
|
|
468
|
+
if (!ca->mask) continue;
|
|
469
|
+
memcpy((boolean8_t *) ca->mask->ptr + st->chunk_off, st->m0, st->chunk_n);
|
|
259
470
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
471
|
+
}
|
|
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];
|
|
279
495
|
}
|
|
280
496
|
}
|
|
281
497
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
* are captured in the final OUTPUT mask. Do not fold this into
|
|
285
|
-
* acquire; doing so would clobber the author's writes. */
|
|
498
|
+
if (st->m0) ca_sweep_gather_chunk_mask(st, off, n);
|
|
499
|
+
return Qnil;
|
|
286
500
|
}
|
|
287
501
|
|
|
288
502
|
int
|
|
289
503
|
ca_sweep_next_chunk (ca_sweep_state_t *st)
|
|
290
504
|
{
|
|
291
|
-
int k_op;
|
|
292
505
|
ca_size_t off, n;
|
|
506
|
+
int tag = 0;
|
|
293
507
|
|
|
294
508
|
if (st->chunked_state == 0) {
|
|
295
509
|
/* first chunk */
|
|
296
510
|
st->chunk_off = 0;
|
|
297
511
|
st->chunked_state = 1;
|
|
298
512
|
} else {
|
|
513
|
+
/* the chunk that just finished is the author's last word on its mask */
|
|
514
|
+
ca_sweep_flush_chunk_mask(st);
|
|
299
515
|
/* advance */
|
|
300
516
|
st->chunk_off += st->chunk_n;
|
|
301
517
|
}
|
|
@@ -310,20 +526,14 @@ ca_sweep_next_chunk (ca_sweep_state_t *st)
|
|
|
310
526
|
if (off + n > st->n_kernel) n = st->n_kernel - off;
|
|
311
527
|
st->chunk_n = n;
|
|
312
528
|
|
|
313
|
-
/*
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
st->base[k_op] = st->base_orig[k_op] + off * st->stride[k_op];
|
|
322
|
-
} else {
|
|
323
|
-
/* non-alias INPUT: per-chunk gather into owned_buf (arena) */
|
|
324
|
-
ca_chunked_gather(ca, off, n, st->owned_buf[k_op]);
|
|
325
|
-
st->base[k_op] = st->owned_buf[k_op];
|
|
326
|
-
}
|
|
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);
|
|
327
537
|
}
|
|
328
538
|
|
|
329
539
|
return 1;
|
|
@@ -333,19 +543,12 @@ void
|
|
|
333
543
|
ca_sweep_release_chunked (ca_sweep_state_t *st)
|
|
334
544
|
{
|
|
335
545
|
int k_op;
|
|
336
|
-
/*
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
*
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
CArray *ca = st->cx[k_op];
|
|
343
|
-
if (st->fsync[k_op] != '1') continue;
|
|
344
|
-
ca_update_mask(ca);
|
|
345
|
-
if (!ca->mask) ca_create_mask(ca);
|
|
346
|
-
memcpy(ca->mask->ptr, st->m0, st->n_kernel);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
546
|
+
/* a walk that raised has already given everything back */
|
|
547
|
+
if (st->chunked_state == 3) return;
|
|
548
|
+
/* The final chunk has no next_chunk call to flush it, so it is flushed
|
|
549
|
+
* here. For INOUT_MASKED forms this is what captures the author's
|
|
550
|
+
* per-cell m_out writes over that last chunk. */
|
|
551
|
+
ca_sweep_flush_chunk_mask(st);
|
|
349
552
|
/* sync OUTPUTs (reverse order, regardless of whether chunk loop ran) */
|
|
350
553
|
for (k_op = st->n_ops - 1; k_op >= 0; k_op--) {
|
|
351
554
|
if (st->fsync[k_op] == '1') ca_sync(st->cx[k_op]);
|
|
@@ -363,6 +566,10 @@ ca_sweep_release_chunked (ca_sweep_state_t *st)
|
|
|
363
566
|
ca_lazy_arena_release(st->m0);
|
|
364
567
|
st->m0 = NULL;
|
|
365
568
|
}
|
|
569
|
+
if (st->mask_scratch) {
|
|
570
|
+
ca_lazy_arena_release(st->mask_scratch);
|
|
571
|
+
st->mask_scratch = NULL;
|
|
572
|
+
}
|
|
366
573
|
}
|
|
367
574
|
|
|
368
575
|
/* ===== WHOLE_BUFFER function form (rb_ensure-protected) ===== */
|