carray 3.0.1 → 3.0.3
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 +824 -0
- data/README.md +5 -6
- data/carray.gemspec +1 -1
- data/ext/ca_axis_dispatch.c +258 -4
- data/ext/ca_axis_group.c +368 -184
- data/ext/ca_binop_dispatch.c +54 -32
- data/ext/ca_binop_dispatch.h +1 -0
- data/ext/ca_categorical_iterator.c +90 -41
- data/ext/ca_compare.c +8 -2
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +27 -11
- data/ext/ca_kernel_iterator.c +960 -245
- data/ext/ca_kernel_iterator.h +352 -181
- data/ext/ca_monop_dispatch.c +4 -4
- data/ext/ca_monop_dispatch.h +4 -0
- data/ext/ca_obj_array.c +194 -42
- data/ext/ca_obj_bincmp.c +152 -121
- data/ext/ca_obj_binop.c +107 -210
- data/ext/ca_obj_bitarray.c +37 -24
- data/ext/ca_obj_bitfield.c +95 -62
- data/ext/ca_obj_block.c +5 -4
- data/ext/ca_obj_byte_swap.c +40 -40
- data/ext/ca_obj_const_string.c +180 -50
- data/ext/ca_obj_face.c +428 -62
- data/ext/ca_obj_face.h +107 -47
- data/ext/ca_obj_fake.c +21 -50
- data/ext/ca_obj_farray.c +1 -0
- data/ext/ca_obj_field.c +16 -3
- data/ext/ca_obj_fixlen_string.c +39 -26
- data/ext/ca_obj_grid.c +181 -124
- data/ext/ca_obj_meld.c +235 -55
- data/ext/ca_obj_moncmp.c +61 -61
- data/ext/ca_obj_monop.c +125 -107
- data/ext/ca_obj_object.c +551 -233
- data/ext/ca_obj_record.c +51 -16
- data/ext/ca_obj_reduce.c +5 -8
- data/ext/ca_obj_refer.c +117 -33
- data/ext/ca_obj_remap.c +57 -17
- data/ext/ca_obj_repeat.c +21 -5
- data/ext/ca_obj_roll.c +115 -11
- data/ext/ca_obj_select.c +88 -75
- data/ext/ca_obj_select_axis.c +58 -60
- data/ext/ca_obj_shift.c +22 -102
- data/ext/ca_obj_stack.c +223 -123
- data/ext/ca_obj_stride.c +267 -35
- data/ext/ca_obj_string.c +25 -22
- data/ext/ca_obj_tile.c +32 -16
- data/ext/ca_obj_time.c +23 -25
- data/ext/ca_obj_timedelta.c +21 -23
- data/ext/ca_obj_transpose.c +12 -3
- data/ext/ca_obj_triop.c +110 -117
- data/ext/ca_obj_window.c +157 -29
- data/ext/ca_op_ipower.c +25 -13
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_rng_normal.h +42 -0
- data/ext/ca_rng_xoshiro256pp.h +105 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +385 -198
- data/ext/ca_sweep_engine.h +31 -23
- data/ext/ca_triop_dispatch.c +4 -1
- data/ext/ca_triop_dispatch.h +5 -2
- data/ext/caframe_csv_reader.c +463 -0
- data/ext/carray.h +90 -28
- data/ext/carray_access.c +487 -231
- data/ext/carray_address_basis.c +731 -0
- data/ext/carray_attribute.c +45 -3
- data/ext/carray_bincount.c +32 -2
- data/ext/carray_broadcast.c +49 -17
- data/ext/carray_call_cfunc.c +667 -511
- data/ext/carray_cast.c +1556 -247
- data/ext/carray_cast_func.rb +34 -21
- data/ext/carray_conversion.c +197 -73
- data/ext/carray_copy.c +84 -38
- data/ext/carray_core.c +591 -119
- data/ext/carray_count.c +148 -96
- data/ext/carray_element.c +18 -27
- data/ext/carray_factorize.c +604 -291
- data/ext/carray_generate.c +76 -40
- data/ext/carray_histogram.c +152 -94
- data/ext/carray_hold.c +2 -1
- data/ext/carray_index_classifier.c +63 -96
- data/ext/carray_internal.h +400 -1
- data/ext/carray_lazy.c +834 -79
- data/ext/carray_loop.c +88 -66
- data/ext/carray_mask.c +705 -324
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +263 -63
- data/ext/carray_memory_view.c +254 -248
- data/ext/carray_operator.c +1093 -363
- data/ext/carray_order.c +133 -69
- data/ext/carray_partition.c +102 -56
- data/ext/carray_random.c +582 -204
- data/ext/carray_scatter.c +308 -65
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +183 -70
- data/ext/carray_sort.c +235 -240
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +99 -21
- data/ext/carray_test.c +167 -64
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_utils.c +363 -98
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +103 -118
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1349 -851
- data/ext/ruby_carray.c +17 -2
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +39 -0
- data/lib/carray/autoload_carray.rb +24 -3
- data/lib/carray/autoload_method_extension.rb +18 -0
- data/lib/carray/axis_group.rb +245 -20
- data/lib/carray/basics.rb +95 -25
- data/lib/carray/bincount_nd.rb +6 -0
- data/lib/carray/block_iterator.rb +200 -54
- data/lib/carray/boolean_reduce.rb +21 -29
- data/lib/carray/categorical.rb +244 -59
- data/lib/carray/categorical_iterator.rb +391 -757
- data/lib/carray/complex.rb +36 -29
- data/lib/carray/conditional.rb +94 -7
- data/lib/carray/const_string.rb +177 -27
- data/lib/carray/construct.rb +40 -0
- data/lib/carray/data_type_extension.rb +3 -0
- data/lib/carray/data_type_limits.rb +91 -0
- data/lib/carray/fixlen_string.rb +1 -1
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +13 -7
- data/lib/carray/frame/csv_parser.rb +590 -76
- data/lib/carray/frame/describe.rb +79 -0
- data/lib/carray/frame/frame.rb +205 -35
- data/lib/carray/frame/group.rb +199 -40
- data/lib/carray/frame/io.rb +452 -60
- data/lib/carray/frame/join.rb +134 -46
- data/lib/carray/frame/records.rb +54 -19
- data/lib/carray/frame/reshape.rb +448 -0
- data/lib/carray/frame/sort.rb +26 -2
- data/lib/carray/frame/verbs.rb +726 -56
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +111 -2
- data/lib/carray/fusion.rb +229 -38
- data/lib/carray/inspect.rb +71 -20
- data/lib/carray/iterator.rb +157 -2
- data/lib/carray/lazy.rb +54 -67
- data/lib/carray/mask_gap_fill.rb +66 -50
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +40 -7
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bincount.rb +2 -2
- data/lib/carray/methods/bit_string.rb +14 -3
- data/lib/carray/methods/composition.rb +16 -10
- data/lib/carray/methods/discovery_along.rb +74 -0
- data/lib/carray/methods/factorize.rb +50 -0
- data/lib/carray/methods/gather_nd.rb +13 -2
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_in.rb +130 -6
- data/lib/carray/methods/join.rb +1 -1
- data/lib/carray/methods/locate_addr.rb +235 -21
- data/lib/carray/methods/mask_duplicates.rb +35 -1
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/nunique.rb +22 -1
- data/lib/carray/methods/repeat.rb +91 -0
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +52 -3
- data/lib/carray/record_order.rb +208 -0
- data/lib/carray/rng.rb +86 -0
- data/lib/carray/runtime.rb +14 -11
- data/lib/carray/segment_iterator.rb +990 -0
- data/lib/carray/serialize.rb +197 -27
- data/lib/carray/slab_iterator.rb +109 -40
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string_operation_extension.rb +9 -4
- data/lib/carray/struct.rb +142 -51
- data/lib/carray/struct_builder.rb +110 -14
- data/lib/carray/table.rb +2 -2
- data/lib/carray/time.rb +535 -105
- data/lib/carray/window_iterator.rb +259 -82
- data/lib/carray.rb +4 -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_block.rb +2 -7
- data/yard-stubs/ca_obj_fake.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +1 -1
- data/yard-stubs/ca_obj_refer.rb +3 -3
- data/yard-stubs/ca_obj_roll.rb +0 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +11 -4
- data/yard-stubs/carray_access.rb +6 -5
- data/yard-stubs/carray_broadcast.rb +2 -2
- data/yard-stubs/carray_cast.rb +78 -43
- data/yard-stubs/carray_conversion.rb +13 -1
- data/yard-stubs/carray_copy.rb +8 -3
- data/yard-stubs/carray_core.rb +3 -83
- data/yard-stubs/carray_count.rb +16 -2
- data/yard-stubs/carray_lazy.rb +210 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +34 -10
- data/yard-stubs/carray_math.rb +1499 -4
- data/yard-stubs/carray_median_percentile.rb +26 -8
- data/yard-stubs/carray_order.rb +43 -82
- data/yard-stubs/carray_scatter.rb +10 -3
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +11 -8
- data/yard-stubs/carray_sort.rb +50 -8
- data/yard-stubs/carray_test.rb +10 -0
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +19 -19
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_build_flags.h +0 -3
- data/ext/carray_config.h +0 -28
- data/ext/carray_kernels_bincmp.c +0 -4446
- data/ext/carray_kernels_binop.c +0 -11001
- data/ext/carray_kernels_init.c +0 -1131
- data/ext/carray_kernels_map.c +0 -3467
- data/ext/carray_kernels_moncmp.c +0 -2097
- data/ext/carray_kernels_monop.c +0 -18313
- data/ext/carray_kernels_reduce_aggregate.c +0 -25837
- data/ext/carray_kernels_reduce_boolean.c +0 -330
- data/ext/carray_kernels_reduce_cumulative.c +0 -14593
- data/ext/carray_kernels_reduce_extreme.c +0 -16948
- data/ext/carray_kernels_reduce_variance.c +0 -3910
- data/ext/carray_kernels_scan.c +0 -3693
- data/ext/carray_kernels_search.c +0 -32138
- data/ext/carray_kernels_sort.c +0 -10626
- data/ext/carray_kernels_triop.c +0 -1392
data/ext/carray_lazy.c
CHANGED
|
@@ -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;
|
|
@@ -153,6 +155,7 @@ static void
|
|
|
153
155
|
ca_lazy_marker_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
154
156
|
ca_size_t *strides, void *data, int dir)
|
|
155
157
|
{
|
|
158
|
+
if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
|
|
156
159
|
CALazyMarker *ca = (CALazyMarker *) ap;
|
|
157
160
|
ca_xfer_stride(ca->parent, starts, counts, strides, data, dir);
|
|
158
161
|
}
|
|
@@ -206,12 +209,8 @@ static void
|
|
|
206
209
|
ca_lazy_marker_func_create_mask (void *ap)
|
|
207
210
|
{
|
|
208
211
|
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);
|
|
212
|
+
ca_view_func_create_mask(ca);
|
|
213
|
+
ca_set_flag(ca->mask, CA_FLAG_READ_ONLY);
|
|
215
214
|
}
|
|
216
215
|
|
|
217
216
|
ca_operation_function_t ca_lazy_marker_func = {
|
|
@@ -250,10 +249,19 @@ rb_ca_lazy_marker_new (VALUE cary)
|
|
|
250
249
|
|
|
251
250
|
/* CArray#lazy — return a zero-cost CALazyMarker wrapping self. Subsequent
|
|
252
251
|
* element-wise ops (.sqrt / .sin / .+ / ...) build a lazy CAMonOp / CABinOp
|
|
253
|
-
* tree instead of evaluating eagerly; `.to_ca` materialises.
|
|
252
|
+
* tree instead of evaluating eagerly; `.to_ca` materialises.
|
|
253
|
+
*
|
|
254
|
+
* A Face returns itself: a lazy tree computes on storage and would lose
|
|
255
|
+
* the Face's meaning (units, labels, string surface), so its operations
|
|
256
|
+
* stay eager. */
|
|
254
257
|
VALUE
|
|
255
258
|
rb_ca_lazy (VALUE self)
|
|
256
259
|
{
|
|
260
|
+
CArray *ca;
|
|
261
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
262
|
+
if ( ca_is_face(ca) ) {
|
|
263
|
+
return self;
|
|
264
|
+
}
|
|
257
265
|
return rb_ca_lazy_marker_new(self);
|
|
258
266
|
}
|
|
259
267
|
|
|
@@ -292,8 +300,65 @@ typedef struct ca_gc_hold {
|
|
|
292
300
|
ca_size_t elements;
|
|
293
301
|
} ca_gc_hold_t;
|
|
294
302
|
|
|
295
|
-
|
|
296
|
-
|
|
303
|
+
/* Each Ractor keeps its own holds: walks in different Ractors push and
|
|
304
|
+
release at the same time. The GC marks every Ractor's set through
|
|
305
|
+
the storage type's mark function. */
|
|
306
|
+
typedef struct ca_gc_local {
|
|
307
|
+
ca_gc_hold_t holds[CA_GC_HOLD_MAX];
|
|
308
|
+
int hold_depth;
|
|
309
|
+
ca_gc_hold_t *buffers; /* held until released, in any order */
|
|
310
|
+
int buffer_n;
|
|
311
|
+
int buffer_cap;
|
|
312
|
+
} ca_gc_local_t;
|
|
313
|
+
|
|
314
|
+
static rb_ractor_local_key_t ca_gc_local_key;
|
|
315
|
+
|
|
316
|
+
static void
|
|
317
|
+
ca_gc_local_mark (void *ptr)
|
|
318
|
+
{
|
|
319
|
+
ca_gc_local_t *g = (ca_gc_local_t *) ptr;
|
|
320
|
+
int i;
|
|
321
|
+
if ( g == NULL ) return;
|
|
322
|
+
for ( i = 0; i < g->hold_depth; i++ ) {
|
|
323
|
+
VALUE *p = g->holds[i].ptr;
|
|
324
|
+
ca_size_t n = g->holds[i].elements;
|
|
325
|
+
while ( n-- ) rb_gc_mark(*p++);
|
|
326
|
+
}
|
|
327
|
+
for ( i = 0; i < g->buffer_n; i++ ) {
|
|
328
|
+
VALUE *p = g->buffers[i].ptr;
|
|
329
|
+
ca_size_t n = g->buffers[i].elements;
|
|
330
|
+
while ( n-- ) rb_gc_mark(*p++);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
static void
|
|
335
|
+
ca_gc_local_free (void *ptr)
|
|
336
|
+
{
|
|
337
|
+
ca_gc_local_t *g = (ca_gc_local_t *) ptr;
|
|
338
|
+
if ( g == NULL ) return;
|
|
339
|
+
free(g->buffers);
|
|
340
|
+
free(g);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
static const struct rb_ractor_local_storage_type ca_gc_local_type = {
|
|
344
|
+
ca_gc_local_mark, ca_gc_local_free
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
/* The state is allocated with the C allocator, like the table it grows:
|
|
348
|
+
a collection must not run while either is half set up. */
|
|
349
|
+
static ca_gc_local_t *
|
|
350
|
+
ca_gc_local (void)
|
|
351
|
+
{
|
|
352
|
+
ca_gc_local_t *g = (ca_gc_local_t *) rb_ractor_local_storage_ptr(ca_gc_local_key);
|
|
353
|
+
if ( g == NULL ) {
|
|
354
|
+
g = (ca_gc_local_t *) calloc(1, sizeof(ca_gc_local_t));
|
|
355
|
+
if ( g == NULL ) {
|
|
356
|
+
rb_memerror();
|
|
357
|
+
}
|
|
358
|
+
rb_ractor_local_storage_ptr_set(ca_gc_local_key, g);
|
|
359
|
+
}
|
|
360
|
+
return g;
|
|
361
|
+
}
|
|
297
362
|
|
|
298
363
|
static void ca_lazy_arena_mark_object_slots (void);
|
|
299
364
|
|
|
@@ -306,22 +371,69 @@ static void ca_lazy_arena_mark_object_slots (void);
|
|
|
306
371
|
int
|
|
307
372
|
ca_gc_hold_push (void *ptr, ca_size_t n_elements)
|
|
308
373
|
{
|
|
309
|
-
|
|
374
|
+
ca_gc_local_t *g;
|
|
375
|
+
int depth;
|
|
310
376
|
|
|
311
|
-
if (
|
|
377
|
+
if ( n_elements <= 0 || ptr == NULL ) {
|
|
378
|
+
return -1;
|
|
379
|
+
}
|
|
380
|
+
g = ca_gc_local();
|
|
381
|
+
depth = g->hold_depth;
|
|
382
|
+
if ( depth >= CA_GC_HOLD_MAX ) {
|
|
312
383
|
return -1;
|
|
313
384
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
385
|
+
g->holds[depth].ptr = (VALUE *) ptr;
|
|
386
|
+
g->holds[depth].elements = n_elements;
|
|
387
|
+
g->hold_depth = depth + 1;
|
|
317
388
|
return depth;
|
|
318
389
|
}
|
|
319
390
|
|
|
320
391
|
void
|
|
321
392
|
ca_gc_hold_pop_to (int depth)
|
|
322
393
|
{
|
|
323
|
-
|
|
324
|
-
|
|
394
|
+
ca_gc_local_t *g;
|
|
395
|
+
if ( depth < 0 ) return;
|
|
396
|
+
g = ca_gc_local();
|
|
397
|
+
if ( depth < g->hold_depth ) {
|
|
398
|
+
g->hold_depth = depth;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
void
|
|
403
|
+
ca_gc_hold_buffer (void *ptr, ca_size_t n_elements)
|
|
404
|
+
{
|
|
405
|
+
ca_gc_local_t *g;
|
|
406
|
+
if ( ptr == NULL || n_elements <= 0 ) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
g = ca_gc_local();
|
|
410
|
+
if ( g->buffer_n == g->buffer_cap ) {
|
|
411
|
+
int cap = g->buffer_cap ? g->buffer_cap * 2 : 16;
|
|
412
|
+
ca_gc_hold_t *grown = (ca_gc_hold_t *) realloc(g->buffers,
|
|
413
|
+
cap * sizeof(ca_gc_hold_t));
|
|
414
|
+
if ( grown == NULL ) {
|
|
415
|
+
rb_memerror();
|
|
416
|
+
}
|
|
417
|
+
g->buffers = grown;
|
|
418
|
+
g->buffer_cap = cap;
|
|
419
|
+
}
|
|
420
|
+
g->buffers[g->buffer_n].ptr = (VALUE *) ptr;
|
|
421
|
+
g->buffers[g->buffer_n].elements = n_elements;
|
|
422
|
+
g->buffer_n++;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
void
|
|
426
|
+
ca_gc_release_buffer (void *ptr)
|
|
427
|
+
{
|
|
428
|
+
ca_gc_local_t *g = (ca_gc_local_t *) rb_ractor_local_storage_ptr(ca_gc_local_key);
|
|
429
|
+
int i;
|
|
430
|
+
if ( g == NULL ) return;
|
|
431
|
+
for ( i = g->buffer_n - 1; i >= 0; i-- ) {
|
|
432
|
+
if ( g->buffers[i].ptr == (VALUE *) ptr ) {
|
|
433
|
+
g->buffers[i] = g->buffers[g->buffer_n - 1];
|
|
434
|
+
g->buffer_n--;
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
325
437
|
}
|
|
326
438
|
}
|
|
327
439
|
|
|
@@ -331,16 +443,11 @@ static VALUE ca_gc_guard = Qnil;
|
|
|
331
443
|
function of a TypedData whose data pointer is NULL. */
|
|
332
444
|
static int ca_gc_guard_body = 0;
|
|
333
445
|
|
|
446
|
+
/* The arena's object slots. The holds are marked per Ractor above. */
|
|
334
447
|
static void
|
|
335
448
|
ca_gc_guard_mark (void *ptr)
|
|
336
449
|
{
|
|
337
|
-
int i;
|
|
338
450
|
(void) ptr;
|
|
339
|
-
for ( i = 0; i < ca_gc_hold_depth; i++ ) {
|
|
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++);
|
|
343
|
-
}
|
|
344
451
|
ca_lazy_arena_mark_object_slots();
|
|
345
452
|
}
|
|
346
453
|
|
|
@@ -374,11 +481,12 @@ static const rb_data_type_t ca_gc_guard_data_type = {
|
|
|
374
481
|
best-fit allocation keeps small requests (mask scratch) out of
|
|
375
482
|
large data slots
|
|
376
483
|
|
|
377
|
-
Footprint:
|
|
378
|
-
e.g. N=1M f64, depth-8 chain =
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
484
|
+
Footprint: one slot per scratch held at the same time, each as large
|
|
485
|
+
as the largest request it has served (e.g. N=1M f64, depth-8 chain =
|
|
486
|
+
8 × 8MB = 64MB kept resident). A request no free slot can hold grows
|
|
487
|
+
a free slot; a new slot opens only while every warm one is in use.
|
|
488
|
+
When all 32 are in use the acquire raises: this signals a programming
|
|
489
|
+
error or a pathological chain rather than a graceful fallback.
|
|
382
490
|
|
|
383
491
|
CAREFUL: single-thread only -- thread-safety across concurrent access
|
|
384
492
|
is not a goal (see guides/devel/04_attach_lifecycle.md). The arena is
|
|
@@ -437,6 +545,57 @@ ca_lazy_arena_exit (void)
|
|
|
437
545
|
from being collected in the first place. */
|
|
438
546
|
}
|
|
439
547
|
|
|
548
|
+
/* Runs body(arg) inside an enter / exit bracket that closes however body
|
|
549
|
+
leaves. cleanup(arg), when given, runs first -- the place to release
|
|
550
|
+
the scratches and detach the arrays body acquired -- and the exit runs
|
|
551
|
+
after it.
|
|
552
|
+
|
|
553
|
+
A raise that skipped the exit would leave the depth above 0 for the
|
|
554
|
+
rest of the process: the depth-0 reset that recovers abandoned slots
|
|
555
|
+
would never fire again, and once 32 slots were abandoned every lazy
|
|
556
|
+
expression would fail. */
|
|
557
|
+
|
|
558
|
+
typedef struct {
|
|
559
|
+
VALUE (*cleanup)(VALUE);
|
|
560
|
+
VALUE arg;
|
|
561
|
+
} ca_lazy_arena_ensure_t;
|
|
562
|
+
|
|
563
|
+
static VALUE
|
|
564
|
+
ca_lazy_arena_ensure (VALUE varg)
|
|
565
|
+
{
|
|
566
|
+
ca_lazy_arena_ensure_t *e = (ca_lazy_arena_ensure_t *) varg;
|
|
567
|
+
if ( e->cleanup ) {
|
|
568
|
+
e->cleanup(e->arg);
|
|
569
|
+
}
|
|
570
|
+
ca_lazy_arena_exit();
|
|
571
|
+
return Qnil;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
VALUE
|
|
575
|
+
ca_lazy_arena_protect (VALUE (*body)(VALUE), VALUE (*cleanup)(VALUE),
|
|
576
|
+
VALUE arg)
|
|
577
|
+
{
|
|
578
|
+
ca_lazy_arena_ensure_t e;
|
|
579
|
+
e.cleanup = cleanup;
|
|
580
|
+
e.arg = arg;
|
|
581
|
+
ca_lazy_arena_enter();
|
|
582
|
+
return rb_ensure(body, arg, ca_lazy_arena_ensure, (VALUE) &e);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/* A cleanup for ca_lazy_arena_protect when body holds one scratch: arg
|
|
586
|
+
points at a struct whose first member is that scratch (NULL until it
|
|
587
|
+
is acquired). */
|
|
588
|
+
VALUE
|
|
589
|
+
ca_lazy_arena_release_held (VALUE arg)
|
|
590
|
+
{
|
|
591
|
+
void **held = (void **) arg;
|
|
592
|
+
if ( *held ) {
|
|
593
|
+
ca_lazy_arena_release(*held);
|
|
594
|
+
*held = NULL;
|
|
595
|
+
}
|
|
596
|
+
return Qnil;
|
|
597
|
+
}
|
|
598
|
+
|
|
440
599
|
void *
|
|
441
600
|
ca_lazy_arena_acquire (ca_size_t bytes)
|
|
442
601
|
{
|
|
@@ -471,7 +630,40 @@ ca_lazy_arena_acquire (ca_size_t bytes)
|
|
|
471
630
|
}
|
|
472
631
|
|
|
473
632
|
|
|
474
|
-
/* Pass 2:
|
|
633
|
+
/* Pass 2: every free warm slot is too small. Grow the largest of
|
|
634
|
+
them rather than open another slot: a slot that is free now was not
|
|
635
|
+
needed at the same time as this request, so the arena holds one
|
|
636
|
+
buffer per simultaneous acquire rather than one per size ever asked
|
|
637
|
+
for. Taking the largest leaves the small slots to the small
|
|
638
|
+
requests (mask scratch next to data scratch).
|
|
639
|
+
|
|
640
|
+
The slot is emptied before the xmalloc so that a NoMemoryError
|
|
641
|
+
leaves it unallocated instead of pointing at freed memory. */
|
|
642
|
+
best = -1;
|
|
643
|
+
best_bytes = -1;
|
|
644
|
+
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
645
|
+
ca_lazy_arena_slot_t *s = &ca_lazy_arena.slots[i];
|
|
646
|
+
if ( s->in_use || s->ptr == NULL ) continue;
|
|
647
|
+
if ( s->bytes > best_bytes ) {
|
|
648
|
+
best = i;
|
|
649
|
+
best_bytes = s->bytes;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if ( best >= 0 ) {
|
|
653
|
+
ca_lazy_arena_slot_t *s = &ca_lazy_arena.slots[best];
|
|
654
|
+
void *old = s->ptr;
|
|
655
|
+
s->ptr = NULL;
|
|
656
|
+
s->bytes = 0;
|
|
657
|
+
xfree(old);
|
|
658
|
+
s->ptr = xmalloc(bytes);
|
|
659
|
+
s->bytes = bytes;
|
|
660
|
+
s->in_use = 1;
|
|
661
|
+
s->object_elements = 0;
|
|
662
|
+
ca_lazy_arena.debug_xmalloc_count++;
|
|
663
|
+
return s->ptr;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/* Pass 3: every warm slot is in use. Open an empty one. */
|
|
475
667
|
empty = -1;
|
|
476
668
|
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
477
669
|
if ( ca_lazy_arena.slots[i].ptr == NULL ) {
|
|
@@ -488,26 +680,11 @@ ca_lazy_arena_acquire (ca_size_t bytes)
|
|
|
488
680
|
return ca_lazy_arena.slots[empty].ptr;
|
|
489
681
|
}
|
|
490
682
|
|
|
491
|
-
/* Pass 3: all slots have ptrs. Find a non-in-use slot and grow it
|
|
492
|
-
(= xfree + xmalloc). This pays a mmap roundtrip but only on
|
|
493
|
-
slot pool exhaustion (= unusual size request). */
|
|
494
|
-
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
495
|
-
if ( ! ca_lazy_arena.slots[i].in_use ) {
|
|
496
|
-
xfree(ca_lazy_arena.slots[i].ptr);
|
|
497
|
-
ca_lazy_arena.slots[i].ptr = xmalloc(bytes);
|
|
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;
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
683
|
/* Pass 4: all CA_LAZY_ARENA_SLOTS in use simultaneously. This is
|
|
507
684
|
a programming error or a pathological chain. Raise. */
|
|
508
|
-
rb_raise(
|
|
509
|
-
"
|
|
510
|
-
"
|
|
685
|
+
rb_raise(rb_eArgError,
|
|
686
|
+
"lazy expression too deep: it needs more than %d intermediate "
|
|
687
|
+
"arrays at once",
|
|
511
688
|
CA_LAZY_ARENA_SLOTS);
|
|
512
689
|
return NULL; /* unreachable */
|
|
513
690
|
}
|
|
@@ -642,38 +819,6 @@ rb_ca_lazy_arena_s_slot_capacities (VALUE klass)
|
|
|
642
819
|
|
|
643
820
|
/* ------------------------------------------------------------------- */
|
|
644
821
|
|
|
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
822
|
static VALUE
|
|
678
823
|
rb_ca_lazy_marker_s_allocate (VALUE klass)
|
|
679
824
|
{
|
|
@@ -687,11 +832,14 @@ rb_ca_lazy_marker_initialize_copy (VALUE self, VALUE other)
|
|
|
687
832
|
{
|
|
688
833
|
CALazyMarker *ca, *cs;
|
|
689
834
|
TypedData_Get_Struct(self, CALazyMarker, &calazy_marker_data_type, ca);
|
|
835
|
+
ca_check_uninitialized(ca);
|
|
690
836
|
TypedData_Get_Struct(other, CALazyMarker, &calazy_marker_data_type, cs);
|
|
691
837
|
ca_lazy_marker_setup(ca, cs->parent);
|
|
692
838
|
return self;
|
|
693
839
|
}
|
|
694
840
|
|
|
841
|
+
static void Init_ca_mask_of_operands (void);
|
|
842
|
+
|
|
695
843
|
void
|
|
696
844
|
Init_carray_lazy (void)
|
|
697
845
|
{
|
|
@@ -707,8 +855,10 @@ Init_carray_lazy (void)
|
|
|
707
855
|
|
|
708
856
|
rb_define_method(rb_cCArray, "lazy", rb_ca_lazy, 0);
|
|
709
857
|
|
|
858
|
+
ca_gc_local_key = rb_ractor_local_storage_ptr_newkey(&ca_gc_local_type);
|
|
859
|
+
|
|
710
860
|
/* Hidden, never collected: its mark function is what keeps the
|
|
711
|
-
|
|
861
|
+
arena's CA_OBJECT slots reachable. */
|
|
712
862
|
ca_gc_guard = TypedData_Wrap_Struct(rb_cObject, &ca_gc_guard_data_type,
|
|
713
863
|
&ca_gc_guard_body);
|
|
714
864
|
rb_gc_register_mark_object(ca_gc_guard);
|
|
@@ -734,4 +884,609 @@ Init_carray_lazy (void)
|
|
|
734
884
|
rb_ca_lazy_arena_s_slot_in_use_count, 0);
|
|
735
885
|
rb_define_singleton_method(rb_cCArray, "__lazy_arena_slot_capacities__",
|
|
736
886
|
rb_ca_lazy_arena_s_slot_capacities, 0);
|
|
887
|
+
|
|
888
|
+
Init_ca_mask_of_operands();
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/* ---------------------------------------------------------------------------
|
|
892
|
+
Requests to the element-wise lazy views (ca_lazy_req_t, carray_internal.h).
|
|
893
|
+
|
|
894
|
+
An operand is pulled over the cells the request names. In the region
|
|
895
|
+
form the strides address the view's cells, so they are restated at the
|
|
896
|
+
operand's cell width: strides[k] / bytes is the index step the two share.
|
|
897
|
+
(A packed stride over counts is not a substitute -- it walks cells 0, 1,
|
|
898
|
+
2 where the request asked for 0, 2, 4.)
|
|
899
|
+
--------------------------------------------------------------------------- */
|
|
900
|
+
|
|
901
|
+
void
|
|
902
|
+
ca_lazy_req_region (ca_lazy_req_t *req, void *view, ca_size_t *starts,
|
|
903
|
+
ca_size_t *counts, ca_size_t *strides)
|
|
904
|
+
{
|
|
905
|
+
CArray *ca = (CArray *) view;
|
|
906
|
+
int8_t k;
|
|
907
|
+
req->n = 1;
|
|
908
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
909
|
+
req->n *= counts[k];
|
|
910
|
+
}
|
|
911
|
+
req->addrs = NULL;
|
|
912
|
+
req->starts = starts;
|
|
913
|
+
req->counts = counts;
|
|
914
|
+
req->strides = strides;
|
|
915
|
+
req->bytes = ca->bytes;
|
|
916
|
+
req->ndim = ca->ndim;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
void
|
|
920
|
+
ca_lazy_req_addrs (ca_lazy_req_t *req, ca_size_t n, ca_size_t *addrs)
|
|
921
|
+
{
|
|
922
|
+
req->n = n;
|
|
923
|
+
req->addrs = addrs;
|
|
924
|
+
req->starts = NULL;
|
|
925
|
+
req->counts = NULL;
|
|
926
|
+
req->strides = NULL;
|
|
927
|
+
req->bytes = 0;
|
|
928
|
+
req->ndim = 0;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/* Whether the request names one contiguous run of the view's cells, in
|
|
932
|
+
the view's own row-major order -- the case in which an operand's buffer
|
|
933
|
+
already holds the cells in the order the kernel reads them. The strides
|
|
934
|
+
must be the native row-major ones, and the box must be whole on every
|
|
935
|
+
axis inside the outermost one it does not span with a count of 1. */
|
|
936
|
+
int
|
|
937
|
+
ca_lazy_req_is_packed (const ca_lazy_req_t *req, void *view)
|
|
938
|
+
{
|
|
939
|
+
CArray *ca = (CArray *) view;
|
|
940
|
+
ca_size_t native = ca->bytes;
|
|
941
|
+
int partial = 0;
|
|
942
|
+
int8_t k;
|
|
943
|
+
if ( req->addrs ) {
|
|
944
|
+
return 0;
|
|
945
|
+
}
|
|
946
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) {
|
|
947
|
+
if ( req->strides[k] != native ) {
|
|
948
|
+
return 0;
|
|
949
|
+
}
|
|
950
|
+
if ( partial && req->counts[k] != 1 ) {
|
|
951
|
+
return 0;
|
|
952
|
+
}
|
|
953
|
+
if ( req->counts[k] != ca->dim[k] ) {
|
|
954
|
+
partial = 1;
|
|
955
|
+
}
|
|
956
|
+
native *= ca->dim[k];
|
|
957
|
+
}
|
|
958
|
+
return 1;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/* The cells of a request that any of `ops` masks, as a packed boolean
|
|
962
|
+
slab in arena scratch, or NULL when none of them is masked. An operand
|
|
963
|
+
of one cell masks every cell or none. Kernels that can raise on a cell,
|
|
964
|
+
or that call Ruby for it, take this to skip the masked cells, as the
|
|
965
|
+
eager operators do. Release with ca_lazy_arena_release. */
|
|
966
|
+
boolean8_t *
|
|
967
|
+
ca_lazy_req_mask (const ca_lazy_req_t *req, int n, CArray **ops)
|
|
968
|
+
{
|
|
969
|
+
boolean8_t *m = NULL;
|
|
970
|
+
ca_size_t j;
|
|
971
|
+
int i;
|
|
972
|
+
int8_t k;
|
|
973
|
+
|
|
974
|
+
for ( i = 0; i < n; i++ ) {
|
|
975
|
+
CArray *op = ops[i];
|
|
976
|
+
if ( ! op || ! ca_has_mask(op) ) {
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
if ( ! m ) {
|
|
980
|
+
m = (boolean8_t *) ca_lazy_arena_acquire(req->n);
|
|
981
|
+
memset(m, 0, req->n);
|
|
982
|
+
}
|
|
983
|
+
if ( op->elements == 1 ) {
|
|
984
|
+
ca_size_t starts[CA_RANK_MAX], counts[CA_RANK_MAX];
|
|
985
|
+
ca_size_t strides[CA_RANK_MAX];
|
|
986
|
+
boolean8_t bit = 0;
|
|
987
|
+
for ( k = 0; k < op->ndim; k++ ) {
|
|
988
|
+
starts[k] = 0; counts[k] = 1; strides[k] = 1;
|
|
989
|
+
}
|
|
990
|
+
ca_xfer_stride(op->mask, starts, counts, strides, &bit, CA_XFER_GET);
|
|
991
|
+
if ( bit ) {
|
|
992
|
+
memset(m, 1, req->n);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
else {
|
|
996
|
+
volatile VALUE holder;
|
|
997
|
+
boolean8_t *om = ALLOCV_N(boolean8_t, holder, req->n);
|
|
998
|
+
ca_lazy_req_pull(op->mask, req, om, CA_XFER_GET);
|
|
999
|
+
for ( j = 0; j < req->n; j++ ) {
|
|
1000
|
+
m[j] |= om[j];
|
|
1001
|
+
}
|
|
1002
|
+
ALLOCV_END(holder);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
return m;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/* A scalar operand (a Ruby value or a CScalar) that the eager promotion
|
|
1009
|
+
has cast to the array's data_type comes back as a cast view over a
|
|
1010
|
+
CScalar. Fix it as the CScalar the cast gives, so the expression holds
|
|
1011
|
+
a plain value leaf. */
|
|
1012
|
+
VALUE
|
|
1013
|
+
ca_lazy_settle_scalar (VALUE operand, int was_scalar)
|
|
1014
|
+
{
|
|
1015
|
+
CArray *ca;
|
|
1016
|
+
if ( ! was_scalar ) {
|
|
1017
|
+
return operand;
|
|
1018
|
+
}
|
|
1019
|
+
/* rb_obj_is_cscalar is true for any view carrying the scalar flag, the
|
|
1020
|
+
cast view included; the obj_type says whether it is a CScalar. */
|
|
1021
|
+
TypedData_Get_Struct(operand, CArray, &carray_data_type, ca);
|
|
1022
|
+
if ( ca->obj_type != CA_OBJ_SCALAR ) {
|
|
1023
|
+
return rb_ca_copy(operand);
|
|
1024
|
+
}
|
|
1025
|
+
return operand;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
void
|
|
1029
|
+
ca_lazy_req_pull (void *operand, const ca_lazy_req_t *req, void *buf, int dir)
|
|
1030
|
+
{
|
|
1031
|
+
CArray *op = (CArray *) operand;
|
|
1032
|
+
ca_size_t strides[CA_RANK_MAX];
|
|
1033
|
+
int8_t k;
|
|
1034
|
+
|
|
1035
|
+
if ( req->addrs ) {
|
|
1036
|
+
ca_xfer_addrs(op, req->n, req->addrs, buf, dir);
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
for ( k = 0; k < req->ndim; k++ ) {
|
|
1040
|
+
strides[k] = req->strides[k] / req->bytes * op->bytes;
|
|
1041
|
+
}
|
|
1042
|
+
ca_xfer_stride(op, req->starts, req->counts, strides, buf, dir);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
/* An expression the caller is about to compute whole -- a reduction along
|
|
1046
|
+
an axis, a sort, a median -- computed by the registered expression
|
|
1047
|
+
evaluator instead, when there is one and it takes the expression.
|
|
1048
|
+
Returns the array it made, or Qnil where nothing is registered, self is
|
|
1049
|
+
not a lazy expression, or the evaluator declined (CArray::Fusion.evaluate
|
|
1050
|
+
decides, and asks only above its size threshold). The caller goes on
|
|
1051
|
+
with that array in place of self, or with self as before.
|
|
1052
|
+
|
|
1053
|
+
Only where the expression would be made whole anyway: a reduction that
|
|
1054
|
+
streams the expression in chunks keeps doing so, since asking here would
|
|
1055
|
+
make it whole and take that much more memory. */
|
|
1056
|
+
VALUE
|
|
1057
|
+
ca_lazy_evaluated (VALUE self)
|
|
1058
|
+
{
|
|
1059
|
+
static ID id_evaluator = 0, id_fusion, id_evaluate;
|
|
1060
|
+
CArray *ca;
|
|
1061
|
+
if ( ! id_evaluator ) {
|
|
1062
|
+
id_evaluator = rb_intern("@expression_evaluator");
|
|
1063
|
+
id_fusion = rb_intern("Fusion");
|
|
1064
|
+
id_evaluate = rb_intern("evaluate");
|
|
1065
|
+
}
|
|
1066
|
+
GetCArray(self, ca);
|
|
1067
|
+
if ( ! ca_is_lazy_view(ca) ) {
|
|
1068
|
+
return Qnil;
|
|
1069
|
+
}
|
|
1070
|
+
if ( ! RTEST(rb_attr_get(rb_cCArray, id_evaluator)) ) {
|
|
1071
|
+
return Qnil;
|
|
1072
|
+
}
|
|
1073
|
+
return rb_funcall(rb_const_get(rb_cCArray, id_fusion), id_evaluate, 1, self);
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
/* ---------------------------------------------------------------------------
|
|
1077
|
+
CAMaskOfOperands: the mask of an element-wise lazy operation over several
|
|
1078
|
+
operands (CABinOp, CABinCmp, CATriOp).
|
|
1079
|
+
|
|
1080
|
+
The operation computes its values each time it is read, so its mask is
|
|
1081
|
+
computed the same way: a request pulls each operand's mask over the cells
|
|
1082
|
+
it names and ORs them. An operand with no mask reads as all false, so an
|
|
1083
|
+
operand that is given a mask after the expression was first read is seen
|
|
1084
|
+
on the next read -- and reading the mask never gives an operand one.
|
|
1085
|
+
|
|
1086
|
+
For the boolean `|` and `&` (three-valued) a masked cell is not masked in
|
|
1087
|
+
the result when an unmasked operand already decides it: true for `|`,
|
|
1088
|
+
false for `&`. The operands' values are pulled for that.
|
|
1089
|
+
|
|
1090
|
+
The view is read-only and has no mask of its own (CA_FLAG_VALUE_ARRAY),
|
|
1091
|
+
so nothing above it asks for one. Its parent is the operation, which
|
|
1092
|
+
owns it as its mask; the operands are read through the operation's
|
|
1093
|
+
parents[].
|
|
1094
|
+
--------------------------------------------------------------------------- */
|
|
1095
|
+
|
|
1096
|
+
int8_t CA_OBJ_MASK_OF_OPERANDS;
|
|
1097
|
+
VALUE rb_cCAMaskOfOperands;
|
|
1098
|
+
|
|
1099
|
+
typedef struct CAMaskOfOperands {
|
|
1100
|
+
int16_t obj_type;
|
|
1101
|
+
int8_t data_type;
|
|
1102
|
+
int8_t ndim;
|
|
1103
|
+
int32_t flags;
|
|
1104
|
+
ca_size_t bytes;
|
|
1105
|
+
ca_size_t elements;
|
|
1106
|
+
ca_size_t *dim;
|
|
1107
|
+
char *ptr;
|
|
1108
|
+
CArray *mask;
|
|
1109
|
+
char *_pool;
|
|
1110
|
+
CArray *parent;
|
|
1111
|
+
uint32_t attach;
|
|
1112
|
+
uint8_t nosync;
|
|
1113
|
+
int8_t mode; /* CA_LAZY_MASK_OR / _KLEENE_OR / _KLEENE_AND / _SELECT */
|
|
1114
|
+
} CAMaskOfOperands;
|
|
1115
|
+
|
|
1116
|
+
static size_t
|
|
1117
|
+
ca_mask_of_operands_dsize (const void *ap)
|
|
1118
|
+
{
|
|
1119
|
+
const CAMaskOfOperands *ca = (const CAMaskOfOperands *) ap;
|
|
1120
|
+
return sizeof(CAMaskOfOperands) + ca->ndim * sizeof(ca_size_t);
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
const rb_data_type_t camask_of_operands_data_type = {
|
|
1124
|
+
.parent = &caview_data_type,
|
|
1125
|
+
.wrap_struct_name = "CAMaskOfOperands",
|
|
1126
|
+
.function = {
|
|
1127
|
+
.dmark = ca_mark,
|
|
1128
|
+
.dfree = ca_free,
|
|
1129
|
+
.dsize = ca_mask_of_operands_dsize,
|
|
1130
|
+
.dcompact = NULL
|
|
1131
|
+
},
|
|
1132
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
1133
|
+
};
|
|
1134
|
+
|
|
1135
|
+
CArray *
|
|
1136
|
+
ca_mask_of_operands_new (CArray *operation, int mode)
|
|
1137
|
+
{
|
|
1138
|
+
CAMaskOfOperands *ca = ALLOC(CAMaskOfOperands);
|
|
1139
|
+
ca->_pool = NULL;
|
|
1140
|
+
ca->obj_type = CA_OBJ_MASK_OF_OPERANDS;
|
|
1141
|
+
ca->data_type = CA_BOOLEAN;
|
|
1142
|
+
ca->flags = CA_FLAG_READ_ONLY | CA_FLAG_VALUE_ARRAY;
|
|
1143
|
+
ca->ndim = operation->ndim;
|
|
1144
|
+
ca->bytes = 1;
|
|
1145
|
+
ca->elements = operation->elements;
|
|
1146
|
+
ca->dim = ALLOC_N(ca_size_t, operation->ndim);
|
|
1147
|
+
ca->ptr = NULL;
|
|
1148
|
+
ca->mask = NULL;
|
|
1149
|
+
ca->parent = operation;
|
|
1150
|
+
ca->attach = 0;
|
|
1151
|
+
ca->nosync = 0;
|
|
1152
|
+
ca->mode = (int8_t) mode;
|
|
1153
|
+
memcpy(ca->dim, operation->dim, operation->ndim * sizeof(ca_size_t));
|
|
1154
|
+
if ( ca_is_scalar(operation) ) {
|
|
1155
|
+
ca_set_flag(ca, CA_FLAG_SCALAR);
|
|
1156
|
+
}
|
|
1157
|
+
return (CArray *) ca;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
static void
|
|
1161
|
+
free_ca_mask_of_operands (void *ap)
|
|
1162
|
+
{
|
|
1163
|
+
CAMaskOfOperands *ca = (CAMaskOfOperands *) ap;
|
|
1164
|
+
if ( ca != NULL ) {
|
|
1165
|
+
xfree(ca->ptr);
|
|
1166
|
+
xfree(ca->dim);
|
|
1167
|
+
xfree(ca);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
static void *
|
|
1172
|
+
ca_mask_of_operands_func_clone (void *ap)
|
|
1173
|
+
{
|
|
1174
|
+
CAMaskOfOperands *ca = (CAMaskOfOperands *) ap;
|
|
1175
|
+
return ca_mask_of_operands_new(ca->parent, ca->mode);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
/* An operand of one cell beside a larger operation is broadcast: its one
|
|
1179
|
+
cell stands for every cell of the request. */
|
|
1180
|
+
static int
|
|
1181
|
+
ca_mask_of_operands_operand_is_scalar (CAMaskOfOperands *lm, CArray *op)
|
|
1182
|
+
{
|
|
1183
|
+
return ( op->elements == 1 && lm->elements > 1 );
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
static void
|
|
1187
|
+
ca_mask_of_operands_pull (CAMaskOfOperands *lm, CArray *src, const ca_lazy_req_t *req,
|
|
1188
|
+
boolean8_t *buf)
|
|
1189
|
+
{
|
|
1190
|
+
if ( ca_mask_of_operands_operand_is_scalar(lm, src) ) {
|
|
1191
|
+
boolean8_t one;
|
|
1192
|
+
ca_xfer_all(src, &one, CA_XFER_GET);
|
|
1193
|
+
memset(buf, one ? 1 : 0, req->n);
|
|
1194
|
+
}
|
|
1195
|
+
else {
|
|
1196
|
+
ca_lazy_req_pull(src, req, buf, CA_XFER_GET);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/* select: masked where the condition is, else where the branch it
|
|
1201
|
+
chooses is. The condition arrives converted to the branches' type
|
|
1202
|
+
from boolean, so a cell is true exactly when one of its bytes is not
|
|
1203
|
+
zero. */
|
|
1204
|
+
static void
|
|
1205
|
+
ca_mask_of_operands_eval_select (CAMaskOfOperands *lm, const ca_lazy_req_t *req,
|
|
1206
|
+
boolean8_t *dst)
|
|
1207
|
+
{
|
|
1208
|
+
CAMultiParent *mp = (CAMultiParent *) lm->parent;
|
|
1209
|
+
CArray *cond = mp->parents[0];
|
|
1210
|
+
ca_size_t bytes = cond->bytes;
|
|
1211
|
+
ca_size_t n = req->n, i, b;
|
|
1212
|
+
volatile VALUE h_value, h_m1, h_m2;
|
|
1213
|
+
boolean8_t *m1, *m2;
|
|
1214
|
+
char *v;
|
|
1215
|
+
int32_t k;
|
|
1216
|
+
|
|
1217
|
+
memset(dst, 0, n);
|
|
1218
|
+
if ( ca_has_mask(cond) ) {
|
|
1219
|
+
ca_mask_of_operands_pull(lm, cond->mask, req, dst);
|
|
1220
|
+
}
|
|
1221
|
+
m1 = ALLOCV_N(boolean8_t, h_m1, n);
|
|
1222
|
+
m2 = ALLOCV_N(boolean8_t, h_m2, n);
|
|
1223
|
+
for ( k = 1; k <= 2; k++ ) {
|
|
1224
|
+
CArray *op = mp->parents[k];
|
|
1225
|
+
boolean8_t *m = ( k == 1 ) ? m1 : m2;
|
|
1226
|
+
if ( ca_has_mask(op) ) {
|
|
1227
|
+
ca_mask_of_operands_pull(lm, op->mask, req, m);
|
|
1228
|
+
}
|
|
1229
|
+
else {
|
|
1230
|
+
memset(m, 0, n);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
if ( ca_mask_of_operands_operand_is_scalar(lm, cond) ) {
|
|
1234
|
+
v = ALLOCV_N(char, h_value, bytes);
|
|
1235
|
+
ca_xfer_all(cond, v, CA_XFER_GET);
|
|
1236
|
+
for ( b = 0; b < bytes && ! v[b]; b++ ) ;
|
|
1237
|
+
for ( i = 0; i < n; i++ ) dst[i] |= ( b < bytes ) ? m1[i] : m2[i];
|
|
1238
|
+
}
|
|
1239
|
+
else {
|
|
1240
|
+
v = ALLOCV_N(char, h_value, n * bytes);
|
|
1241
|
+
ca_lazy_req_pull(cond, req, v, CA_XFER_GET);
|
|
1242
|
+
for ( i = 0; i < n; i++ ) {
|
|
1243
|
+
const char *c = v + i * bytes;
|
|
1244
|
+
for ( b = 0; b < bytes && ! c[b]; b++ ) ;
|
|
1245
|
+
dst[i] |= ( b < bytes ) ? m1[i] : m2[i];
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
ALLOCV_END(h_value);
|
|
1249
|
+
ALLOCV_END(h_m2);
|
|
1250
|
+
ALLOCV_END(h_m1);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
static void
|
|
1254
|
+
ca_mask_of_operands_eval (CAMaskOfOperands *lm, const ca_lazy_req_t *req, boolean8_t *dst)
|
|
1255
|
+
{
|
|
1256
|
+
CAMultiParent *mp = (CAMultiParent *) lm->parent;
|
|
1257
|
+
int kleene = ( lm->mode == CA_LAZY_MASK_KLEENE_OR ||
|
|
1258
|
+
lm->mode == CA_LAZY_MASK_KLEENE_AND );
|
|
1259
|
+
boolean8_t absorbing = ( lm->mode == CA_LAZY_MASK_KLEENE_OR ) ? 1 : 0;
|
|
1260
|
+
volatile VALUE h_mask, h_value, h_known;
|
|
1261
|
+
boolean8_t *m, *v = NULL, *known = NULL;
|
|
1262
|
+
ca_size_t n = req->n, i;
|
|
1263
|
+
int32_t k;
|
|
1264
|
+
|
|
1265
|
+
if ( lm->mode == CA_LAZY_MASK_SELECT ) {
|
|
1266
|
+
ca_mask_of_operands_eval_select(lm, req, dst);
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
memset(dst, 0, n);
|
|
1271
|
+
m = ALLOCV_N(boolean8_t, h_mask, n);
|
|
1272
|
+
if ( kleene ) {
|
|
1273
|
+
v = ALLOCV_N(boolean8_t, h_value, n);
|
|
1274
|
+
known = ALLOCV_N(boolean8_t, h_known, n);
|
|
1275
|
+
memset(known, 0, n);
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
for ( k = 0; k < mp->n_parents; k++ ) {
|
|
1279
|
+
CArray *op = mp->parents[k];
|
|
1280
|
+
if ( ca_has_mask(op) ) {
|
|
1281
|
+
ca_mask_of_operands_pull(lm, op->mask, req, m);
|
|
1282
|
+
for ( i = 0; i < n; i++ ) dst[i] |= m[i];
|
|
1283
|
+
}
|
|
1284
|
+
else {
|
|
1285
|
+
memset(m, 0, n);
|
|
1286
|
+
}
|
|
1287
|
+
if ( kleene ) {
|
|
1288
|
+
ca_mask_of_operands_pull(lm, op, req, v);
|
|
1289
|
+
for ( i = 0; i < n; i++ ) {
|
|
1290
|
+
if ( ! m[i] && ( v[i] ? 1 : 0 ) == absorbing ) known[i] = 1;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
if ( kleene ) {
|
|
1296
|
+
for ( i = 0; i < n; i++ ) {
|
|
1297
|
+
if ( known[i] ) dst[i] = 0;
|
|
1298
|
+
}
|
|
1299
|
+
ALLOCV_END(h_known);
|
|
1300
|
+
ALLOCV_END(h_value);
|
|
1301
|
+
}
|
|
1302
|
+
ALLOCV_END(h_mask);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
NORETURN(static void ca_mask_of_operands_read_only (void));
|
|
1306
|
+
static void
|
|
1307
|
+
ca_mask_of_operands_read_only (void)
|
|
1308
|
+
{
|
|
1309
|
+
rb_raise(rb_eRuntimeError, "the mask of a lazy operation is read-only");
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
static void
|
|
1313
|
+
ca_mask_of_operands_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
1314
|
+
ca_size_t *strides, void *data, int dir)
|
|
1315
|
+
{
|
|
1316
|
+
ca_lazy_req_t req;
|
|
1317
|
+
if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
|
|
1318
|
+
if ( dir != CA_XFER_GET ) ca_mask_of_operands_read_only();
|
|
1319
|
+
ca_lazy_req_region(&req, ap, starts, counts, strides);
|
|
1320
|
+
ca_mask_of_operands_eval((CAMaskOfOperands *) ap, &req, (boolean8_t *) data);
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
static void
|
|
1324
|
+
ca_mask_of_operands_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
|
|
1325
|
+
void *data, int dir)
|
|
1326
|
+
{
|
|
1327
|
+
ca_lazy_req_t req;
|
|
1328
|
+
if ( dir != CA_XFER_GET ) ca_mask_of_operands_read_only();
|
|
1329
|
+
ca_lazy_req_addrs(&req, n, addrs);
|
|
1330
|
+
ca_mask_of_operands_eval((CAMaskOfOperands *) ap, &req, (boolean8_t *) data);
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
static void
|
|
1334
|
+
ca_mask_of_operands_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
|
|
1335
|
+
{
|
|
1336
|
+
CArray *ca = (CArray *) ap;
|
|
1337
|
+
ca_size_t addr = 0;
|
|
1338
|
+
int8_t k;
|
|
1339
|
+
if ( dir != CA_XFER_GET ) ca_mask_of_operands_read_only();
|
|
1340
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
1341
|
+
addr = addr * ca->dim[k] + idx[k];
|
|
1342
|
+
}
|
|
1343
|
+
ca_mask_of_operands_func_xfer_addrs(ap, 1, &addr, data, dir);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
static void
|
|
1347
|
+
ca_mask_of_operands_func_xfer_all (void *ap, void *data, int dir)
|
|
1348
|
+
{
|
|
1349
|
+
CArray *ca = (CArray *) ap;
|
|
1350
|
+
ca_size_t starts[CA_RANK_MAX];
|
|
1351
|
+
ca_size_t native[CA_RANK_MAX];
|
|
1352
|
+
ca_size_t s = 1;
|
|
1353
|
+
int8_t k;
|
|
1354
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
|
|
1355
|
+
for ( k = 0; k < ca->ndim; k++ ) starts[k] = 0;
|
|
1356
|
+
ca_mask_of_operands_func_xfer_stride(ap, starts, ca->dim, native, data, dir);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
static VALUE
|
|
1360
|
+
ca_mask_of_operands_fill_buffer (VALUE arg)
|
|
1361
|
+
{
|
|
1362
|
+
CAMaskOfOperands *ca = (CAMaskOfOperands *) arg;
|
|
1363
|
+
ca_mask_of_operands_func_xfer_all(ca, ca->ptr, CA_XFER_GET);
|
|
1364
|
+
return Qnil;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
/* The buffer is published only once it is filled: a read that raises
|
|
1368
|
+
frees it and leaves the view unattached. */
|
|
1369
|
+
static void
|
|
1370
|
+
ca_mask_of_operands_func_attach (void *ap)
|
|
1371
|
+
{
|
|
1372
|
+
CAMaskOfOperands *ca = (CAMaskOfOperands *) ap;
|
|
1373
|
+
int tag = 0;
|
|
1374
|
+
ca->ptr = xmalloc(ca->elements);
|
|
1375
|
+
rb_protect(ca_mask_of_operands_fill_buffer, (VALUE) ca, &tag);
|
|
1376
|
+
if ( tag ) {
|
|
1377
|
+
xfree(ca->ptr);
|
|
1378
|
+
ca->ptr = NULL;
|
|
1379
|
+
rb_jump_tag(tag);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
static void
|
|
1384
|
+
ca_mask_of_operands_func_allocate (void *ap)
|
|
1385
|
+
{
|
|
1386
|
+
CAMaskOfOperands *ca = (CAMaskOfOperands *) ap;
|
|
1387
|
+
ca->ptr = xmalloc(ca->elements);
|
|
1388
|
+
memset(ca->ptr, 0, ca->elements);
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
static void
|
|
1392
|
+
ca_mask_of_operands_func_sync (void *ap)
|
|
1393
|
+
{
|
|
1394
|
+
/* read-only */
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
static void
|
|
1398
|
+
ca_mask_of_operands_func_detach (void *ap)
|
|
1399
|
+
{
|
|
1400
|
+
CAMaskOfOperands *ca = (CAMaskOfOperands *) ap;
|
|
1401
|
+
xfree(ca->ptr);
|
|
1402
|
+
ca->ptr = NULL;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
NORETURN(static void ca_mask_of_operands_func_fill_data (void *ap, void *ptr));
|
|
1406
|
+
static void
|
|
1407
|
+
ca_mask_of_operands_func_fill_data (void *ap, void *ptr)
|
|
1408
|
+
{
|
|
1409
|
+
ca_mask_of_operands_read_only();
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
NORETURN(static void ca_mask_of_operands_func_create_mask (void *ap));
|
|
1413
|
+
static void
|
|
1414
|
+
ca_mask_of_operands_func_create_mask (void *ap)
|
|
1415
|
+
{
|
|
1416
|
+
rb_raise(rb_eTypeError, "can not create mask array for the mask array");
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
ca_operation_function_t ca_mask_of_operands_func = {
|
|
1420
|
+
-1, /* CA_OBJ_MASK_OF_OPERANDS, set at install time */
|
|
1421
|
+
CA_VIEW_ARRAY,
|
|
1422
|
+
free_ca_mask_of_operands,
|
|
1423
|
+
ca_mask_of_operands_func_clone,
|
|
1424
|
+
ca_mask_of_operands_func_allocate,
|
|
1425
|
+
ca_mask_of_operands_func_attach,
|
|
1426
|
+
ca_mask_of_operands_func_sync,
|
|
1427
|
+
ca_mask_of_operands_func_detach,
|
|
1428
|
+
ca_mask_of_operands_func_fill_data,
|
|
1429
|
+
ca_mask_of_operands_func_create_mask,
|
|
1430
|
+
ca_mask_of_operands_func_xfer_index,
|
|
1431
|
+
ca_mask_of_operands_func_xfer_addrs,
|
|
1432
|
+
NULL, /* fold_stride: computed, never folds */
|
|
1433
|
+
ca_mask_of_operands_func_xfer_stride,
|
|
1434
|
+
ca_mask_of_operands_func_xfer_all,
|
|
1435
|
+
};
|
|
1436
|
+
|
|
1437
|
+
static void
|
|
1438
|
+
Init_ca_mask_of_operands (void)
|
|
1439
|
+
{
|
|
1440
|
+
rb_cCAMaskOfOperands = rb_define_class("CAMaskOfOperands", rb_cCAView);
|
|
1441
|
+
CA_OBJ_MASK_OF_OPERANDS = ca_install_obj_type(rb_cCAMaskOfOperands,
|
|
1442
|
+
&camask_of_operands_data_type,
|
|
1443
|
+
rb_cCArrayMask,
|
|
1444
|
+
&carray_mask_data_type,
|
|
1445
|
+
&ca_mask_of_operands_func,
|
|
1446
|
+
sizeof(ca_mask_of_operands_func));
|
|
1447
|
+
rb_undef_alloc_func(rb_cCAMaskOfOperands);
|
|
1448
|
+
ca_iter_register_source_kind(CA_OBJ_MASK_OF_OPERANDS, CA_ITER_SRC_ATTACH);
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
/* The mask of a lazy operation over several operands: shared with the one
|
|
1452
|
+
masked operand when no other operand can ever be given a mask, computed
|
|
1453
|
+
from all of them on each read otherwise (CAMaskOfOperands above).
|
|
1454
|
+
|
|
1455
|
+
An operand can be given a mask later unless it is broadcast from one
|
|
1456
|
+
cell (a scalar the expression made from a literal), a value array, or a
|
|
1457
|
+
read-only entity. The shared mask is read-only, as the operation is. */
|
|
1458
|
+
CArray *
|
|
1459
|
+
ca_lazy_operation_mask (CArray *operation, int mode)
|
|
1460
|
+
{
|
|
1461
|
+
CAMultiParent *mp = (CAMultiParent *) operation;
|
|
1462
|
+
CArray *masked = NULL;
|
|
1463
|
+
int32_t k, n_masked = 0, n_open = 0;
|
|
1464
|
+
|
|
1465
|
+
for ( k = 0; k < mp->n_parents; k++ ) {
|
|
1466
|
+
CArray *op = mp->parents[k];
|
|
1467
|
+
if ( ca_has_mask(op) ) {
|
|
1468
|
+
masked = op;
|
|
1469
|
+
n_masked++;
|
|
1470
|
+
}
|
|
1471
|
+
else if ( ! ( op->elements == 1 && operation->elements > 1 )
|
|
1472
|
+
&& ! ca_is_value_array(op)
|
|
1473
|
+
&& ! ( ca_is_entity(op) && ca_is_readonly(op) ) ) {
|
|
1474
|
+
n_open++;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
if ( n_masked == 0 ) {
|
|
1479
|
+
return NULL;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
if ( mode == CA_LAZY_MASK_OR && n_masked == 1 && n_open == 0
|
|
1483
|
+
&& masked->elements == operation->elements ) {
|
|
1484
|
+
CArray *shared = (CArray *) ca_refer_new(masked->mask, CA_BOOLEAN,
|
|
1485
|
+
operation->ndim, operation->dim,
|
|
1486
|
+
0, 0);
|
|
1487
|
+
ca_set_flag(shared, CA_FLAG_READ_ONLY);
|
|
1488
|
+
return shared;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
return ca_mask_of_operands_new(operation, mode);
|
|
737
1492
|
}
|