carray 3.0.2 → 3.0.4
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 +568 -0
- data/README.md +6 -6
- data/carray.gemspec +1 -1
- data/ext/ca_axis_descriptor.h +27 -24
- data/ext/ca_axis_dispatch.c +220 -0
- data/ext/ca_axis_group.c +201 -123
- data/ext/ca_binop_dispatch.c +46 -52
- data/ext/ca_binop_dispatch.h +1 -1
- data/ext/ca_categorical_iterator.c +249 -183
- data/ext/ca_compare.c +7 -12
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +38 -23
- data/ext/ca_kernel_iterator.c +1765 -2076
- data/ext/ca_kernel_iterator.h +584 -566
- data/ext/ca_monop_dispatch.c +86 -63
- data/ext/ca_monop_dispatch.h +5 -0
- data/ext/ca_obj_array.c +223 -56
- data/ext/ca_obj_bincmp.c +172 -138
- data/ext/ca_obj_binop.c +116 -230
- data/ext/ca_obj_bitarray.c +40 -26
- data/ext/ca_obj_bitfield.c +100 -68
- data/ext/ca_obj_block.c +19 -9
- data/ext/ca_obj_byte_swap.c +45 -46
- data/ext/ca_obj_const_string.c +269 -44
- data/ext/ca_obj_face.c +612 -89
- data/ext/ca_obj_face.h +111 -47
- data/ext/ca_obj_fake.c +36 -79
- data/ext/ca_obj_farray.c +7 -3
- data/ext/ca_obj_field.c +33 -8
- data/ext/ca_obj_fixlen_string.c +23 -24
- data/ext/ca_obj_grid.c +541 -210
- data/ext/ca_obj_meld.c +331 -111
- data/ext/ca_obj_moncmp.c +80 -65
- data/ext/ca_obj_monop.c +177 -149
- data/ext/ca_obj_object.c +664 -248
- data/ext/ca_obj_record.c +63 -23
- data/ext/ca_obj_reduce.c +18 -16
- data/ext/ca_obj_refer.c +140 -58
- data/ext/ca_obj_remap.c +85 -24
- data/ext/ca_obj_repeat.c +24 -9
- data/ext/ca_obj_roll.c +121 -16
- data/ext/ca_obj_select.c +61 -58
- data/ext/ca_obj_select_axis.c +73 -107
- data/ext/ca_obj_shift.c +32 -102
- data/ext/ca_obj_stack.c +372 -122
- data/ext/ca_obj_stride.c +348 -140
- data/ext/ca_obj_string.c +88 -22
- data/ext/ca_obj_tile.c +38 -21
- data/ext/ca_obj_time.c +45 -31
- data/ext/ca_obj_timedelta.c +43 -29
- data/ext/ca_obj_transpose.c +17 -6
- data/ext/ca_obj_triop.c +125 -141
- data/ext/ca_obj_window.c +330 -44
- data/ext/ca_op_ipower.c +24 -11
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +104 -81
- data/ext/ca_sweep_engine.h +7 -20
- data/ext/ca_triop_dispatch.c +4 -1
- data/ext/ca_triop_dispatch.h +5 -2
- data/ext/caframe_csv_reader.c +542 -0
- data/ext/carray.h +241 -92
- data/ext/carray_access.c +608 -276
- data/ext/carray_address_basis.c +494 -97
- data/ext/carray_attribute.c +129 -4
- data/ext/carray_bincount.c +39 -7
- data/ext/carray_broadcast.c +56 -19
- data/ext/carray_build_flags.h +1 -1
- data/ext/carray_call_cfunc.c +108 -28
- data/ext/carray_cast.c +1576 -223
- data/ext/carray_cast_func.rb +77 -24
- data/ext/carray_class.c +4 -3
- data/ext/carray_conversion.c +208 -75
- data/ext/carray_copy.c +82 -24
- data/ext/carray_core.c +646 -162
- data/ext/carray_count.c +171 -88
- data/ext/carray_element.c +94 -60
- data/ext/carray_factorize.c +670 -324
- data/ext/carray_generate.c +260 -83
- data/ext/carray_histogram.c +209 -106
- data/ext/carray_hold.c +14 -16
- data/ext/carray_index_classifier.c +422 -243
- data/ext/carray_internal.h +692 -3
- data/ext/carray_kernels_bincmp.c +7 -0
- data/ext/carray_kernels_binop.c +487 -120
- data/ext/carray_kernels_init.c +653 -50
- data/ext/carray_kernels_moncmp.c +15 -8
- data/ext/carray_kernels_monop.c +99 -43
- data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
- data/ext/carray_kernels_reduce_boolean.c +112 -36
- data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
- data/ext/carray_kernels_reduce_extreme.c +3032 -2892
- data/ext/carray_kernels_reduce_variance.c +439 -500
- data/ext/carray_kernels_scan.c +1097 -488
- data/ext/carray_kernels_search.c +3787 -2176
- data/ext/carray_kernels_sort.c +1200 -920
- data/ext/carray_kernels_triop.c +479 -7
- data/ext/carray_key_windows.c +216 -0
- data/ext/carray_lazy.c +967 -153
- data/ext/carray_loop.c +81 -65
- data/ext/carray_mask.c +910 -324
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +299 -100
- data/ext/carray_memory_view.c +872 -758
- data/ext/carray_operator.c +1171 -364
- data/ext/carray_order.c +505 -133
- data/ext/carray_partition.c +112 -65
- data/ext/carray_random.c +327 -239
- data/ext/carray_round.c +273 -0
- data/ext/carray_scatter.c +346 -78
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +249 -86
- data/ext/carray_sort.c +224 -227
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +118 -39
- data/ext/carray_test.c +193 -69
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_undef.c +4 -0
- data/ext/carray_utils.c +579 -113
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +19 -4
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1202 -1014
- data/ext/ruby_carray.c +17 -5
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +160 -17
- data/lib/carray/autoload_carray.rb +23 -27
- data/lib/carray/autoload_method_extension.rb +20 -5
- data/lib/carray/axis_group.rb +360 -63
- data/lib/carray/basics.rb +162 -28
- data/lib/carray/bincount_nd.rb +18 -2
- data/lib/carray/block_iterator.rb +231 -58
- data/lib/carray/boolean_reduce.rb +34 -42
- data/lib/carray/categorical.rb +242 -52
- data/lib/carray/categorical_iterator.rb +437 -827
- data/lib/carray/complex.rb +41 -34
- data/lib/carray/conditional.rb +150 -14
- data/lib/carray/const_string.rb +59 -4
- data/lib/carray/construct.rb +15 -11
- data/lib/carray/data_type_extension.rb +1 -0
- data/lib/carray/distance_limit.rb +77 -0
- data/lib/carray/fixlen_string.rb +7 -1
- data/lib/carray/frame/column_verbs.rb +70 -0
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +82 -7
- data/lib/carray/frame/csv_parser.rb +688 -87
- data/lib/carray/frame/describe.rb +80 -0
- data/lib/carray/frame/frame.rb +369 -45
- data/lib/carray/frame/group.rb +631 -90
- data/lib/carray/frame/io.rb +506 -79
- data/lib/carray/frame/join.rb +411 -62
- data/lib/carray/frame/records.rb +116 -19
- data/lib/carray/frame/reshape.rb +455 -0
- data/lib/carray/frame/sort.rb +30 -18
- data/lib/carray/frame/verbs.rb +868 -61
- data/lib/carray/frame/windows.rb +117 -0
- data/lib/carray/frame.rb +4 -0
- data/lib/carray/fuse_source.rb +118 -2
- data/lib/carray/fusion.rb +257 -42
- data/lib/carray/histogram.rb +6 -1
- data/lib/carray/inspect.rb +54 -13
- data/lib/carray/iterator.rb +99 -9
- data/lib/carray/key_windows.rb +620 -0
- data/lib/carray/lazy.rb +216 -193
- data/lib/carray/mask_gap_fill.rb +112 -92
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +52 -21
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bin.rb +26 -18
- data/lib/carray/methods/bincount.rb +18 -2
- data/lib/carray/methods/bit_string.rb +15 -4
- data/lib/carray/methods/choose.rb +16 -3
- data/lib/carray/methods/composition.rb +18 -10
- data/lib/carray/methods/factorize.rb +2 -2
- data/lib/carray/methods/gather_nd.rb +25 -5
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_between.rb +59 -0
- data/lib/carray/methods/is_in.rb +140 -10
- data/lib/carray/methods/join.rb +2 -7
- data/lib/carray/methods/lag.rb +49 -0
- data/lib/carray/methods/locate_addr.rb +182 -45
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/nunique.rb +11 -6
- data/lib/carray/methods/repeat.rb +1 -20
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +11 -2
- data/lib/carray/methods/value_counts.rb +4 -11
- data/lib/carray/record_order.rb +208 -0
- data/lib/carray/rng.rb +3 -0
- data/lib/carray/runtime.rb +65 -17
- data/lib/carray/segment_iterator.rb +1221 -0
- data/lib/carray/serialize.rb +214 -30
- data/lib/carray/slab_iterator.rb +101 -27
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string.rb +105 -5
- data/lib/carray/string_operation_extension.rb +12 -4
- data/lib/carray/struct.rb +137 -58
- data/lib/carray/struct_builder.rb +124 -17
- data/lib/carray/time.rb +1222 -186
- data/lib/carray/window_iterator.rb +242 -81
- data/lib/carray.rb +9 -2
- data/yard-stubs/ca_obj_array.rb +4 -4
- data/yard-stubs/ca_obj_bitarray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +23 -12
- data/yard-stubs/ca_obj_fake.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +5 -1
- data/yard-stubs/ca_obj_grid.rb +21 -10
- data/yard-stubs/ca_obj_refer.rb +5 -4
- data/yard-stubs/ca_obj_roll.rb +2 -3
- data/yard-stubs/ca_obj_shift.rb +5 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_tile.rb +1 -1
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +9 -5
- data/yard-stubs/carray_access.rb +11 -5
- data/yard-stubs/carray_attribute.rb +0 -5
- data/yard-stubs/carray_broadcast.rb +1 -1
- data/yard-stubs/carray_cast.rb +88 -43
- data/yard-stubs/carray_conversion.rb +15 -2
- data/yard-stubs/carray_copy.rb +12 -5
- data/yard-stubs/carray_core.rb +3 -3
- data/yard-stubs/carray_count.rb +9 -0
- data/yard-stubs/carray_element.rb +10 -4
- data/yard-stubs/carray_generate.rb +21 -1
- data/yard-stubs/carray_lazy.rb +5 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +60 -12
- data/yard-stubs/carray_math.rb +52 -14
- data/yard-stubs/carray_median_percentile.rb +15 -8
- data/yard-stubs/carray_memory_view.rb +40 -23
- data/yard-stubs/carray_order.rb +61 -29
- data/yard-stubs/carray_random.rb +13 -4
- data/yard-stubs/carray_scatter.rb +21 -9
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +10 -2
- data/yard-stubs/carray_sort.rb +49 -8
- data/yard-stubs/carray_test.rb +15 -3
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +19 -5
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_kernels_map.c +0 -3467
- data/lib/carray/arrow_tensor.rb +0 -401
- data/lib/carray/table.rb +0 -108
data/ext/carray_lazy.c
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* ---------------------------------------------------------------------------
|
|
2
2
|
|
|
3
|
-
CALazyMarker view + the
|
|
3
|
+
CALazyMarker view + the lazy arena.
|
|
4
4
|
|
|
5
5
|
CALazyMarker is a zero-cost marker that holds a CArray entity and
|
|
6
6
|
causes `.sqrt` / `.sin` / etc. on it to build a lazy CAMonOp tree
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
is a pure pass-through: every CArray operation delegates to the
|
|
12
12
|
parent. Detection happens via ca_is_lazy_view().
|
|
13
13
|
|
|
14
|
-
ca_lazy_arena is the
|
|
14
|
+
ca_lazy_arena is the per-Ractor slot-pool arena that CAMonOp / CABinOp
|
|
15
15
|
/ CAMonCmp / CABinCmp use for scratch during to_ca; slot buffers
|
|
16
16
|
stay alive across calls to amortise mmap into steady-state reuse.
|
|
17
17
|
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
|
|
20
20
|
#include "carray.h"
|
|
21
21
|
#include "carray_internal.h" /* ca_lazy_arena_*, ca_is_lazy_view */
|
|
22
|
+
#include "ca_kernel_iterator.h" /* ca_iter_register_source_kind */
|
|
23
|
+
#include <ruby/ractor.h>
|
|
22
24
|
|
|
23
25
|
int8_t CA_OBJ_LAZY_MARKER;
|
|
24
26
|
VALUE rb_cCALazyMarker;
|
|
@@ -78,10 +80,6 @@ ca_lazy_marker_setup (CALazyMarker *ca, CArray *parent)
|
|
|
78
80
|
|
|
79
81
|
memcpy(ca->dim, parent->dim, parent->ndim * sizeof(ca_size_t));
|
|
80
82
|
|
|
81
|
-
if ( ca_has_mask(parent) ) {
|
|
82
|
-
ca_create_mask(ca);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
83
|
if ( ca_is_scalar(parent) ) {
|
|
86
84
|
ca_set_flag(ca, CA_FLAG_SCALAR);
|
|
87
85
|
}
|
|
@@ -103,8 +101,10 @@ ca_lazy_marker_setup (CALazyMarker *ca, CArray *parent)
|
|
|
103
101
|
CALazyMarker *
|
|
104
102
|
ca_lazy_marker_new (CArray *parent)
|
|
105
103
|
{
|
|
104
|
+
/* check: the setup checks nothing */
|
|
106
105
|
CALazyMarker *ca = ALLOC(CALazyMarker);
|
|
107
106
|
ca_lazy_marker_setup(ca, parent);
|
|
107
|
+
ca_fill_or_free((CArray *) ca, ca_view_inherit_mask, (VALUE) ca);
|
|
108
108
|
return ca;
|
|
109
109
|
}
|
|
110
110
|
|
|
@@ -153,6 +153,7 @@ static void
|
|
|
153
153
|
ca_lazy_marker_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
154
154
|
ca_size_t *strides, void *data, int dir)
|
|
155
155
|
{
|
|
156
|
+
if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
|
|
156
157
|
CALazyMarker *ca = (CALazyMarker *) ap;
|
|
157
158
|
ca_xfer_stride(ca->parent, starts, counts, strides, data, dir);
|
|
158
159
|
}
|
|
@@ -206,12 +207,8 @@ static void
|
|
|
206
207
|
ca_lazy_marker_func_create_mask (void *ap)
|
|
207
208
|
{
|
|
208
209
|
CALazyMarker *ca = (CALazyMarker *) ap;
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
ca->mask =
|
|
213
|
-
(CArray *) ca_refer_new(ca->parent->mask,
|
|
214
|
-
CA_BOOLEAN, ca->ndim, ca->dim, 0, 0);
|
|
210
|
+
ca_view_func_create_mask(ca);
|
|
211
|
+
ca_set_flag(ca->mask, CA_FLAG_READ_ONLY);
|
|
215
212
|
}
|
|
216
213
|
|
|
217
214
|
ca_operation_function_t ca_lazy_marker_func = {
|
|
@@ -250,10 +247,19 @@ rb_ca_lazy_marker_new (VALUE cary)
|
|
|
250
247
|
|
|
251
248
|
/* CArray#lazy — return a zero-cost CALazyMarker wrapping self. Subsequent
|
|
252
249
|
* element-wise ops (.sqrt / .sin / .+ / ...) build a lazy CAMonOp / CABinOp
|
|
253
|
-
* tree instead of evaluating eagerly; `.to_ca` materialises.
|
|
250
|
+
* tree instead of evaluating eagerly; `.to_ca` materialises.
|
|
251
|
+
*
|
|
252
|
+
* A Face returns itself: a lazy tree computes on storage and would lose
|
|
253
|
+
* the Face's meaning (units, labels, string surface), so its operations
|
|
254
|
+
* stay eager. */
|
|
254
255
|
VALUE
|
|
255
256
|
rb_ca_lazy (VALUE self)
|
|
256
257
|
{
|
|
258
|
+
CArray *ca;
|
|
259
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
260
|
+
if ( ca_is_face(ca) ) {
|
|
261
|
+
return self;
|
|
262
|
+
}
|
|
257
263
|
return rb_ca_lazy_marker_new(self);
|
|
258
264
|
}
|
|
259
265
|
|
|
@@ -292,10 +298,66 @@ typedef struct ca_gc_hold {
|
|
|
292
298
|
ca_size_t elements;
|
|
293
299
|
} ca_gc_hold_t;
|
|
294
300
|
|
|
295
|
-
|
|
296
|
-
|
|
301
|
+
/* Each Ractor keeps its own holds: walks in different Ractors push and
|
|
302
|
+
release at the same time. The GC marks every Ractor's set through
|
|
303
|
+
the storage type's mark function. */
|
|
304
|
+
typedef struct ca_gc_local {
|
|
305
|
+
ca_gc_hold_t holds[CA_GC_HOLD_MAX];
|
|
306
|
+
int hold_depth;
|
|
307
|
+
ca_gc_hold_t *buffers; /* held until released, in any order */
|
|
308
|
+
int buffer_n;
|
|
309
|
+
int buffer_cap;
|
|
310
|
+
} ca_gc_local_t;
|
|
311
|
+
|
|
312
|
+
static rb_ractor_local_key_t ca_gc_local_key;
|
|
313
|
+
|
|
314
|
+
static void
|
|
315
|
+
ca_gc_local_mark (void *ptr)
|
|
316
|
+
{
|
|
317
|
+
ca_gc_local_t *g = (ca_gc_local_t *) ptr;
|
|
318
|
+
int i;
|
|
319
|
+
if ( g == NULL ) return;
|
|
320
|
+
for ( i = 0; i < g->hold_depth; i++ ) {
|
|
321
|
+
VALUE *p = g->holds[i].ptr;
|
|
322
|
+
ca_size_t n = g->holds[i].elements;
|
|
323
|
+
while ( n-- ) rb_gc_mark(*p++);
|
|
324
|
+
}
|
|
325
|
+
for ( i = 0; i < g->buffer_n; i++ ) {
|
|
326
|
+
VALUE *p = g->buffers[i].ptr;
|
|
327
|
+
ca_size_t n = g->buffers[i].elements;
|
|
328
|
+
while ( n-- ) rb_gc_mark(*p++);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
static void
|
|
333
|
+
ca_gc_local_free (void *ptr)
|
|
334
|
+
{
|
|
335
|
+
ca_gc_local_t *g = (ca_gc_local_t *) ptr;
|
|
336
|
+
if ( g == NULL ) return;
|
|
337
|
+
free(g->buffers);
|
|
338
|
+
free(g);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
static const struct rb_ractor_local_storage_type ca_gc_local_type = {
|
|
342
|
+
ca_gc_local_mark, ca_gc_local_free
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
/* The state is allocated with the C allocator, like the table it grows:
|
|
346
|
+
a collection must not run while either is half set up. */
|
|
347
|
+
static ca_gc_local_t *
|
|
348
|
+
ca_gc_local (void)
|
|
349
|
+
{
|
|
350
|
+
ca_gc_local_t *g = (ca_gc_local_t *) rb_ractor_local_storage_ptr(ca_gc_local_key);
|
|
351
|
+
if ( g == NULL ) {
|
|
352
|
+
g = (ca_gc_local_t *) calloc(1, sizeof(ca_gc_local_t));
|
|
353
|
+
if ( g == NULL ) {
|
|
354
|
+
rb_memerror();
|
|
355
|
+
}
|
|
356
|
+
rb_ractor_local_storage_ptr_set(ca_gc_local_key, g);
|
|
357
|
+
}
|
|
358
|
+
return g;
|
|
359
|
+
}
|
|
297
360
|
|
|
298
|
-
static void ca_lazy_arena_mark_object_slots (void);
|
|
299
361
|
|
|
300
362
|
/* Returns the depth to hand back to ca_gc_hold_pop_to, so nested holds
|
|
301
363
|
unwind in order. Pushing is best-effort at the ceiling: a chain
|
|
@@ -306,56 +368,72 @@ static void ca_lazy_arena_mark_object_slots (void);
|
|
|
306
368
|
int
|
|
307
369
|
ca_gc_hold_push (void *ptr, ca_size_t n_elements)
|
|
308
370
|
{
|
|
309
|
-
|
|
371
|
+
ca_gc_local_t *g;
|
|
372
|
+
int depth;
|
|
310
373
|
|
|
311
|
-
if (
|
|
374
|
+
if ( n_elements <= 0 || ptr == NULL ) {
|
|
312
375
|
return -1;
|
|
313
376
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
377
|
+
g = ca_gc_local();
|
|
378
|
+
depth = g->hold_depth;
|
|
379
|
+
if ( depth >= CA_GC_HOLD_MAX ) {
|
|
380
|
+
return -1;
|
|
381
|
+
}
|
|
382
|
+
g->holds[depth].ptr = (VALUE *) ptr;
|
|
383
|
+
g->holds[depth].elements = n_elements;
|
|
384
|
+
g->hold_depth = depth + 1;
|
|
317
385
|
return depth;
|
|
318
386
|
}
|
|
319
387
|
|
|
320
388
|
void
|
|
321
389
|
ca_gc_hold_pop_to (int depth)
|
|
322
390
|
{
|
|
323
|
-
|
|
324
|
-
|
|
391
|
+
ca_gc_local_t *g;
|
|
392
|
+
if ( depth < 0 ) return;
|
|
393
|
+
g = ca_gc_local();
|
|
394
|
+
if ( depth < g->hold_depth ) {
|
|
395
|
+
g->hold_depth = depth;
|
|
325
396
|
}
|
|
326
397
|
}
|
|
327
398
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
/* The wrapped pointer must be non-NULL: Ruby's GC skips the mark
|
|
331
|
-
function of a TypedData whose data pointer is NULL. */
|
|
332
|
-
static int ca_gc_guard_body = 0;
|
|
333
|
-
|
|
334
|
-
static void
|
|
335
|
-
ca_gc_guard_mark (void *ptr)
|
|
399
|
+
void
|
|
400
|
+
ca_gc_hold_buffer (void *ptr, ca_size_t n_elements)
|
|
336
401
|
{
|
|
337
|
-
|
|
338
|
-
(
|
|
339
|
-
|
|
340
|
-
VALUE *p = ca_gc_holds[i].ptr;
|
|
341
|
-
ca_size_t n = ca_gc_holds[i].elements;
|
|
342
|
-
while ( n-- ) rb_gc_mark(*p++);
|
|
402
|
+
ca_gc_local_t *g;
|
|
403
|
+
if ( ptr == NULL || n_elements <= 0 ) {
|
|
404
|
+
return;
|
|
343
405
|
}
|
|
344
|
-
|
|
406
|
+
g = ca_gc_local();
|
|
407
|
+
if ( g->buffer_n == g->buffer_cap ) {
|
|
408
|
+
int cap = g->buffer_cap ? g->buffer_cap * 2 : 16;
|
|
409
|
+
ca_gc_hold_t *grown = (ca_gc_hold_t *) realloc(g->buffers,
|
|
410
|
+
cap * sizeof(ca_gc_hold_t));
|
|
411
|
+
if ( grown == NULL ) {
|
|
412
|
+
rb_memerror();
|
|
413
|
+
}
|
|
414
|
+
g->buffers = grown;
|
|
415
|
+
g->buffer_cap = cap;
|
|
416
|
+
}
|
|
417
|
+
g->buffers[g->buffer_n].ptr = (VALUE *) ptr;
|
|
418
|
+
g->buffers[g->buffer_n].elements = n_elements;
|
|
419
|
+
g->buffer_n++;
|
|
345
420
|
}
|
|
346
421
|
|
|
347
|
-
|
|
348
|
-
|
|
422
|
+
void
|
|
423
|
+
ca_gc_release_buffer (void *ptr)
|
|
349
424
|
{
|
|
350
|
-
(
|
|
425
|
+
ca_gc_local_t *g = (ca_gc_local_t *) rb_ractor_local_storage_ptr(ca_gc_local_key);
|
|
426
|
+
int i;
|
|
427
|
+
if ( g == NULL ) return;
|
|
428
|
+
for ( i = g->buffer_n - 1; i >= 0; i-- ) {
|
|
429
|
+
if ( g->buffers[i].ptr == (VALUE *) ptr ) {
|
|
430
|
+
g->buffers[i] = g->buffers[g->buffer_n - 1];
|
|
431
|
+
g->buffer_n--;
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
351
435
|
}
|
|
352
436
|
|
|
353
|
-
static const rb_data_type_t ca_gc_guard_data_type = {
|
|
354
|
-
"carray_object_gc_guard",
|
|
355
|
-
{ ca_gc_guard_mark, ca_gc_guard_free, NULL, },
|
|
356
|
-
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
|
357
|
-
};
|
|
358
|
-
|
|
359
437
|
/* ------------------------------------------------------------------- */
|
|
360
438
|
/* ca_lazy_arena */
|
|
361
439
|
/* ------------------------------------------------------------------- */
|
|
@@ -374,15 +452,17 @@ static const rb_data_type_t ca_gc_guard_data_type = {
|
|
|
374
452
|
best-fit allocation keeps small requests (mask scratch) out of
|
|
375
453
|
large data slots
|
|
376
454
|
|
|
377
|
-
Footprint:
|
|
378
|
-
e.g. N=1M f64, depth-8 chain =
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
455
|
+
Footprint: one slot per scratch held at the same time, each as large
|
|
456
|
+
as the largest request it has served (e.g. N=1M f64, depth-8 chain =
|
|
457
|
+
8 × 8MB = 64MB kept resident). A request no free slot can hold grows
|
|
458
|
+
a free slot; a new slot opens only while every warm one is in use.
|
|
459
|
+
Past 256 MB the idle slots are given back at the next top-level entry
|
|
460
|
+
(ca_lazy_arena_trim). When all 32 are in use the acquire raises: this signals a programming
|
|
461
|
+
error or a pathological chain rather than a graceful fallback.
|
|
382
462
|
|
|
383
|
-
|
|
384
|
-
is not a goal (see
|
|
385
|
-
|
|
463
|
+
One arena per Ractor (see ca_lazy_arena_get). Within a Ractor it is
|
|
464
|
+
not guarded: thread-safety across concurrent access is not a goal (see
|
|
465
|
+
guides/devel/04_attach_lifecycle.md). */
|
|
386
466
|
|
|
387
467
|
#define CA_LAZY_ARENA_SLOTS 32
|
|
388
468
|
|
|
@@ -401,29 +481,109 @@ typedef struct ca_lazy_arena {
|
|
|
401
481
|
ca_size_t debug_reuse_count; /* test: how many acquire = reuse */
|
|
402
482
|
} ca_lazy_arena_t;
|
|
403
483
|
|
|
404
|
-
|
|
484
|
+
/* One arena per Ractor: the slots are handed out by pointer and toggled
|
|
485
|
+
without a lock, so two Ractors sharing them would hand the same buffer
|
|
486
|
+
to both. The storage marks the object slots in use and frees the
|
|
487
|
+
buffers when the Ractor ends. */
|
|
488
|
+
static rb_ractor_local_key_t ca_lazy_arena_key;
|
|
489
|
+
|
|
490
|
+
static void ca_lazy_arena_mark_object_slots (ca_lazy_arena_t *arena);
|
|
491
|
+
|
|
492
|
+
static void
|
|
493
|
+
ca_lazy_arena_mark (void *ptr)
|
|
494
|
+
{
|
|
495
|
+
if ( ptr ) ca_lazy_arena_mark_object_slots((ca_lazy_arena_t *) ptr);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
static void
|
|
499
|
+
ca_lazy_arena_free (void *ptr)
|
|
500
|
+
{
|
|
501
|
+
ca_lazy_arena_t *arena = (ca_lazy_arena_t *) ptr;
|
|
502
|
+
int i;
|
|
503
|
+
if ( arena == NULL ) return;
|
|
504
|
+
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
505
|
+
if ( arena->slots[i].ptr ) ruby_xfree(arena->slots[i].ptr);
|
|
506
|
+
}
|
|
507
|
+
ruby_xfree(arena);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
static const struct rb_ractor_local_storage_type ca_lazy_arena_type = {
|
|
511
|
+
ca_lazy_arena_mark, ca_lazy_arena_free
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
static ca_lazy_arena_t *
|
|
515
|
+
ca_lazy_arena_get (void)
|
|
516
|
+
{
|
|
517
|
+
ca_lazy_arena_t *arena =
|
|
518
|
+
(ca_lazy_arena_t *) rb_ractor_local_storage_ptr(ca_lazy_arena_key);
|
|
519
|
+
if ( arena == NULL ) {
|
|
520
|
+
arena = ZALLOC(ca_lazy_arena_t);
|
|
521
|
+
rb_ractor_local_storage_ptr_set(ca_lazy_arena_key, arena);
|
|
522
|
+
}
|
|
523
|
+
return arena;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/* Above this the arena returns memory: the idle slots are freed, the
|
|
527
|
+
smallest first, until what it holds is under the limit. The largest
|
|
528
|
+
slot is always kept, so a workload that repeats one request keeps its
|
|
529
|
+
warm buffer however large it is. */
|
|
530
|
+
#define CA_LAZY_ARENA_KEEP_BYTES ((ca_size_t) 256 << 20)
|
|
531
|
+
|
|
532
|
+
static void
|
|
533
|
+
ca_lazy_arena_trim (ca_lazy_arena_t *arena)
|
|
534
|
+
{
|
|
535
|
+
ca_size_t total = 0;
|
|
536
|
+
int i, largest = -1;
|
|
537
|
+
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
538
|
+
if ( arena->slots[i].ptr == NULL ) continue;
|
|
539
|
+
total += arena->slots[i].bytes;
|
|
540
|
+
if ( largest < 0 || arena->slots[i].bytes > arena->slots[largest].bytes ) {
|
|
541
|
+
largest = i;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
while ( total > CA_LAZY_ARENA_KEEP_BYTES ) {
|
|
545
|
+
int smallest = -1;
|
|
546
|
+
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
547
|
+
ca_lazy_arena_slot_t *s = &arena->slots[i];
|
|
548
|
+
if ( s->ptr == NULL || s->in_use || i == largest ) continue;
|
|
549
|
+
if ( smallest < 0 || s->bytes < arena->slots[smallest].bytes ) {
|
|
550
|
+
smallest = i;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if ( smallest < 0 ) break;
|
|
554
|
+
total -= arena->slots[smallest].bytes;
|
|
555
|
+
xfree(arena->slots[smallest].ptr);
|
|
556
|
+
arena->slots[smallest].ptr = NULL;
|
|
557
|
+
arena->slots[smallest].bytes = 0;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
405
560
|
|
|
406
561
|
void
|
|
407
562
|
ca_lazy_arena_enter (void)
|
|
408
563
|
{
|
|
409
|
-
|
|
564
|
+
ca_lazy_arena_t *arena = ca_lazy_arena_get();
|
|
565
|
+
if ( arena->depth == 0 ) {
|
|
566
|
+
/* Only a slot no one holds is freed: a lazy view's attach takes
|
|
567
|
+
scratch outside any enter / exit bracket. */
|
|
568
|
+
ca_lazy_arena_trim(arena);
|
|
410
569
|
/* Top-level reset to recover from a prior exception that left
|
|
411
570
|
slots in_use without a matching release. Safe because
|
|
412
571
|
depth==0 means no nested caller is mid-acquire. */
|
|
413
572
|
int i;
|
|
414
573
|
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
415
|
-
|
|
416
|
-
|
|
574
|
+
arena->slots[i].in_use = 0;
|
|
575
|
+
arena->slots[i].object_elements = 0;
|
|
417
576
|
}
|
|
418
577
|
}
|
|
419
|
-
|
|
578
|
+
arena->depth++;
|
|
420
579
|
}
|
|
421
580
|
|
|
422
581
|
void
|
|
423
582
|
ca_lazy_arena_exit (void)
|
|
424
583
|
{
|
|
425
|
-
|
|
426
|
-
|
|
584
|
+
ca_lazy_arena_t *arena = ca_lazy_arena_get();
|
|
585
|
+
if ( arena->depth > 0 ) {
|
|
586
|
+
arena->depth--;
|
|
427
587
|
}
|
|
428
588
|
/* Keep all slot buffers, and leave in_use and the object tags alone.
|
|
429
589
|
A scratch can be acquired outside any enter/exit bracket -- a lazy
|
|
@@ -437,15 +597,67 @@ ca_lazy_arena_exit (void)
|
|
|
437
597
|
from being collected in the first place. */
|
|
438
598
|
}
|
|
439
599
|
|
|
600
|
+
/* Runs body(arg) inside an enter / exit bracket that closes however body
|
|
601
|
+
leaves. cleanup(arg), when given, runs first -- the place to release
|
|
602
|
+
the scratches and detach the arrays body acquired -- and the exit runs
|
|
603
|
+
after it.
|
|
604
|
+
|
|
605
|
+
A raise that skipped the exit would leave the depth above 0 for the
|
|
606
|
+
rest of the process: the depth-0 reset that recovers abandoned slots
|
|
607
|
+
would never fire again, and once 32 slots were abandoned every lazy
|
|
608
|
+
expression would fail. */
|
|
609
|
+
|
|
610
|
+
typedef struct {
|
|
611
|
+
VALUE (*cleanup)(VALUE);
|
|
612
|
+
VALUE arg;
|
|
613
|
+
} ca_lazy_arena_ensure_t;
|
|
614
|
+
|
|
615
|
+
static VALUE
|
|
616
|
+
ca_lazy_arena_ensure (VALUE varg)
|
|
617
|
+
{
|
|
618
|
+
ca_lazy_arena_ensure_t *e = (ca_lazy_arena_ensure_t *) varg;
|
|
619
|
+
if ( e->cleanup ) {
|
|
620
|
+
e->cleanup(e->arg);
|
|
621
|
+
}
|
|
622
|
+
ca_lazy_arena_exit();
|
|
623
|
+
return Qnil;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
VALUE
|
|
627
|
+
ca_lazy_arena_protect (VALUE (*body)(VALUE), VALUE (*cleanup)(VALUE),
|
|
628
|
+
VALUE arg)
|
|
629
|
+
{
|
|
630
|
+
ca_lazy_arena_ensure_t e;
|
|
631
|
+
e.cleanup = cleanup;
|
|
632
|
+
e.arg = arg;
|
|
633
|
+
ca_lazy_arena_enter();
|
|
634
|
+
return rb_ensure(body, arg, ca_lazy_arena_ensure, (VALUE) &e);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/* A cleanup for ca_lazy_arena_protect when body holds one scratch: arg
|
|
638
|
+
points at a struct whose first member is that scratch (NULL until it
|
|
639
|
+
is acquired). */
|
|
640
|
+
VALUE
|
|
641
|
+
ca_lazy_arena_release_held (VALUE arg)
|
|
642
|
+
{
|
|
643
|
+
void **held = (void **) arg;
|
|
644
|
+
if ( *held ) {
|
|
645
|
+
ca_lazy_arena_release(*held);
|
|
646
|
+
*held = NULL;
|
|
647
|
+
}
|
|
648
|
+
return Qnil;
|
|
649
|
+
}
|
|
650
|
+
|
|
440
651
|
void *
|
|
441
652
|
ca_lazy_arena_acquire (ca_size_t bytes)
|
|
442
653
|
{
|
|
654
|
+
ca_lazy_arena_t *arena = ca_lazy_arena_get();
|
|
443
655
|
int i, best, empty;
|
|
444
656
|
ca_size_t best_bytes;
|
|
445
657
|
|
|
446
658
|
if ( bytes <= 0 ) bytes = 1;
|
|
447
659
|
|
|
448
|
-
|
|
660
|
+
arena->debug_acquire_count++;
|
|
449
661
|
|
|
450
662
|
/* Pass 1: best-fit among unused warm slots (smallest >= bytes).
|
|
451
663
|
CAREFUL: ca_size_t is signed int64_t, so the "no match yet"
|
|
@@ -454,7 +666,7 @@ ca_lazy_arena_acquire (ca_size_t bytes)
|
|
|
454
666
|
best = -1;
|
|
455
667
|
best_bytes = CA_LENGTH_MAX;
|
|
456
668
|
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
457
|
-
ca_lazy_arena_slot_t *s = &
|
|
669
|
+
ca_lazy_arena_slot_t *s = &arena->slots[i];
|
|
458
670
|
if ( s->in_use ) continue;
|
|
459
671
|
if ( s->ptr == NULL ) continue;
|
|
460
672
|
if ( s->bytes < bytes ) continue;
|
|
@@ -464,50 +676,68 @@ ca_lazy_arena_acquire (ca_size_t bytes)
|
|
|
464
676
|
}
|
|
465
677
|
}
|
|
466
678
|
if ( best >= 0 ) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
return
|
|
679
|
+
arena->slots[best].in_use = 1;
|
|
680
|
+
arena->slots[best].object_elements = 0;
|
|
681
|
+
arena->debug_reuse_count++;
|
|
682
|
+
return arena->slots[best].ptr;
|
|
471
683
|
}
|
|
472
684
|
|
|
473
685
|
|
|
474
|
-
/* Pass 2:
|
|
475
|
-
|
|
686
|
+
/* Pass 2: every free warm slot is too small. Grow the largest of
|
|
687
|
+
them rather than open another slot: a slot that is free now was not
|
|
688
|
+
needed at the same time as this request, so the arena holds one
|
|
689
|
+
buffer per simultaneous acquire rather than one per size ever asked
|
|
690
|
+
for. Taking the largest leaves the small slots to the small
|
|
691
|
+
requests (mask scratch next to data scratch).
|
|
692
|
+
|
|
693
|
+
The slot is emptied before the xmalloc so that a NoMemoryError
|
|
694
|
+
leaves it unallocated instead of pointing at freed memory. */
|
|
695
|
+
best = -1;
|
|
696
|
+
best_bytes = -1;
|
|
476
697
|
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
698
|
+
ca_lazy_arena_slot_t *s = &arena->slots[i];
|
|
699
|
+
if ( s->in_use || s->ptr == NULL ) continue;
|
|
700
|
+
if ( s->bytes > best_bytes ) {
|
|
701
|
+
best = i;
|
|
702
|
+
best_bytes = s->bytes;
|
|
480
703
|
}
|
|
481
704
|
}
|
|
482
|
-
if (
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
705
|
+
if ( best >= 0 ) {
|
|
706
|
+
ca_lazy_arena_slot_t *s = &arena->slots[best];
|
|
707
|
+
void *old = s->ptr;
|
|
708
|
+
s->ptr = NULL;
|
|
709
|
+
s->bytes = 0;
|
|
710
|
+
xfree(old);
|
|
711
|
+
s->ptr = xmalloc(bytes); /* cells: ca_lazy_arena_acquire_object clears object slots */
|
|
712
|
+
s->bytes = bytes;
|
|
713
|
+
s->in_use = 1;
|
|
714
|
+
s->object_elements = 0;
|
|
715
|
+
arena->debug_xmalloc_count++;
|
|
716
|
+
return s->ptr;
|
|
489
717
|
}
|
|
490
718
|
|
|
491
|
-
/* Pass 3:
|
|
492
|
-
|
|
493
|
-
slot pool exhaustion (= unusual size request). */
|
|
719
|
+
/* Pass 3: every warm slot is in use. Open an empty one. */
|
|
720
|
+
empty = -1;
|
|
494
721
|
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
495
|
-
if (
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
ca_lazy_arena.slots[i].bytes = bytes;
|
|
499
|
-
ca_lazy_arena.slots[i].in_use = 1;
|
|
500
|
-
ca_lazy_arena.slots[i].object_elements = 0;
|
|
501
|
-
ca_lazy_arena.debug_xmalloc_count++;
|
|
502
|
-
return ca_lazy_arena.slots[i].ptr;
|
|
722
|
+
if ( arena->slots[i].ptr == NULL ) {
|
|
723
|
+
empty = i;
|
|
724
|
+
break;
|
|
503
725
|
}
|
|
504
726
|
}
|
|
727
|
+
if ( empty >= 0 ) {
|
|
728
|
+
arena->slots[empty].ptr = xmalloc(bytes); /* cells: ca_lazy_arena_acquire_object clears object slots */
|
|
729
|
+
arena->slots[empty].bytes = bytes;
|
|
730
|
+
arena->slots[empty].in_use = 1;
|
|
731
|
+
arena->slots[empty].object_elements = 0;
|
|
732
|
+
arena->debug_xmalloc_count++;
|
|
733
|
+
return arena->slots[empty].ptr;
|
|
734
|
+
}
|
|
505
735
|
|
|
506
736
|
/* Pass 4: all CA_LAZY_ARENA_SLOTS in use simultaneously. This is
|
|
507
737
|
a programming error or a pathological chain. Raise. */
|
|
508
|
-
rb_raise(
|
|
509
|
-
"
|
|
510
|
-
"
|
|
738
|
+
rb_raise(rb_eArgError,
|
|
739
|
+
"lazy expression too deep: it needs more than %d intermediate "
|
|
740
|
+
"arrays at once",
|
|
511
741
|
CA_LAZY_ARENA_SLOTS);
|
|
512
742
|
return NULL; /* unreachable */
|
|
513
743
|
}
|
|
@@ -515,12 +745,13 @@ ca_lazy_arena_acquire (ca_size_t bytes)
|
|
|
515
745
|
void
|
|
516
746
|
ca_lazy_arena_release (void *ptr)
|
|
517
747
|
{
|
|
748
|
+
ca_lazy_arena_t *arena = ca_lazy_arena_get();
|
|
518
749
|
int i;
|
|
519
750
|
if ( ptr == NULL ) return;
|
|
520
751
|
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
521
|
-
if (
|
|
522
|
-
|
|
523
|
-
|
|
752
|
+
if ( arena->slots[i].ptr == ptr ) {
|
|
753
|
+
arena->slots[i].in_use = 0;
|
|
754
|
+
arena->slots[i].object_elements = 0;
|
|
524
755
|
return;
|
|
525
756
|
}
|
|
526
757
|
}
|
|
@@ -539,21 +770,18 @@ ca_lazy_arena_release (void *ptr)
|
|
|
539
770
|
void *
|
|
540
771
|
ca_lazy_arena_acquire_object (ca_size_t n_elements)
|
|
541
772
|
{
|
|
773
|
+
ca_lazy_arena_t *arena = ca_lazy_arena_get();
|
|
542
774
|
void *ptr;
|
|
543
775
|
int i;
|
|
544
776
|
|
|
545
777
|
if ( n_elements < 0 ) n_elements = 0;
|
|
546
778
|
ptr = ca_lazy_arena_acquire(n_elements * (ca_size_t) sizeof(VALUE));
|
|
547
779
|
|
|
548
|
-
|
|
549
|
-
VALUE *p = (VALUE *) ptr;
|
|
550
|
-
ca_size_t n = n_elements;
|
|
551
|
-
while ( n-- ) *p++ = Qnil;
|
|
552
|
-
}
|
|
780
|
+
ca_object_cells_clear(ptr, n_elements);
|
|
553
781
|
|
|
554
782
|
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
555
|
-
if (
|
|
556
|
-
|
|
783
|
+
if ( arena->slots[i].ptr == ptr ) {
|
|
784
|
+
arena->slots[i].object_elements = n_elements;
|
|
557
785
|
break;
|
|
558
786
|
}
|
|
559
787
|
}
|
|
@@ -561,11 +789,11 @@ ca_lazy_arena_acquire_object (ca_size_t n_elements)
|
|
|
561
789
|
}
|
|
562
790
|
|
|
563
791
|
static void
|
|
564
|
-
ca_lazy_arena_mark_object_slots (
|
|
792
|
+
ca_lazy_arena_mark_object_slots (ca_lazy_arena_t *arena)
|
|
565
793
|
{
|
|
566
794
|
int i;
|
|
567
795
|
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
568
|
-
ca_lazy_arena_slot_t *s = &
|
|
796
|
+
ca_lazy_arena_slot_t *s = &arena->slots[i];
|
|
569
797
|
ca_size_t n;
|
|
570
798
|
VALUE *p;
|
|
571
799
|
if ( ! s->in_use || s->object_elements <= 0 ) continue;
|
|
@@ -577,45 +805,52 @@ ca_lazy_arena_mark_object_slots (void)
|
|
|
577
805
|
|
|
578
806
|
/* Test instrumentation — exposed to Ruby for the arena smoke and
|
|
579
807
|
regression assertions. Not user-facing API. */
|
|
808
|
+
#ifdef CARRAY_DEV_BUILD
|
|
580
809
|
static VALUE
|
|
581
810
|
rb_ca_lazy_arena_s_reset_counters (VALUE klass)
|
|
582
811
|
{
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
812
|
+
ca_lazy_arena_t *arena = ca_lazy_arena_get();
|
|
813
|
+
arena->debug_acquire_count = 0;
|
|
814
|
+
arena->debug_xmalloc_count = 0;
|
|
815
|
+
arena->debug_reuse_count = 0;
|
|
586
816
|
return Qnil;
|
|
587
817
|
}
|
|
588
818
|
|
|
589
819
|
static VALUE
|
|
590
820
|
rb_ca_lazy_arena_s_acquire_count (VALUE klass)
|
|
591
821
|
{
|
|
592
|
-
|
|
822
|
+
ca_lazy_arena_t *arena = ca_lazy_arena_get();
|
|
823
|
+
return SIZE2NUM(arena->debug_acquire_count);
|
|
593
824
|
}
|
|
594
825
|
|
|
595
826
|
static VALUE
|
|
596
827
|
rb_ca_lazy_arena_s_xmalloc_count (VALUE klass)
|
|
597
828
|
{
|
|
598
|
-
|
|
829
|
+
ca_lazy_arena_t *arena = ca_lazy_arena_get();
|
|
830
|
+
return SIZE2NUM(arena->debug_xmalloc_count);
|
|
599
831
|
}
|
|
600
832
|
|
|
601
833
|
static VALUE
|
|
602
834
|
rb_ca_lazy_arena_s_reuse_count (VALUE klass)
|
|
603
835
|
{
|
|
604
|
-
|
|
836
|
+
ca_lazy_arena_t *arena = ca_lazy_arena_get();
|
|
837
|
+
return SIZE2NUM(arena->debug_reuse_count);
|
|
605
838
|
}
|
|
606
839
|
|
|
607
840
|
static VALUE
|
|
608
841
|
rb_ca_lazy_arena_s_depth (VALUE klass)
|
|
609
842
|
{
|
|
610
|
-
|
|
843
|
+
ca_lazy_arena_t *arena = ca_lazy_arena_get();
|
|
844
|
+
return INT2NUM(arena->depth);
|
|
611
845
|
}
|
|
612
846
|
|
|
613
847
|
static VALUE
|
|
614
848
|
rb_ca_lazy_arena_s_slot_in_use_count (VALUE klass)
|
|
615
849
|
{
|
|
850
|
+
ca_lazy_arena_t *arena = ca_lazy_arena_get();
|
|
616
851
|
int i, n = 0;
|
|
617
852
|
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
618
|
-
if (
|
|
853
|
+
if ( arena->slots[i].in_use ) n++;
|
|
619
854
|
}
|
|
620
855
|
return INT2NUM(n);
|
|
621
856
|
}
|
|
@@ -629,51 +864,21 @@ rb_ca_lazy_arena_s_slot_in_use_count (VALUE klass)
|
|
|
629
864
|
static VALUE
|
|
630
865
|
rb_ca_lazy_arena_s_slot_capacities (VALUE klass)
|
|
631
866
|
{
|
|
867
|
+
ca_lazy_arena_t *arena = ca_lazy_arena_get();
|
|
632
868
|
int i;
|
|
633
869
|
VALUE arr = rb_ary_new();
|
|
634
870
|
(void) klass;
|
|
635
871
|
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
636
|
-
if (
|
|
637
|
-
rb_ary_push(arr, SIZE2NUM(
|
|
872
|
+
if ( arena->slots[i].ptr != NULL ) {
|
|
873
|
+
rb_ary_push(arr, SIZE2NUM(arena->slots[i].bytes));
|
|
638
874
|
}
|
|
639
875
|
}
|
|
640
876
|
return arr;
|
|
641
877
|
}
|
|
878
|
+
#endif /* CARRAY_DEV_BUILD */
|
|
642
879
|
|
|
643
880
|
/* ------------------------------------------------------------------- */
|
|
644
881
|
|
|
645
|
-
/* ca_lazy_wrap_scalar(other, self_ca) — wrap a non-CArray Ruby value
|
|
646
|
-
* as a 1-element CScalar whose data_type follows the array's family,
|
|
647
|
-
* mirroring the array+scalar branch of rb_ca_cast_self_or_other. The
|
|
648
|
-
* scalar takes on self's data_type so binops preserve self's
|
|
649
|
-
* precision (`f32_array + 2.5` stays f32).
|
|
650
|
-
*
|
|
651
|
-
* CA_OBJECT self -> CA_OBJECT
|
|
652
|
-
* CA_BOOLEAN self -> derive from other's Ruby type via
|
|
653
|
-
* ca_value_to_data_type (bool arithmetic is a
|
|
654
|
-
* corner case; eager widens the array, so we
|
|
655
|
-
* just let the scalar choose)
|
|
656
|
-
* numeric self -> self_ca->data_type
|
|
657
|
-
*
|
|
658
|
-
* Callers broadcast against the array via stride 0 in xfer_stride. */
|
|
659
|
-
VALUE
|
|
660
|
-
ca_lazy_wrap_scalar (VALUE other, CArray *self_ca)
|
|
661
|
-
{
|
|
662
|
-
int8_t dt;
|
|
663
|
-
|
|
664
|
-
if ( ca_is_object_type(self_ca) ) {
|
|
665
|
-
dt = CA_OBJECT;
|
|
666
|
-
}
|
|
667
|
-
else if ( self_ca->data_type == CA_BOOLEAN ) {
|
|
668
|
-
dt = ca_value_to_data_type(other);
|
|
669
|
-
}
|
|
670
|
-
else {
|
|
671
|
-
dt = self_ca->data_type;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
return rb_cscalar_new_with_value(dt, 0, other);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
882
|
static VALUE
|
|
678
883
|
rb_ca_lazy_marker_s_allocate (VALUE klass)
|
|
679
884
|
{
|
|
@@ -687,11 +892,15 @@ rb_ca_lazy_marker_initialize_copy (VALUE self, VALUE other)
|
|
|
687
892
|
{
|
|
688
893
|
CALazyMarker *ca, *cs;
|
|
689
894
|
TypedData_Get_Struct(self, CALazyMarker, &calazy_marker_data_type, ca);
|
|
895
|
+
ca_check_uninitialized(ca);
|
|
690
896
|
TypedData_Get_Struct(other, CALazyMarker, &calazy_marker_data_type, cs);
|
|
691
897
|
ca_lazy_marker_setup(ca, cs->parent);
|
|
898
|
+
ca_view_inherit_mask((VALUE) ca);
|
|
692
899
|
return self;
|
|
693
900
|
}
|
|
694
901
|
|
|
902
|
+
static void Init_ca_mask_of_operands (void);
|
|
903
|
+
|
|
695
904
|
void
|
|
696
905
|
Init_carray_lazy (void)
|
|
697
906
|
{
|
|
@@ -707,16 +916,15 @@ Init_carray_lazy (void)
|
|
|
707
916
|
|
|
708
917
|
rb_define_method(rb_cCArray, "lazy", rb_ca_lazy, 0);
|
|
709
918
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
&ca_gc_guard_body);
|
|
714
|
-
rb_gc_register_mark_object(ca_gc_guard);
|
|
919
|
+
ca_gc_local_key = rb_ractor_local_storage_ptr_newkey(&ca_gc_local_type);
|
|
920
|
+
|
|
921
|
+
ca_lazy_arena_key = rb_ractor_local_storage_ptr_newkey(&ca_lazy_arena_type);
|
|
715
922
|
|
|
716
923
|
rb_define_alloc_func(rb_cCALazyMarker, rb_ca_lazy_marker_s_allocate);
|
|
717
924
|
rb_define_method(rb_cCALazyMarker, "initialize_copy",
|
|
718
925
|
rb_ca_lazy_marker_initialize_copy, 1);
|
|
719
926
|
|
|
927
|
+
#ifdef CARRAY_DEV_BUILD
|
|
720
928
|
/* Arena test instrumentation (not user-facing). Bound on CArray
|
|
721
929
|
for symmetry with CAMonOp / CABinOp counters, but the arena is
|
|
722
930
|
a singleton process-wide state. */
|
|
@@ -734,4 +942,610 @@ Init_carray_lazy (void)
|
|
|
734
942
|
rb_ca_lazy_arena_s_slot_in_use_count, 0);
|
|
735
943
|
rb_define_singleton_method(rb_cCArray, "__lazy_arena_slot_capacities__",
|
|
736
944
|
rb_ca_lazy_arena_s_slot_capacities, 0);
|
|
945
|
+
#endif
|
|
946
|
+
|
|
947
|
+
Init_ca_mask_of_operands();
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
/* ---------------------------------------------------------------------------
|
|
951
|
+
Requests to the element-wise lazy views (ca_lazy_req_t, carray_internal.h).
|
|
952
|
+
|
|
953
|
+
An operand is pulled over the cells the request names. In the region
|
|
954
|
+
form the strides address the view's cells, so they are restated at the
|
|
955
|
+
operand's cell width: strides[k] / bytes is the index step the two share.
|
|
956
|
+
(A packed stride over counts is not a substitute -- it walks cells 0, 1,
|
|
957
|
+
2 where the request asked for 0, 2, 4.)
|
|
958
|
+
--------------------------------------------------------------------------- */
|
|
959
|
+
|
|
960
|
+
void
|
|
961
|
+
ca_lazy_req_region (ca_lazy_req_t *req, void *view, ca_size_t *starts,
|
|
962
|
+
ca_size_t *counts, ca_size_t *strides)
|
|
963
|
+
{
|
|
964
|
+
CArray *ca = (CArray *) view;
|
|
965
|
+
int8_t k;
|
|
966
|
+
req->n = 1;
|
|
967
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
968
|
+
req->n *= counts[k];
|
|
969
|
+
}
|
|
970
|
+
req->addrs = NULL;
|
|
971
|
+
req->starts = starts;
|
|
972
|
+
req->counts = counts;
|
|
973
|
+
req->strides = strides;
|
|
974
|
+
req->bytes = ca->bytes;
|
|
975
|
+
req->ndim = ca->ndim;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
void
|
|
979
|
+
ca_lazy_req_addrs (ca_lazy_req_t *req, ca_size_t n, ca_size_t *addrs)
|
|
980
|
+
{
|
|
981
|
+
req->n = n;
|
|
982
|
+
req->addrs = addrs;
|
|
983
|
+
req->starts = NULL;
|
|
984
|
+
req->counts = NULL;
|
|
985
|
+
req->strides = NULL;
|
|
986
|
+
req->bytes = 0;
|
|
987
|
+
req->ndim = 0;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/* Whether the request names one contiguous run of the view's cells, in
|
|
991
|
+
the view's own row-major order -- the case in which an operand's buffer
|
|
992
|
+
already holds the cells in the order the kernel reads them. The strides
|
|
993
|
+
must be the native row-major ones, and the box must be whole on every
|
|
994
|
+
axis inside the outermost one it does not span with a count of 1. */
|
|
995
|
+
int
|
|
996
|
+
ca_lazy_req_is_packed (const ca_lazy_req_t *req, void *view)
|
|
997
|
+
{
|
|
998
|
+
CArray *ca = (CArray *) view;
|
|
999
|
+
ca_size_t native = ca->bytes;
|
|
1000
|
+
int partial = 0;
|
|
1001
|
+
int8_t k;
|
|
1002
|
+
if ( req->addrs ) {
|
|
1003
|
+
return 0;
|
|
1004
|
+
}
|
|
1005
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) {
|
|
1006
|
+
if ( req->strides[k] != native ) {
|
|
1007
|
+
return 0;
|
|
1008
|
+
}
|
|
1009
|
+
if ( partial && req->counts[k] != 1 ) {
|
|
1010
|
+
return 0;
|
|
1011
|
+
}
|
|
1012
|
+
if ( req->counts[k] != ca->dim[k] ) {
|
|
1013
|
+
partial = 1;
|
|
1014
|
+
}
|
|
1015
|
+
native *= ca->dim[k];
|
|
1016
|
+
}
|
|
1017
|
+
return 1;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
/* The cells of a request that any of `ops` masks, as a packed boolean
|
|
1021
|
+
slab in arena scratch, or NULL when none of them is masked. An operand
|
|
1022
|
+
of one cell masks every cell or none. Kernels that can raise on a cell,
|
|
1023
|
+
or that call Ruby for it, take this to skip the masked cells, as the
|
|
1024
|
+
eager operators do. Release with ca_lazy_arena_release. */
|
|
1025
|
+
boolean8_t *
|
|
1026
|
+
ca_lazy_req_mask (const ca_lazy_req_t *req, int n, CArray **ops)
|
|
1027
|
+
{
|
|
1028
|
+
boolean8_t *m = NULL;
|
|
1029
|
+
ca_size_t j;
|
|
1030
|
+
int i;
|
|
1031
|
+
int8_t k;
|
|
1032
|
+
|
|
1033
|
+
for ( i = 0; i < n; i++ ) {
|
|
1034
|
+
CArray *op = ops[i];
|
|
1035
|
+
if ( ! op || ! ca_has_mask(op) ) {
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
if ( ! m ) {
|
|
1039
|
+
m = (boolean8_t *) ca_lazy_arena_acquire(req->n);
|
|
1040
|
+
memset(m, 0, req->n);
|
|
1041
|
+
}
|
|
1042
|
+
if ( op->elements == 1 ) {
|
|
1043
|
+
ca_size_t starts[CA_RANK_MAX], counts[CA_RANK_MAX];
|
|
1044
|
+
ca_size_t strides[CA_RANK_MAX];
|
|
1045
|
+
boolean8_t bit = 0;
|
|
1046
|
+
for ( k = 0; k < op->ndim; k++ ) {
|
|
1047
|
+
starts[k] = 0; counts[k] = 1; strides[k] = 1;
|
|
1048
|
+
}
|
|
1049
|
+
ca_xfer_stride(op->mask, starts, counts, strides, &bit, CA_XFER_GET);
|
|
1050
|
+
if ( bit ) {
|
|
1051
|
+
memset(m, 1, req->n);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
else {
|
|
1055
|
+
volatile VALUE holder;
|
|
1056
|
+
boolean8_t *om = ALLOCV_N(boolean8_t, holder, req->n);
|
|
1057
|
+
ca_lazy_req_pull(op->mask, req, om, CA_XFER_GET);
|
|
1058
|
+
for ( j = 0; j < req->n; j++ ) {
|
|
1059
|
+
m[j] |= om[j];
|
|
1060
|
+
}
|
|
1061
|
+
ALLOCV_END(holder);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
return m;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
/* A scalar operand (a Ruby value or a CScalar) that the eager promotion
|
|
1068
|
+
has cast to the array's data_type comes back as a cast view over a
|
|
1069
|
+
CScalar. Fix it as the CScalar the cast gives, so the expression holds
|
|
1070
|
+
a plain value leaf. */
|
|
1071
|
+
VALUE
|
|
1072
|
+
ca_lazy_settle_scalar (VALUE operand, int was_scalar)
|
|
1073
|
+
{
|
|
1074
|
+
CArray *ca;
|
|
1075
|
+
if ( ! was_scalar ) {
|
|
1076
|
+
return operand;
|
|
1077
|
+
}
|
|
1078
|
+
/* rb_obj_is_cscalar is true for any view carrying the scalar flag, the
|
|
1079
|
+
cast view included; the obj_type says whether it is a CScalar. */
|
|
1080
|
+
TypedData_Get_Struct(operand, CArray, &carray_data_type, ca);
|
|
1081
|
+
if ( ca->obj_type != CA_OBJ_SCALAR ) {
|
|
1082
|
+
return rb_ca_copy(operand);
|
|
1083
|
+
}
|
|
1084
|
+
return operand;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
void
|
|
1088
|
+
ca_lazy_req_pull (void *operand, const ca_lazy_req_t *req, void *buf, int dir)
|
|
1089
|
+
{
|
|
1090
|
+
CArray *op = (CArray *) operand;
|
|
1091
|
+
ca_size_t strides[CA_RANK_MAX];
|
|
1092
|
+
int8_t k;
|
|
1093
|
+
|
|
1094
|
+
if ( req->addrs ) {
|
|
1095
|
+
ca_xfer_addrs(op, req->n, req->addrs, buf, dir);
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
for ( k = 0; k < req->ndim; k++ ) {
|
|
1099
|
+
strides[k] = req->strides[k] / req->bytes * op->bytes;
|
|
1100
|
+
}
|
|
1101
|
+
ca_xfer_stride(op, req->starts, req->counts, strides, buf, dir);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/* An expression the caller is about to compute whole -- a reduction along
|
|
1105
|
+
an axis, a sort, a median -- computed by the registered expression
|
|
1106
|
+
evaluator instead, when there is one and it takes the expression.
|
|
1107
|
+
Returns the array it made, or Qnil where nothing is registered, self is
|
|
1108
|
+
not a lazy expression, or the evaluator declined (CArray::Fusion.evaluate
|
|
1109
|
+
decides, and asks only above its size threshold). The caller goes on
|
|
1110
|
+
with that array in place of self, or with self as before.
|
|
1111
|
+
|
|
1112
|
+
Only where the expression would be made whole anyway: a reduction that
|
|
1113
|
+
streams the expression in chunks keeps doing so, since asking here would
|
|
1114
|
+
make it whole and take that much more memory. */
|
|
1115
|
+
VALUE
|
|
1116
|
+
ca_lazy_evaluated (VALUE self)
|
|
1117
|
+
{
|
|
1118
|
+
/* rb_intern caches each literal by itself; a shared guard over several
|
|
1119
|
+
IDs would let a second Ractor read one not yet set. */
|
|
1120
|
+
ID id_evaluator = rb_intern("@expression_evaluator");
|
|
1121
|
+
ID id_fusion = rb_intern("Fusion");
|
|
1122
|
+
ID id_evaluate = rb_intern("evaluate");
|
|
1123
|
+
CArray *ca;
|
|
1124
|
+
GetCArray(self, ca);
|
|
1125
|
+
if ( ! ca_is_lazy_view(ca) ) {
|
|
1126
|
+
return Qnil;
|
|
1127
|
+
}
|
|
1128
|
+
/* registered in the main Ractor, and asked only there */
|
|
1129
|
+
if ( ! ca_in_main_ractor() || ! RTEST(rb_attr_get(rb_cCArray, id_evaluator)) ) {
|
|
1130
|
+
return Qnil;
|
|
1131
|
+
}
|
|
1132
|
+
return rb_funcall(rb_const_get(rb_cCArray, id_fusion), id_evaluate, 1, self);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
/* ---------------------------------------------------------------------------
|
|
1136
|
+
CAMaskOfOperands: the mask of an element-wise lazy operation over several
|
|
1137
|
+
operands (CABinOp, CABinCmp, CATriOp).
|
|
1138
|
+
|
|
1139
|
+
The operation computes its values each time it is read, so its mask is
|
|
1140
|
+
computed the same way: a request pulls each operand's mask over the cells
|
|
1141
|
+
it names and ORs them. An operand with no mask reads as all false, so an
|
|
1142
|
+
operand that is given a mask after the expression was first read is seen
|
|
1143
|
+
on the next read -- and reading the mask never gives an operand one.
|
|
1144
|
+
|
|
1145
|
+
For the boolean `|` and `&` (three-valued) a masked cell is not masked in
|
|
1146
|
+
the result when an unmasked operand already decides it: true for `|`,
|
|
1147
|
+
false for `&`. The operands' values are pulled for that.
|
|
1148
|
+
|
|
1149
|
+
The view is read-only and has no mask of its own (CA_FLAG_VALUE_ARRAY),
|
|
1150
|
+
so nothing above it asks for one. Its parent is the operation, which
|
|
1151
|
+
owns it as its mask; the operands are read through the operation's
|
|
1152
|
+
parents[].
|
|
1153
|
+
--------------------------------------------------------------------------- */
|
|
1154
|
+
|
|
1155
|
+
int8_t CA_OBJ_MASK_OF_OPERANDS;
|
|
1156
|
+
VALUE rb_cCAMaskOfOperands;
|
|
1157
|
+
|
|
1158
|
+
typedef struct CAMaskOfOperands {
|
|
1159
|
+
int16_t obj_type;
|
|
1160
|
+
int8_t data_type;
|
|
1161
|
+
int8_t ndim;
|
|
1162
|
+
int32_t flags;
|
|
1163
|
+
ca_size_t bytes;
|
|
1164
|
+
ca_size_t elements;
|
|
1165
|
+
ca_size_t *dim;
|
|
1166
|
+
char *ptr;
|
|
1167
|
+
CArray *mask;
|
|
1168
|
+
char *_pool;
|
|
1169
|
+
CArray *parent;
|
|
1170
|
+
uint32_t attach;
|
|
1171
|
+
uint8_t nosync;
|
|
1172
|
+
int8_t mode; /* CA_LAZY_MASK_OR / _KLEENE_OR / _KLEENE_AND / _SELECT */
|
|
1173
|
+
} CAMaskOfOperands;
|
|
1174
|
+
|
|
1175
|
+
static size_t
|
|
1176
|
+
ca_mask_of_operands_dsize (const void *ap)
|
|
1177
|
+
{
|
|
1178
|
+
const CAMaskOfOperands *ca = (const CAMaskOfOperands *) ap;
|
|
1179
|
+
return sizeof(CAMaskOfOperands) + ca->ndim * sizeof(ca_size_t);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
const rb_data_type_t camask_of_operands_data_type = {
|
|
1183
|
+
.parent = &caview_data_type,
|
|
1184
|
+
.wrap_struct_name = "CAMaskOfOperands",
|
|
1185
|
+
.function = {
|
|
1186
|
+
.dmark = ca_mark,
|
|
1187
|
+
.dfree = ca_free,
|
|
1188
|
+
.dsize = ca_mask_of_operands_dsize,
|
|
1189
|
+
.dcompact = NULL
|
|
1190
|
+
},
|
|
1191
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
1192
|
+
};
|
|
1193
|
+
|
|
1194
|
+
CArray *
|
|
1195
|
+
ca_mask_of_operands_new (CArray *operation, int mode)
|
|
1196
|
+
{
|
|
1197
|
+
CAMaskOfOperands *ca = ALLOC(CAMaskOfOperands);
|
|
1198
|
+
ca->_pool = NULL;
|
|
1199
|
+
ca->obj_type = CA_OBJ_MASK_OF_OPERANDS;
|
|
1200
|
+
ca->data_type = CA_BOOLEAN;
|
|
1201
|
+
ca->flags = CA_FLAG_READ_ONLY | CA_FLAG_VALUE_ARRAY;
|
|
1202
|
+
ca->ndim = operation->ndim;
|
|
1203
|
+
ca->bytes = 1;
|
|
1204
|
+
ca->elements = operation->elements;
|
|
1205
|
+
ca->dim = ALLOC_N(ca_size_t, operation->ndim);
|
|
1206
|
+
ca->ptr = NULL;
|
|
1207
|
+
ca->mask = NULL;
|
|
1208
|
+
ca->parent = operation;
|
|
1209
|
+
ca->attach = 0;
|
|
1210
|
+
ca->nosync = 0;
|
|
1211
|
+
ca->mode = (int8_t) mode;
|
|
1212
|
+
memcpy(ca->dim, operation->dim, operation->ndim * sizeof(ca_size_t));
|
|
1213
|
+
if ( ca_is_scalar(operation) ) {
|
|
1214
|
+
ca_set_flag(ca, CA_FLAG_SCALAR);
|
|
1215
|
+
}
|
|
1216
|
+
return (CArray *) ca;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
static void
|
|
1220
|
+
free_ca_mask_of_operands (void *ap)
|
|
1221
|
+
{
|
|
1222
|
+
CAMaskOfOperands *ca = (CAMaskOfOperands *) ap;
|
|
1223
|
+
if ( ca != NULL ) {
|
|
1224
|
+
xfree(ca->ptr);
|
|
1225
|
+
xfree(ca->dim);
|
|
1226
|
+
xfree(ca);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
static void *
|
|
1231
|
+
ca_mask_of_operands_func_clone (void *ap)
|
|
1232
|
+
{
|
|
1233
|
+
CAMaskOfOperands *ca = (CAMaskOfOperands *) ap;
|
|
1234
|
+
return ca_mask_of_operands_new(ca->parent, ca->mode);
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
/* An operand of one cell beside a larger operation is broadcast: its one
|
|
1238
|
+
cell stands for every cell of the request. */
|
|
1239
|
+
static int
|
|
1240
|
+
ca_mask_of_operands_operand_is_scalar (CAMaskOfOperands *lm, CArray *op)
|
|
1241
|
+
{
|
|
1242
|
+
return ( op->elements == 1 && lm->elements > 1 );
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
static void
|
|
1246
|
+
ca_mask_of_operands_pull (CAMaskOfOperands *lm, CArray *src, const ca_lazy_req_t *req,
|
|
1247
|
+
boolean8_t *buf)
|
|
1248
|
+
{
|
|
1249
|
+
if ( ca_mask_of_operands_operand_is_scalar(lm, src) ) {
|
|
1250
|
+
boolean8_t one;
|
|
1251
|
+
ca_xfer_all(src, &one, CA_XFER_GET);
|
|
1252
|
+
memset(buf, one ? 1 : 0, req->n);
|
|
1253
|
+
}
|
|
1254
|
+
else {
|
|
1255
|
+
ca_lazy_req_pull(src, req, buf, CA_XFER_GET);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
/* select: masked where the condition is, else where the branch it
|
|
1260
|
+
chooses is. The condition arrives converted to the branches' type
|
|
1261
|
+
from boolean, so a cell is true exactly when one of its bytes is not
|
|
1262
|
+
zero. */
|
|
1263
|
+
static void
|
|
1264
|
+
ca_mask_of_operands_eval_select (CAMaskOfOperands *lm, const ca_lazy_req_t *req,
|
|
1265
|
+
boolean8_t *dst)
|
|
1266
|
+
{
|
|
1267
|
+
CAMultiParent *mp = (CAMultiParent *) lm->parent;
|
|
1268
|
+
CArray *cond = mp->parents[0];
|
|
1269
|
+
ca_size_t bytes = cond->bytes;
|
|
1270
|
+
ca_size_t n = req->n, i, b;
|
|
1271
|
+
volatile VALUE h_value, h_m1, h_m2;
|
|
1272
|
+
boolean8_t *m1, *m2;
|
|
1273
|
+
char *v;
|
|
1274
|
+
int32_t k;
|
|
1275
|
+
|
|
1276
|
+
memset(dst, 0, n);
|
|
1277
|
+
if ( ca_has_mask(cond) ) {
|
|
1278
|
+
ca_mask_of_operands_pull(lm, cond->mask, req, dst);
|
|
1279
|
+
}
|
|
1280
|
+
m1 = ALLOCV_N(boolean8_t, h_m1, n);
|
|
1281
|
+
m2 = ALLOCV_N(boolean8_t, h_m2, n);
|
|
1282
|
+
for ( k = 1; k <= 2; k++ ) {
|
|
1283
|
+
CArray *op = mp->parents[k];
|
|
1284
|
+
boolean8_t *m = ( k == 1 ) ? m1 : m2;
|
|
1285
|
+
if ( ca_has_mask(op) ) {
|
|
1286
|
+
ca_mask_of_operands_pull(lm, op->mask, req, m);
|
|
1287
|
+
}
|
|
1288
|
+
else {
|
|
1289
|
+
memset(m, 0, n);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
if ( ca_mask_of_operands_operand_is_scalar(lm, cond) ) {
|
|
1293
|
+
v = ALLOCV_N(char, h_value, bytes);
|
|
1294
|
+
ca_xfer_all(cond, v, CA_XFER_GET);
|
|
1295
|
+
for ( b = 0; b < bytes && ! v[b]; b++ ) ;
|
|
1296
|
+
for ( i = 0; i < n; i++ ) dst[i] |= ( b < bytes ) ? m1[i] : m2[i];
|
|
1297
|
+
}
|
|
1298
|
+
else {
|
|
1299
|
+
v = ALLOCV_N(char, h_value, n * bytes);
|
|
1300
|
+
ca_lazy_req_pull(cond, req, v, CA_XFER_GET);
|
|
1301
|
+
for ( i = 0; i < n; i++ ) {
|
|
1302
|
+
const char *c = v + i * bytes;
|
|
1303
|
+
for ( b = 0; b < bytes && ! c[b]; b++ ) ;
|
|
1304
|
+
dst[i] |= ( b < bytes ) ? m1[i] : m2[i];
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
ALLOCV_END(h_value);
|
|
1308
|
+
ALLOCV_END(h_m2);
|
|
1309
|
+
ALLOCV_END(h_m1);
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
static void
|
|
1313
|
+
ca_mask_of_operands_eval (CAMaskOfOperands *lm, const ca_lazy_req_t *req, boolean8_t *dst)
|
|
1314
|
+
{
|
|
1315
|
+
CAMultiParent *mp = (CAMultiParent *) lm->parent;
|
|
1316
|
+
int kleene = ( lm->mode == CA_LAZY_MASK_KLEENE_OR ||
|
|
1317
|
+
lm->mode == CA_LAZY_MASK_KLEENE_AND );
|
|
1318
|
+
boolean8_t absorbing = ( lm->mode == CA_LAZY_MASK_KLEENE_OR ) ? 1 : 0;
|
|
1319
|
+
volatile VALUE h_mask, h_value, h_known;
|
|
1320
|
+
boolean8_t *m, *v = NULL, *known = NULL;
|
|
1321
|
+
ca_size_t n = req->n, i;
|
|
1322
|
+
int32_t k;
|
|
1323
|
+
|
|
1324
|
+
if ( lm->mode == CA_LAZY_MASK_SELECT ) {
|
|
1325
|
+
ca_mask_of_operands_eval_select(lm, req, dst);
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
memset(dst, 0, n);
|
|
1330
|
+
m = ALLOCV_N(boolean8_t, h_mask, n);
|
|
1331
|
+
if ( kleene ) {
|
|
1332
|
+
v = ALLOCV_N(boolean8_t, h_value, n);
|
|
1333
|
+
known = ALLOCV_N(boolean8_t, h_known, n);
|
|
1334
|
+
memset(known, 0, n);
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
for ( k = 0; k < mp->n_parents; k++ ) {
|
|
1338
|
+
CArray *op = mp->parents[k];
|
|
1339
|
+
if ( ca_has_mask(op) ) {
|
|
1340
|
+
ca_mask_of_operands_pull(lm, op->mask, req, m);
|
|
1341
|
+
for ( i = 0; i < n; i++ ) dst[i] |= m[i];
|
|
1342
|
+
}
|
|
1343
|
+
else {
|
|
1344
|
+
memset(m, 0, n);
|
|
1345
|
+
}
|
|
1346
|
+
if ( kleene ) {
|
|
1347
|
+
ca_mask_of_operands_pull(lm, op, req, v);
|
|
1348
|
+
for ( i = 0; i < n; i++ ) {
|
|
1349
|
+
if ( ! m[i] && ( v[i] ? 1 : 0 ) == absorbing ) known[i] = 1;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
if ( kleene ) {
|
|
1355
|
+
for ( i = 0; i < n; i++ ) {
|
|
1356
|
+
if ( known[i] ) dst[i] = 0;
|
|
1357
|
+
}
|
|
1358
|
+
ALLOCV_END(h_known);
|
|
1359
|
+
ALLOCV_END(h_value);
|
|
1360
|
+
}
|
|
1361
|
+
ALLOCV_END(h_mask);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
NORETURN(static void ca_mask_of_operands_read_only (void));
|
|
1365
|
+
static void
|
|
1366
|
+
ca_mask_of_operands_read_only (void)
|
|
1367
|
+
{
|
|
1368
|
+
/* read-only-check: says it is the mask, which no lazy node owns */
|
|
1369
|
+
rb_raise(rb_eRuntimeError, "the mask of a lazy operation is read-only");
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
static void
|
|
1373
|
+
ca_mask_of_operands_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
1374
|
+
ca_size_t *strides, void *data, int dir)
|
|
1375
|
+
{
|
|
1376
|
+
ca_lazy_req_t req;
|
|
1377
|
+
if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
|
|
1378
|
+
if ( dir != CA_XFER_GET ) ca_mask_of_operands_read_only();
|
|
1379
|
+
ca_lazy_req_region(&req, ap, starts, counts, strides);
|
|
1380
|
+
ca_mask_of_operands_eval((CAMaskOfOperands *) ap, &req, (boolean8_t *) data);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
static void
|
|
1384
|
+
ca_mask_of_operands_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
|
|
1385
|
+
void *data, int dir)
|
|
1386
|
+
{
|
|
1387
|
+
ca_lazy_req_t req;
|
|
1388
|
+
if ( dir != CA_XFER_GET ) ca_mask_of_operands_read_only();
|
|
1389
|
+
ca_lazy_req_addrs(&req, n, addrs);
|
|
1390
|
+
ca_mask_of_operands_eval((CAMaskOfOperands *) ap, &req, (boolean8_t *) data);
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
static void
|
|
1394
|
+
ca_mask_of_operands_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
|
|
1395
|
+
{
|
|
1396
|
+
CArray *ca = (CArray *) ap;
|
|
1397
|
+
ca_size_t addr = 0;
|
|
1398
|
+
int8_t k;
|
|
1399
|
+
if ( dir != CA_XFER_GET ) ca_mask_of_operands_read_only();
|
|
1400
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
1401
|
+
addr = addr * ca->dim[k] + idx[k];
|
|
1402
|
+
}
|
|
1403
|
+
ca_mask_of_operands_func_xfer_addrs(ap, 1, &addr, data, dir);
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
static void
|
|
1407
|
+
ca_mask_of_operands_func_xfer_all (void *ap, void *data, int dir)
|
|
1408
|
+
{
|
|
1409
|
+
CArray *ca = (CArray *) ap;
|
|
1410
|
+
ca_size_t starts[CA_RANK_MAX];
|
|
1411
|
+
ca_size_t native[CA_RANK_MAX];
|
|
1412
|
+
ca_size_t s = 1;
|
|
1413
|
+
int8_t k;
|
|
1414
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
|
|
1415
|
+
for ( k = 0; k < ca->ndim; k++ ) starts[k] = 0;
|
|
1416
|
+
ca_mask_of_operands_func_xfer_stride(ap, starts, ca->dim, native, data, dir);
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
static VALUE
|
|
1420
|
+
ca_mask_of_operands_fill_buffer (VALUE arg)
|
|
1421
|
+
{
|
|
1422
|
+
void **a = (void **) arg;
|
|
1423
|
+
ca_mask_of_operands_func_xfer_all(a[0], a[1], CA_XFER_GET);
|
|
1424
|
+
return Qnil;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
/* The buffer is published only once it is filled: a read that raises
|
|
1428
|
+
frees it and leaves the view unattached. */
|
|
1429
|
+
static void
|
|
1430
|
+
ca_mask_of_operands_func_attach (void *ap)
|
|
1431
|
+
{
|
|
1432
|
+
CAMaskOfOperands *ca = (CAMaskOfOperands *) ap;
|
|
1433
|
+
char *buf = ca_alloc_cells((CArray *) ca);
|
|
1434
|
+
void *a[2];
|
|
1435
|
+
a[0] = ca;
|
|
1436
|
+
a[1] = buf;
|
|
1437
|
+
ca_fill_or_xfree(buf, ca_mask_of_operands_fill_buffer, (VALUE) a);
|
|
1438
|
+
ca->ptr = buf;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
static void
|
|
1442
|
+
ca_mask_of_operands_func_allocate (void *ap)
|
|
1443
|
+
{
|
|
1444
|
+
CAMaskOfOperands *ca = (CAMaskOfOperands *) ap;
|
|
1445
|
+
ca->ptr = ca_alloc_cells((CArray *) ca);
|
|
1446
|
+
memset(ca->ptr, 0, ca->elements);
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
static void
|
|
1450
|
+
ca_mask_of_operands_func_sync (void *ap)
|
|
1451
|
+
{
|
|
1452
|
+
/* read-only */
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
static void
|
|
1456
|
+
ca_mask_of_operands_func_detach (void *ap)
|
|
1457
|
+
{
|
|
1458
|
+
CAMaskOfOperands *ca = (CAMaskOfOperands *) ap;
|
|
1459
|
+
xfree(ca->ptr);
|
|
1460
|
+
ca->ptr = NULL;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
NORETURN(static void ca_mask_of_operands_func_fill_data (void *ap, void *ptr));
|
|
1464
|
+
static void
|
|
1465
|
+
ca_mask_of_operands_func_fill_data (void *ap, void *ptr)
|
|
1466
|
+
{
|
|
1467
|
+
ca_mask_of_operands_read_only();
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
NORETURN(static void ca_mask_of_operands_func_create_mask (void *ap));
|
|
1471
|
+
static void
|
|
1472
|
+
ca_mask_of_operands_func_create_mask (void *ap)
|
|
1473
|
+
{
|
|
1474
|
+
rb_raise(rb_eTypeError, "can not create mask array for the mask array");
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
ca_operation_function_t ca_mask_of_operands_func = {
|
|
1478
|
+
-1, /* CA_OBJ_MASK_OF_OPERANDS, set at install time */
|
|
1479
|
+
CA_VIEW_ARRAY,
|
|
1480
|
+
free_ca_mask_of_operands,
|
|
1481
|
+
ca_mask_of_operands_func_clone,
|
|
1482
|
+
ca_mask_of_operands_func_allocate,
|
|
1483
|
+
ca_mask_of_operands_func_attach,
|
|
1484
|
+
ca_mask_of_operands_func_sync,
|
|
1485
|
+
ca_mask_of_operands_func_detach,
|
|
1486
|
+
ca_mask_of_operands_func_fill_data,
|
|
1487
|
+
ca_mask_of_operands_func_create_mask,
|
|
1488
|
+
ca_mask_of_operands_func_xfer_index,
|
|
1489
|
+
ca_mask_of_operands_func_xfer_addrs,
|
|
1490
|
+
NULL, /* fold_stride: computed, never folds */
|
|
1491
|
+
ca_mask_of_operands_func_xfer_stride,
|
|
1492
|
+
ca_mask_of_operands_func_xfer_all,
|
|
1493
|
+
};
|
|
1494
|
+
|
|
1495
|
+
static void
|
|
1496
|
+
Init_ca_mask_of_operands (void)
|
|
1497
|
+
{
|
|
1498
|
+
rb_cCAMaskOfOperands = rb_define_class("CAMaskOfOperands", rb_cCAView);
|
|
1499
|
+
CA_OBJ_MASK_OF_OPERANDS = ca_install_obj_type(rb_cCAMaskOfOperands,
|
|
1500
|
+
&camask_of_operands_data_type,
|
|
1501
|
+
rb_cCArrayMask,
|
|
1502
|
+
&carray_mask_data_type,
|
|
1503
|
+
&ca_mask_of_operands_func,
|
|
1504
|
+
sizeof(ca_mask_of_operands_func));
|
|
1505
|
+
rb_undef_alloc_func(rb_cCAMaskOfOperands);
|
|
1506
|
+
ca_iter_register_source_kind(CA_OBJ_MASK_OF_OPERANDS, CA_ITER_SRC_ATTACH);
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
/* The mask of a lazy operation over several operands: shared with the one
|
|
1510
|
+
masked operand when no other operand can ever be given a mask, computed
|
|
1511
|
+
from all of them on each read otherwise (CAMaskOfOperands above).
|
|
1512
|
+
|
|
1513
|
+
An operand can be given a mask later unless it is broadcast from one
|
|
1514
|
+
cell (a scalar the expression made from a literal), a value array, or a
|
|
1515
|
+
read-only entity. The shared mask is read-only, as the operation is.
|
|
1516
|
+
|
|
1517
|
+
With no masked operand the mask is still made when asked for (a view
|
|
1518
|
+
over the operation that masks cells of its own, such as a shift with
|
|
1519
|
+
fill UNDEF, needs one to lie over): it reads all false until an
|
|
1520
|
+
operand is given a mask. */
|
|
1521
|
+
CArray *
|
|
1522
|
+
ca_lazy_operation_mask (CArray *operation, int mode)
|
|
1523
|
+
{
|
|
1524
|
+
CAMultiParent *mp = (CAMultiParent *) operation;
|
|
1525
|
+
CArray *masked = NULL;
|
|
1526
|
+
int32_t k, n_masked = 0, n_open = 0;
|
|
1527
|
+
|
|
1528
|
+
for ( k = 0; k < mp->n_parents; k++ ) {
|
|
1529
|
+
CArray *op = mp->parents[k];
|
|
1530
|
+
if ( ca_has_mask(op) ) {
|
|
1531
|
+
masked = op;
|
|
1532
|
+
n_masked++;
|
|
1533
|
+
}
|
|
1534
|
+
else if ( ! ( op->elements == 1 && operation->elements > 1 )
|
|
1535
|
+
&& ! ca_is_value_array(op)
|
|
1536
|
+
&& ! ( ca_is_entity(op) && ca_is_readonly(op) ) ) {
|
|
1537
|
+
n_open++;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
if ( mode == CA_LAZY_MASK_OR && n_masked == 1 && n_open == 0
|
|
1542
|
+
&& masked->elements == operation->elements ) {
|
|
1543
|
+
CArray *shared = (CArray *) ca_refer_new(masked->mask, CA_BOOLEAN,
|
|
1544
|
+
operation->ndim, operation->dim,
|
|
1545
|
+
0, 0);
|
|
1546
|
+
ca_set_flag(shared, CA_FLAG_READ_ONLY);
|
|
1547
|
+
return shared;
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
return ca_mask_of_operands_new(operation, mode);
|
|
737
1551
|
}
|