carray 2.0.1 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +6 -25
- data/CHANGELOG.md +338 -0
- data/{NEWS.md → CHANGELOG.v1.md} +3 -0
- data/LICENSE +1 -1
- data/README.md +120 -36
- data/carray.gemspec +32 -30
- data/ext/ca_array_pool.c +91 -0
- data/ext/ca_axis_descriptor.h +186 -0
- data/ext/ca_axis_dispatch.c +924 -0
- data/ext/ca_axis_group.c +1244 -0
- data/ext/ca_bincmp_dispatch.c +76 -0
- data/ext/ca_bincmp_dispatch.h +85 -0
- data/ext/ca_binop_dispatch.c +124 -0
- data/ext/ca_binop_dispatch.h +152 -0
- data/ext/ca_categorical_iterator.c +1375 -0
- data/ext/ca_compare.c +94 -0
- data/ext/ca_compare.h +26 -0
- data/ext/ca_composite_dispatch.c +414 -0
- data/ext/ca_composite_dispatch.h +116 -0
- data/ext/ca_for_buffer.h +96 -0
- data/ext/ca_for_each_element.h +239 -0
- data/ext/ca_group_iter.c +304 -0
- data/ext/ca_iter_substrate.h +325 -0
- data/ext/ca_kernel_iterator.c +4367 -0
- data/ext/ca_kernel_iterator.h +2596 -0
- data/ext/ca_moncmp_dispatch.c +37 -0
- data/ext/ca_moncmp_dispatch.h +62 -0
- data/ext/ca_monop_dispatch.c +200 -0
- data/ext/ca_monop_dispatch.h +235 -0
- data/ext/ca_obj_array.c +355 -359
- data/ext/ca_obj_bincmp.c +839 -0
- data/ext/ca_obj_binop.c +948 -0
- data/ext/ca_obj_bitarray.c +369 -164
- data/ext/ca_obj_bitfield.c +294 -234
- data/ext/ca_obj_block.c +189 -711
- data/ext/ca_obj_byte_swap.c +766 -0
- data/ext/ca_obj_const_string.c +967 -0
- data/ext/ca_obj_face.c +750 -0
- data/ext/ca_obj_face.h +279 -0
- data/ext/ca_obj_fake.c +239 -100
- data/ext/ca_obj_farray.c +54 -441
- data/ext/ca_obj_field.c +82 -529
- data/ext/ca_obj_fixlen_string.c +308 -0
- data/ext/ca_obj_grid.c +866 -440
- data/ext/ca_obj_meld.c +1039 -0
- data/ext/ca_obj_moncmp.c +588 -0
- data/ext/ca_obj_monop.c +1123 -0
- data/ext/ca_obj_object.c +866 -296
- data/ext/ca_obj_record.c +470 -0
- data/ext/ca_obj_reduce.c +97 -82
- data/ext/ca_obj_refer.c +593 -459
- data/ext/ca_obj_remap.c +475 -0
- data/ext/ca_obj_repeat.c +92 -477
- data/ext/ca_obj_roll.c +624 -0
- data/ext/ca_obj_select.c +344 -296
- data/ext/ca_obj_select_axis.c +1306 -0
- data/ext/ca_obj_shift.c +231 -793
- data/ext/ca_obj_source.c +78 -0
- data/ext/ca_obj_stack.c +1173 -0
- data/ext/ca_obj_stride.c +2584 -0
- data/ext/ca_obj_string.c +270 -0
- data/ext/ca_obj_tile.c +622 -0
- data/ext/ca_obj_time.c +548 -0
- data/ext/ca_obj_timedelta.c +437 -0
- data/ext/ca_obj_transpose.c +62 -516
- data/ext/ca_obj_triop.c +785 -0
- data/ext/ca_obj_window.c +1202 -565
- data/ext/ca_op_byte_swap.c +175 -0
- data/ext/ca_op_cmplx64.h +123 -0
- data/ext/ca_op_ipower.c +316 -0
- data/ext/ca_op_powi.h +88 -0
- data/ext/ca_sort_kernels.h +132 -0
- data/ext/ca_sweep_engine.c +473 -0
- data/ext/ca_sweep_engine.h +166 -0
- data/ext/ca_transform_common.c +235 -0
- data/ext/ca_triop_dispatch.c +55 -0
- data/ext/ca_triop_dispatch.h +62 -0
- data/ext/carray.h +810 -420
- data/ext/carray_access.c +873 -731
- data/ext/carray_attribute.c +98 -329
- data/ext/carray_bincount.c +255 -0
- data/ext/carray_broadcast.c +376 -0
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +2897 -874
- data/ext/carray_call_cfunc.h +313 -0
- data/ext/carray_cast.c +1264 -315
- data/ext/carray_cast_func.rb +81 -40
- data/ext/carray_class.c +53 -63
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +350 -346
- data/ext/carray_copy.c +168 -270
- data/ext/carray_core.c +1396 -206
- data/ext/carray_count.c +312 -0
- data/ext/carray_data_type.c +43 -19
- data/ext/carray_element.c +585 -213
- data/ext/carray_factorize.c +2542 -0
- data/ext/carray_generate.c +230 -559
- data/ext/carray_histogram.c +490 -0
- data/ext/carray_hold.c +228 -0
- data/ext/carray_index_classifier.c +1021 -0
- data/ext/carray_index_classifier.h +27 -0
- data/ext/carray_internal.h +136 -0
- data/ext/carray_kernels_bincmp.c +4446 -0
- data/ext/carray_kernels_binop.c +11001 -0
- data/ext/carray_kernels_init.c +1131 -0
- data/ext/carray_kernels_map.c +3467 -0
- data/ext/carray_kernels_moncmp.c +2097 -0
- data/ext/carray_kernels_monop.c +18313 -0
- data/ext/carray_kernels_reduce_aggregate.c +25837 -0
- data/ext/carray_kernels_reduce_boolean.c +330 -0
- data/ext/carray_kernels_reduce_cumulative.c +14593 -0
- data/ext/carray_kernels_reduce_extreme.c +16948 -0
- data/ext/carray_kernels_reduce_variance.c +3910 -0
- data/ext/carray_kernels_scan.c +3693 -0
- data/ext/carray_kernels_search.c +32138 -0
- data/ext/carray_kernels_sort.c +10626 -0
- data/ext/carray_kernels_triop.c +1392 -0
- data/ext/carray_lazy.c +737 -0
- data/ext/carray_loop.c +88 -200
- data/ext/carray_mask.c +853 -158
- data/ext/carray_math_kernel.h +120 -0
- data/ext/carray_mathfunc.c +10 -241
- data/ext/carray_median_percentile.c +1257 -0
- data/ext/carray_memory_view.c +1650 -0
- data/ext/carray_operator.c +1525 -320
- data/ext/carray_order.c +664 -1394
- data/ext/carray_partition.c +416 -0
- data/ext/carray_random.c +518 -0
- data/ext/carray_scatter.c +357 -0
- data/ext/carray_slab.c +1219 -0
- data/ext/carray_slab.h +84 -0
- data/ext/carray_sort.c +829 -0
- data/ext/carray_sort_kernel.c +620 -0
- data/ext/carray_struct.c +695 -0
- data/ext/carray_test.c +343 -229
- data/ext/carray_undef.c +34 -17
- data/ext/carray_utils.c +175 -74
- data/ext/extconf.rb +234 -55
- data/ext/mk_call_cfunc.rb +671 -0
- data/ext/mkkernel.rb +9096 -0
- data/ext/ruby_carray.c +211 -108
- data/ext/version.h +4 -14
- data/ext/version.rb +5 -13
- data/lib/carray/arrow_tensor.rb +401 -0
- data/lib/carray/attribute.rb +166 -0
- data/lib/carray/autoload_carray.rb +239 -0
- data/lib/carray/autoload_method_extension.rb +45 -0
- data/lib/carray/axis_group.rb +711 -0
- data/lib/carray/basics.rb +481 -0
- data/lib/carray/bincount_nd.rb +358 -0
- data/lib/carray/block_iterator.rb +614 -0
- data/lib/carray/boolean_reduce.rb +109 -0
- data/lib/carray/categorical.rb +561 -0
- data/lib/carray/categorical_iterator.rb +1084 -0
- data/lib/carray/complex.rb +150 -0
- data/lib/carray/conditional.rb +216 -0
- data/lib/carray/const_string.rb +228 -0
- data/lib/carray/construct.rb +160 -328
- data/lib/carray/core_extensions.rb +297 -0
- data/lib/carray/data_type_extension.rb +250 -0
- data/lib/carray/fixlen_string.rb +95 -0
- data/lib/carray/frame/concat.rb +132 -0
- data/lib/carray/frame/convert.rb +95 -0
- data/lib/carray/frame/csv_parser.rb +211 -0
- data/lib/carray/frame/frame.rb +642 -0
- data/lib/carray/frame/group.rb +186 -0
- data/lib/carray/frame/io.rb +321 -0
- data/lib/carray/frame/join.rb +248 -0
- data/lib/carray/frame/records.rb +99 -0
- data/lib/carray/frame/sort.rb +113 -0
- data/lib/carray/frame/verbs.rb +316 -0
- data/lib/carray/frame.rb +16 -0
- data/lib/carray/fuse_source.rb +123 -0
- data/lib/carray/fusion.rb +218 -0
- data/lib/carray/histogram.rb +512 -0
- data/lib/carray/inspect.rb +37 -26
- data/lib/carray/iterator.rb +58 -349
- data/lib/carray/lazy.rb +941 -0
- data/lib/carray/mask_gap_fill.rb +200 -0
- data/lib/carray/math.rb +78 -342
- data/lib/carray/meld_reduce.rb +289 -0
- data/lib/carray/methods/align_addr.rb +116 -0
- data/lib/carray/methods/bin.rb +128 -0
- data/lib/carray/methods/bincount.rb +87 -0
- data/lib/carray/methods/bit_string.rb +92 -0
- data/lib/carray/methods/broadcast.rb +63 -0
- data/lib/carray/methods/choose.rb +39 -0
- data/lib/carray/methods/composition.rb +280 -0
- data/lib/carray/methods/gather_nd.rb +206 -0
- data/lib/carray/methods/index.rb +39 -0
- data/lib/carray/methods/insert_block.rb +99 -0
- data/lib/carray/methods/is_in.rb +141 -0
- data/lib/carray/methods/join.rb +90 -0
- data/lib/carray/methods/locate_addr.rb +52 -0
- data/lib/carray/methods/mask_duplicates.rb +41 -0
- data/lib/carray/methods/meshgrid.rb +90 -0
- data/lib/carray/methods/mode.rb +126 -0
- data/lib/carray/methods/nunique.rb +46 -0
- data/lib/carray/methods/resize.rb +56 -0
- data/lib/carray/methods/snap.rb +161 -0
- data/lib/carray/methods/string_format.rb +57 -0
- data/lib/carray/methods/unique.rb +47 -0
- data/lib/carray/methods/value_counts.rb +71 -0
- data/lib/carray/mkmf.rb +124 -101
- data/lib/carray/runtime.rb +89 -0
- data/lib/carray/serialize.rb +478 -167
- data/lib/carray/slab_iterator.rb +305 -0
- data/lib/carray/stack.rb +291 -0
- data/lib/carray/string.rb +56 -180
- data/lib/carray/string_operation_extension.rb +289 -0
- data/lib/carray/struct.rb +335 -323
- data/lib/carray/struct_builder.rb +697 -0
- data/lib/carray/table.rb +41 -2
- data/lib/carray/time.rb +2654 -38
- data/lib/carray/window_iterator.rb +927 -0
- data/lib/carray.rb +55 -57
- data/yard-stubs/ca_obj_array.rb +385 -0
- data/yard-stubs/ca_obj_bitarray.rb +38 -0
- data/yard-stubs/ca_obj_bitfield.rb +43 -0
- data/yard-stubs/ca_obj_block.rb +73 -0
- data/yard-stubs/ca_obj_byte_swap.rb +56 -0
- data/yard-stubs/ca_obj_fake.rb +31 -0
- data/yard-stubs/ca_obj_farray.rb +32 -0
- data/yard-stubs/ca_obj_field.rb +45 -0
- data/yard-stubs/ca_obj_grid.rb +35 -0
- data/yard-stubs/ca_obj_refer.rb +72 -0
- data/yard-stubs/ca_obj_roll.rb +45 -0
- data/yard-stubs/ca_obj_shift.rb +43 -0
- data/yard-stubs/ca_obj_stride.rb +181 -0
- data/yard-stubs/ca_obj_tile.rb +29 -0
- data/yard-stubs/ca_obj_transpose.rb +40 -0
- data/yard-stubs/ca_obj_window.rb +49 -0
- data/yard-stubs/carray_access.rb +131 -0
- data/yard-stubs/carray_attribute.rb +246 -0
- data/yard-stubs/carray_broadcast.rb +37 -0
- data/yard-stubs/carray_cast.rb +489 -0
- data/yard-stubs/carray_class.rb +65 -0
- data/yard-stubs/carray_conversion.rb +76 -0
- data/yard-stubs/carray_copy.rb +79 -0
- data/yard-stubs/carray_core.rb +114 -0
- data/yard-stubs/carray_count.rb +79 -0
- data/yard-stubs/carray_element.rb +108 -0
- data/yard-stubs/carray_generate.rb +66 -0
- data/yard-stubs/carray_lazy.rb +23 -0
- data/yard-stubs/carray_loop.rb +140 -0
- data/yard-stubs/carray_mask.rb +259 -0
- data/yard-stubs/carray_math.rb +132 -0
- data/yard-stubs/carray_mathfunc.rb +45 -0
- data/yard-stubs/carray_median_percentile.rb +89 -0
- data/yard-stubs/carray_memory_view.rb +163 -0
- data/yard-stubs/carray_order.rb +312 -0
- data/yard-stubs/carray_random.rb +89 -0
- data/yard-stubs/carray_scatter.rb +106 -0
- data/yard-stubs/carray_slab.rb +57 -0
- data/yard-stubs/carray_sort.rb +163 -0
- data/yard-stubs/carray_test.rb +85 -0
- data/yard-stubs/carray_undef.rb +64 -0
- data/yard-stubs/carray_utils.rb +97 -0
- data/yard-stubs/ruby_carray.rb +193 -0
- metadata +220 -138
- data/Rakefile +0 -51
- data/TODO.md +0 -18
- data/ext/ca_iter_block.c +0 -257
- data/ext/ca_iter_dimension.c +0 -299
- data/ext/ca_iter_window.c +0 -214
- data/ext/ca_obj_mapping.c +0 -644
- data/ext/ca_obj_unbound_repeat.c +0 -529
- data/ext/carray_iterator.c +0 -641
- data/ext/carray_math.rb +0 -850
- data/ext/carray_numeric.c +0 -259
- data/ext/carray_sort_addr.c +0 -254
- data/ext/carray_stat.c +0 -2100
- data/ext/carray_stat_proc.rb +0 -1999
- data/ext/mkmath.rb +0 -741
- data/ext/ruby_ccomplex.c +0 -509
- data/ext/ruby_float_func.c +0 -86
- data/lib/carray/array.rb +0 -8
- data/lib/carray/autoload/autoload_base.rb +0 -19
- data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
- data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
- data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
- data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
- data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
- data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
- data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
- data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
- data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
- data/lib/carray/autoload/autoload_gem_random.rb +0 -8
- data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
- data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
- data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
- data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
- data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
- data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
- data/lib/carray/autoload/autoload_object_link.rb +0 -1
- data/lib/carray/autoload/autoload_object_pack.rb +0 -2
- data/lib/carray/autoload.rb +0 -141
- data/lib/carray/basic.rb +0 -191
- data/lib/carray/broadcast.rb +0 -101
- data/lib/carray/compose.rb +0 -315
- data/lib/carray/convert.rb +0 -115
- data/lib/carray/info.rb +0 -110
- data/lib/carray/io/imagemagick.rb +0 -235
- data/lib/carray/mask.rb +0 -102
- data/lib/carray/math/histogram.rb +0 -177
- data/lib/carray/math/recurrence.rb +0 -93
- data/lib/carray/object/ca_obj_iterator.rb +0 -50
- data/lib/carray/object/ca_obj_link.rb +0 -50
- data/lib/carray/object/ca_obj_pack.rb +0 -99
- data/lib/carray/obsolete.rb +0 -256
- data/lib/carray/ordering.rb +0 -181
- data/lib/carray/testing.rb +0 -51
- data/lib/carray/transform.rb +0 -109
- data/mailmap +0 -1
- data/misc/Methods.ja.md +0 -182
- data/misc/NOTE +0 -51
- data/spec/Classes/CABitfield_spec.rb +0 -58
- data/spec/Classes/CABlockIterator_spec.rb +0 -114
- data/spec/Classes/CABlock_spec.rb +0 -205
- data/spec/Classes/CAField_spec.rb +0 -39
- data/spec/Classes/CAGrid_spec.rb +0 -75
- data/spec/Classes/CAMap_spec.rb +0 -0
- data/spec/Classes/CAMapping_spec.rb +0 -105
- data/spec/Classes/CAObject_attribute_spec.rb +0 -33
- data/spec/Classes/CAObject_spec.rb +0 -33
- data/spec/Classes/CARefer_spec.rb +0 -93
- data/spec/Classes/CARepeat_spec.rb +0 -65
- data/spec/Classes/CASelect_spec.rb +0 -22
- data/spec/Classes/CAShift_spec.rb +0 -16
- data/spec/Classes/CAStruct_spec.rb +0 -71
- data/spec/Classes/CATranspose_spec.rb +0 -60
- data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
- data/spec/Classes/CAWindow_spec.rb +0 -54
- data/spec/Classes/CAWrap_spec.rb +0 -8
- data/spec/Classes/CArray_spec.rb +0 -184
- data/spec/Classes/CScalar_spec.rb +0 -55
- data/spec/Features/feature_130_spec.rb +0 -19
- data/spec/Features/feature_attributes_spec.rb +0 -280
- data/spec/Features/feature_boolean_spec.rb +0 -98
- data/spec/Features/feature_broadcast.rb +0 -116
- data/spec/Features/feature_cast_function.rb +0 -19
- data/spec/Features/feature_cast_spec.rb +0 -33
- data/spec/Features/feature_class_spec.rb +0 -84
- data/spec/Features/feature_complex_spec.rb +0 -42
- data/spec/Features/feature_composite_spec.rb +0 -124
- data/spec/Features/feature_convert_spec.rb +0 -46
- data/spec/Features/feature_copy_spec.rb +0 -123
- data/spec/Features/feature_creation_spec.rb +0 -84
- data/spec/Features/feature_element_spec.rb +0 -144
- data/spec/Features/feature_extream_spec.rb +0 -54
- data/spec/Features/feature_generate_spec.rb +0 -74
- data/spec/Features/feature_index_spec.rb +0 -69
- data/spec/Features/feature_mask_spec.rb +0 -580
- data/spec/Features/feature_math_spec.rb +0 -97
- data/spec/Features/feature_order_spec.rb +0 -146
- data/spec/Features/feature_ref_store_spec.rb +0 -209
- data/spec/Features/feature_serialization_spec.rb +0 -125
- data/spec/Features/feature_stat_spec.rb +0 -397
- data/spec/Features/feature_virtual_spec.rb +0 -48
- data/spec/Features/method_eq_spec.rb +0 -81
- data/spec/Features/method_is_nan_spec.rb +0 -12
- data/spec/Features/method_map_spec.rb +0 -54
- data/spec/Features/method_max_with.rb +0 -20
- data/spec/Features/method_min_with.rb +0 -19
- data/spec/Features/method_ne_spec.rb +0 -18
- data/spec/Features/method_project_spec.rb +0 -188
- data/spec/Features/method_ref_spec.rb +0 -27
- data/spec/Features/method_round_spec.rb +0 -11
- data/spec/Features/method_s_linspace_spec.rb +0 -48
- data/spec/Features/method_s_span_spec.rb +0 -14
- data/spec/Features/method_seq_spec.rb +0 -47
- data/spec/Features/method_sort_with.rb +0 -43
- data/spec/Features/method_sorted_with.rb +0 -29
- data/spec/Features/method_span_spec.rb +0 -42
- data/spec/Features/method_wrap_readonly_spec.rb +0 -43
- data/spec/UnitTest/test_CAVirtual.rb +0 -214
- data/spec/spec_all.rb +0 -10
- data/utils/ca_ase.rb +0 -21
- data/utils/ca_methods.rb +0 -15
- data/utils/cast_checker.rb +0 -30
- data/utils/convert_test.rb +0 -73
- data/utils/extract_yard.rb +0 -22
- data/utils/guess_shape.rb +0 -76
- data/utils/monkey_patch_methods.rb +0 -62
- data/utils/remove_resource_fork.sh +0 -5
data/ext/carray_core.c
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/* ---------------------------------------------------------------------------
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Copyright (C) 2005-2020 Hiroki Motoyoshi
|
|
3
|
+
Core runtime: obj_type registration (ca_install_obj_type), the
|
|
4
|
+
ca_func / ca_class / ca_typeddata dispatch tables, TypedData mark /
|
|
5
|
+
free, the attach lifecycle, and the per-cell / per-region / whole-view
|
|
6
|
+
transfer primitives (ca_xfer_index / _addrs / _stride / _all).
|
|
8
7
|
|
|
9
8
|
---------------------------------------------------------------------------- */
|
|
10
9
|
|
|
11
10
|
#include "carray.h"
|
|
11
|
+
#include "carray_internal.h" /* ca_lazy_arena_enter / _exit */
|
|
12
|
+
#include "ca_iter_substrate.h"
|
|
12
13
|
#include <stdarg.h>
|
|
13
14
|
|
|
14
15
|
/* definition of ca_endian */
|
|
@@ -92,11 +93,7 @@ ca_valid[CA_NTYPE] = {
|
|
|
92
93
|
#else
|
|
93
94
|
0,
|
|
94
95
|
#endif
|
|
95
|
-
|
|
96
|
-
1,
|
|
97
|
-
#else
|
|
98
|
-
0,
|
|
99
|
-
#endif
|
|
96
|
+
0, /* CA_FLOAT128 (not built) */
|
|
100
97
|
#ifdef HAVE_TYPE_CMPLX64_T
|
|
101
98
|
1,
|
|
102
99
|
#else
|
|
@@ -107,11 +104,7 @@ ca_valid[CA_NTYPE] = {
|
|
|
107
104
|
#else
|
|
108
105
|
0,
|
|
109
106
|
#endif
|
|
110
|
-
|
|
111
|
-
1,
|
|
112
|
-
#else
|
|
113
|
-
0,
|
|
114
|
-
#endif
|
|
107
|
+
0, /* CA_CMPLX256 (not built) */
|
|
115
108
|
1
|
|
116
109
|
};
|
|
117
110
|
|
|
@@ -131,10 +124,10 @@ ca_sizeof[CA_NTYPE] = {
|
|
|
131
124
|
sizeof(uint64_t),
|
|
132
125
|
sizeof(float32_t),
|
|
133
126
|
sizeof(float64_t),
|
|
134
|
-
|
|
127
|
+
0, /* float128_t (not built) */
|
|
135
128
|
sizeof(cmplx64_t),
|
|
136
129
|
sizeof(cmplx128_t),
|
|
137
|
-
|
|
130
|
+
0, /* cmplx256_t (not built) */
|
|
138
131
|
sizeof(VALUE),
|
|
139
132
|
};
|
|
140
133
|
|
|
@@ -154,10 +147,10 @@ ca_type_name[CA_NTYPE] = {
|
|
|
154
147
|
"uint64",
|
|
155
148
|
"float32",
|
|
156
149
|
"float64",
|
|
157
|
-
"float128",
|
|
150
|
+
"(retired:float128)",
|
|
158
151
|
"cmplx64",
|
|
159
152
|
"cmplx128",
|
|
160
|
-
"cmplx256",
|
|
153
|
+
"(retired:cmplx256)",
|
|
161
154
|
"object",
|
|
162
155
|
};
|
|
163
156
|
|
|
@@ -238,21 +231,17 @@ ca_cast_table2[CA_NTYPE][CA_NTYPE] = {
|
|
|
238
231
|
* CASelect
|
|
239
232
|
* CAObject
|
|
240
233
|
* CARepeat
|
|
241
|
-
* CAUnboundRepeat
|
|
242
234
|
*/
|
|
243
235
|
|
|
244
236
|
void
|
|
245
|
-
ca_init_obj_type ()
|
|
237
|
+
ca_init_obj_type (void)
|
|
246
238
|
{
|
|
247
239
|
extern ca_operation_function_t ca_array_func;
|
|
248
240
|
extern ca_operation_function_t ca_wrap_func;
|
|
249
241
|
extern ca_operation_function_t ca_scalar_func;
|
|
250
|
-
extern ca_operation_function_t ca_refer_func;
|
|
251
|
-
extern ca_operation_function_t ca_block_func;
|
|
252
242
|
extern ca_operation_function_t ca_select_func;
|
|
253
243
|
extern ca_operation_function_t ca_object_func;
|
|
254
|
-
extern ca_operation_function_t
|
|
255
|
-
extern ca_operation_function_t ca_ubrep_func;
|
|
244
|
+
extern ca_operation_function_t ca_stride_func;
|
|
256
245
|
|
|
257
246
|
/* CArray */
|
|
258
247
|
ca_func[CA_OBJ_ARRAY] = ca_array_func;
|
|
@@ -275,15 +264,22 @@ ca_init_obj_type ()
|
|
|
275
264
|
ca_mask_class[CA_OBJ_SCALAR] = rb_cCArrayMask;
|
|
276
265
|
ca_mask_typeddata[CA_OBJ_SCALAR] = &carray_mask_data_type;
|
|
277
266
|
|
|
278
|
-
/* CARefer
|
|
279
|
-
|
|
267
|
+
/* CARefer (CAStride subclass). The function table is
|
|
268
|
+
installed as ca_stride_func here as a baseline; Init_ca_obj_refer
|
|
269
|
+
overrides ca_func[CA_OBJ_REFER] with a copy that has custom
|
|
270
|
+
free_object (frees the mask0 tail) and custom create_mask
|
|
271
|
+
(handles byte-reinterpret cases). */
|
|
272
|
+
ca_func[CA_OBJ_REFER] = ca_stride_func;
|
|
280
273
|
ca_class[CA_OBJ_REFER] = rb_cCARefer;
|
|
281
274
|
ca_typeddata[CA_OBJ_REFER] = &carefer_data_type;
|
|
282
275
|
ca_mask_class[CA_OBJ_REFER] = rb_cCAReferMask;
|
|
283
276
|
ca_mask_typeddata[CA_OBJ_REFER] = &carefer_mask_data_type;
|
|
284
277
|
|
|
285
|
-
/* CABlock
|
|
286
|
-
|
|
278
|
+
/* CABlock (CAStride subclass). Baseline registered to
|
|
279
|
+
ca_stride_func; Init_ca_obj_block overrides with a copy that has
|
|
280
|
+
custom free_object (frees the tail arrays) and custom create_mask
|
|
281
|
+
(builds the mask as a CABlock with matching block parameters). */
|
|
282
|
+
ca_func[CA_OBJ_BLOCK] = ca_stride_func;
|
|
287
283
|
ca_class[CA_OBJ_BLOCK] = rb_cCABlock;
|
|
288
284
|
ca_typeddata[CA_OBJ_BLOCK] = &cablock_data_type;
|
|
289
285
|
ca_mask_class[CA_OBJ_BLOCK] = rb_cCABlockMask;
|
|
@@ -303,19 +299,14 @@ ca_init_obj_type ()
|
|
|
303
299
|
ca_mask_class[CA_OBJ_OBJECT] = rb_cCArrayMask;
|
|
304
300
|
ca_mask_typeddata[CA_OBJ_OBJECT] = &carray_mask_data_type;
|
|
305
301
|
|
|
306
|
-
/* CARepeat
|
|
307
|
-
|
|
302
|
+
/* CARepeat (subclass of CAStride; shares its function table and
|
|
303
|
+
TypedData entirely). */
|
|
304
|
+
ca_func[CA_OBJ_REPEAT] = ca_stride_func;
|
|
308
305
|
ca_class[CA_OBJ_REPEAT] = rb_cCARepeat;
|
|
309
|
-
ca_typeddata[CA_OBJ_REPEAT] = &
|
|
306
|
+
ca_typeddata[CA_OBJ_REPEAT] = &castride_data_type;
|
|
310
307
|
ca_mask_class[CA_OBJ_REPEAT] = rb_cCARepeatMask;
|
|
311
|
-
ca_mask_typeddata[CA_OBJ_REPEAT] = &
|
|
308
|
+
ca_mask_typeddata[CA_OBJ_REPEAT] = &castride_mask_data_type;
|
|
312
309
|
|
|
313
|
-
/* CAUnboundRepeat */
|
|
314
|
-
ca_func[CA_OBJ_UNBOUND_REPEAT] = ca_ubrep_func;
|
|
315
|
-
ca_class[CA_OBJ_UNBOUND_REPEAT] = rb_cCAUnboundRepeat;
|
|
316
|
-
ca_typeddata[CA_OBJ_UNBOUND_REPEAT] = &caunboundrepeat_data_type;
|
|
317
|
-
ca_mask_class[CA_OBJ_UNBOUND_REPEAT] = rb_cCAUnboundRepeatMask;
|
|
318
|
-
ca_mask_typeddata[CA_OBJ_UNBOUND_REPEAT] = &caunboundrepeat_mask_data_type;
|
|
319
310
|
|
|
320
311
|
ca_obj_num = 9;
|
|
321
312
|
}
|
|
@@ -325,11 +316,12 @@ ca_init_obj_type ()
|
|
|
325
316
|
*/
|
|
326
317
|
|
|
327
318
|
int
|
|
328
|
-
ca_install_obj_type (VALUE klass,
|
|
329
|
-
const rb_data_type_t *typeddata,
|
|
330
|
-
VALUE mask_klass,
|
|
331
|
-
const rb_data_type_t *mask_typeddata,
|
|
332
|
-
ca_operation_function_t func
|
|
319
|
+
ca_install_obj_type (VALUE klass,
|
|
320
|
+
const rb_data_type_t *typeddata,
|
|
321
|
+
VALUE mask_klass,
|
|
322
|
+
const rb_data_type_t *mask_typeddata,
|
|
323
|
+
const ca_operation_function_t *func,
|
|
324
|
+
size_t func_size)
|
|
333
325
|
{
|
|
334
326
|
int obj_type = ca_obj_num++;
|
|
335
327
|
|
|
@@ -339,10 +331,31 @@ ca_install_obj_type (VALUE klass,
|
|
|
339
331
|
CA_OBJ_TYPE_MAX);
|
|
340
332
|
}
|
|
341
333
|
|
|
342
|
-
|
|
334
|
+
if ( func_size > sizeof(ca_operation_function_t) ) {
|
|
335
|
+
rb_raise(rb_eRuntimeError,
|
|
336
|
+
"operation table is larger than this carray's (%zu > %zu); "
|
|
337
|
+
"the caller was built against a newer carray",
|
|
338
|
+
func_size, sizeof(ca_operation_function_t));
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/* Everything from xfer_index on was appended after the table's first
|
|
342
|
+
shape, so a caller may legitimately stop short of it. Anything shorter
|
|
343
|
+
than that cannot dispatch at all. */
|
|
344
|
+
if ( func_size < offsetof(ca_operation_function_t, xfer_index) ) {
|
|
345
|
+
rb_raise(rb_eRuntimeError,
|
|
346
|
+
"operation table is too small to dispatch (%zu < %zu)",
|
|
347
|
+
func_size,
|
|
348
|
+
(size_t) offsetof(ca_operation_function_t, xfer_index));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/* Copy by the caller's length and zero the rest: slots this build knows
|
|
352
|
+
about but the caller does not are NULL, which every dispatcher already
|
|
353
|
+
reads as "not provided". */
|
|
354
|
+
MEMZERO(&ca_func[obj_type], ca_operation_function_t, 1);
|
|
355
|
+
memcpy(&ca_func[obj_type], func, func_size);
|
|
356
|
+
ca_func[obj_type].obj_type = obj_type;
|
|
343
357
|
|
|
344
358
|
ca_class[obj_type] = klass;
|
|
345
|
-
ca_func[obj_type] = func;
|
|
346
359
|
ca_typeddata[obj_type] = typeddata;
|
|
347
360
|
ca_mask_class[obj_type] = mask_klass;
|
|
348
361
|
ca_mask_typeddata[obj_type] = mask_typeddata;
|
|
@@ -396,47 +409,412 @@ ca_free_nop (void *ap)
|
|
|
396
409
|
|
|
397
410
|
/* ------------------------------------------------------------------- */
|
|
398
411
|
|
|
412
|
+
/* Returns true iff ca_attach(ca) is essentially O(1) (= no malloc /
|
|
413
|
+
copy):
|
|
414
|
+
- entity arrays (CA_REAL_ARRAY: already attached, ca->ptr valid)
|
|
415
|
+
- CAStride-family views whose composed strides are row-major
|
|
416
|
+
contiguous (the alias path takes parent->ptr + base_offset
|
|
417
|
+
without allocating)
|
|
418
|
+
Used by kernel_iterator's L1 alias decision (ca_iter_can_alias level 1)
|
|
419
|
+
and by overlay view dispatch. The name says "is_alias" (a structural
|
|
420
|
+
property) rather than "is_cheap" (a cost claim): the predicate checks
|
|
421
|
+
whether ca can be aliased without materialise. */
|
|
422
|
+
int
|
|
423
|
+
ca_attach_is_alias (void *ap)
|
|
424
|
+
{
|
|
425
|
+
CArray *ca = (CArray *) ap;
|
|
426
|
+
extern ca_operation_function_t ca_stride_func;
|
|
427
|
+
extern ca_operation_function_t ca_lazy_marker_func;
|
|
428
|
+
extern int ca_stride_is_contiguous (CAStride *ca);
|
|
429
|
+
extern int ca_stride_attach_aliases_root (CAStride *ca);
|
|
430
|
+
|
|
431
|
+
if ( ca == NULL ) return 0;
|
|
432
|
+
if ( ca_is_entity(ca) ) return 1;
|
|
433
|
+
|
|
434
|
+
/* CALazyMarker's attach is literally `ca->ptr = ca->parent->ptr` after
|
|
435
|
+
attaching the parent — it adds no layout of its own — so it aliases
|
|
436
|
+
exactly when its parent does. Without this a marker looks expensive
|
|
437
|
+
to every caller and views built on it fall onto materialising paths,
|
|
438
|
+
even though there is nothing between the marker and real memory. */
|
|
439
|
+
if ( ca_func[ca->obj_type].attach == ca_lazy_marker_func.attach ) {
|
|
440
|
+
return ca_attach_is_alias(((CAView *) ca)->parent);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/* CAStride family share ca_stride_func.attach (= ca_stride_func_attach).
|
|
444
|
+
The alias-attach fast path is taken iff composed strides are
|
|
445
|
+
row-major contiguous. ca_stride_is_contiguous checks the leaf
|
|
446
|
+
view's own strides, which is what we want — the compose-fold to
|
|
447
|
+
root happens during attach itself; if leaf is contig and parent
|
|
448
|
+
chain is too (transitively, since each CAStride's strides are
|
|
449
|
+
composed against parent's), the alias path fires.
|
|
450
|
+
|
|
451
|
+
...and iff there is parent memory to alias at the end of that fold.
|
|
452
|
+
A non-entity root has none to lend, so attach builds its own buffer
|
|
453
|
+
and writes through ca->ptr reach the root only via ca_sync. Saying
|
|
454
|
+
"alias" there would let a caller write and skip the sync. */
|
|
455
|
+
if ( ca_func[ca->obj_type].attach == ca_stride_func.attach ) {
|
|
456
|
+
return ca_stride_is_contiguous((CAStride *) ca)
|
|
457
|
+
&& ca_stride_attach_aliases_root((CAStride *) ca);
|
|
458
|
+
}
|
|
459
|
+
return 0;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/* ------------------------------------------------------------------- */
|
|
463
|
+
|
|
464
|
+
/* Allocate a reduction-output CArray for kernel_iterator authors.
|
|
465
|
+
|
|
466
|
+
Arguments:
|
|
467
|
+
self Ruby VALUE wrapping the input CArray (source of the reduction).
|
|
468
|
+
slab_axes sort-ascending list of axis indices that the kernel
|
|
469
|
+
will walk per slab (= the axes removed from the output).
|
|
470
|
+
May contain any K in [1..self.ndim]; the helper
|
|
471
|
+
validates range and uniqueness.
|
|
472
|
+
naxes length of slab_axes. Must satisfy 0 < naxes <= self.ndim.
|
|
473
|
+
data_type output data_type (CA_INT32 / CA_FLOAT64 / ... — any numeric
|
|
474
|
+
data_type with non-zero ca_sizeof[]). May differ from
|
|
475
|
+
self's data_type (e.g. mean of int32 → float64).
|
|
476
|
+
|
|
477
|
+
Output shape:
|
|
478
|
+
- Partial reduction (naxes < self.ndim): self.dim with slab axes
|
|
479
|
+
removed in ascending order, ndim = self.ndim - naxes.
|
|
480
|
+
- Full reduction (naxes == self.ndim): shape [1] 1-D CArray
|
|
481
|
+
(kernel writes op[0] and the author wraps the result to a Ruby
|
|
482
|
+
Float / CScalar at their own discretion).
|
|
483
|
+
|
|
484
|
+
Same axis-validation rules as init_l2 CA_SLAB_AXES (= duplicates and
|
|
485
|
+
out-of-range raise ArgumentError so author input bugs surface here
|
|
486
|
+
rather than at the slab walk). Mask is NULL on the output (=
|
|
487
|
+
reduction kernels populate it themselves if needed). */
|
|
488
|
+
VALUE
|
|
489
|
+
rb_ca_new_reduced_bytes (VALUE self, int8_t *slab_axes, int8_t naxes,
|
|
490
|
+
int32_t data_type, ca_size_t bytes, int keep_axis)
|
|
491
|
+
{
|
|
492
|
+
CArray *ca;
|
|
493
|
+
int8_t k;
|
|
494
|
+
|
|
495
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
496
|
+
|
|
497
|
+
if ( naxes <= 0 || naxes > ca->ndim ) {
|
|
498
|
+
rb_raise(rb_eArgError,
|
|
499
|
+
"rb_ca_new_reduced: naxes=%d invalid for ndim=%d",
|
|
500
|
+
(int) naxes, (int) ca->ndim);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
int8_t in_slab[CA_RANK_MAX];
|
|
504
|
+
for ( k = 0; k < CA_RANK_MAX; k++ ) in_slab[k] = 0;
|
|
505
|
+
for ( k = 0; k < naxes; k++ ) {
|
|
506
|
+
int8_t ax = slab_axes[k];
|
|
507
|
+
if ( ax < 0 || ax >= ca->ndim ) {
|
|
508
|
+
rb_raise(rb_eArgError,
|
|
509
|
+
"rb_ca_new_reduced: slab_axes[%d]=%d out of range [0, %d)",
|
|
510
|
+
(int) k, (int) ax, (int) ca->ndim);
|
|
511
|
+
}
|
|
512
|
+
if ( in_slab[ax] ) {
|
|
513
|
+
rb_raise(rb_eArgError,
|
|
514
|
+
"rb_ca_new_reduced: duplicate axis %d in slab_axes", (int) ax);
|
|
515
|
+
}
|
|
516
|
+
in_slab[ax] = 1;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
ca_size_t out_dim[CA_RANK_MAX];
|
|
520
|
+
int8_t out_ndim = 0;
|
|
521
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
522
|
+
if ( !in_slab[k] ) {
|
|
523
|
+
out_dim[out_ndim++] = ca->dim[k];
|
|
524
|
+
}
|
|
525
|
+
else if ( keep_axis ) {
|
|
526
|
+
/* keep_axis: retain each reduced axis as a length-1 axis instead
|
|
527
|
+
of dropping it (= automation of view[..., :_]).
|
|
528
|
+
Element count and row-major order are unchanged, so the kernel
|
|
529
|
+
slab walk writes the output identically. */
|
|
530
|
+
out_dim[out_ndim++] = 1;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
if ( out_ndim == 0 ) {
|
|
534
|
+
/* Full reduction without keep_axis: collapse to a 1-element array.
|
|
535
|
+
(With keep_axis, full reduction already produced [1, 1, ..., 1]
|
|
536
|
+
above so out_ndim == ca->ndim and this branch is not taken.) */
|
|
537
|
+
out_dim[0] = 1;
|
|
538
|
+
out_ndim = 1;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if ( data_type < 0 || data_type >= CA_NTYPE ) {
|
|
542
|
+
rb_raise(rb_eArgError,
|
|
543
|
+
"rb_ca_new_reduced: data_type=%d out of range", (int) data_type);
|
|
544
|
+
}
|
|
545
|
+
if ( bytes <= 0 ) {
|
|
546
|
+
rb_raise(rb_eArgError,
|
|
547
|
+
"rb_ca_new_reduced: bytes=%ld invalid for data_type=%d",
|
|
548
|
+
(long) bytes, (int) data_type);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return rb_carray_new(data_type, out_ndim, out_dim, bytes, NULL);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/* Fixed-element-size wrapper: the byte width is looked up from ca_sizeof,
|
|
555
|
+
which rejects CA_FIXLEN / CA_OBJECT (runtime-width / VALUE cells). A
|
|
556
|
+
reduction whose output is a runtime-width data_type (fixlen min / max)
|
|
557
|
+
must call rb_ca_new_reduced_bytes with the source's own byte width. */
|
|
558
|
+
VALUE
|
|
559
|
+
rb_ca_new_reduced (VALUE self, int8_t *slab_axes, int8_t naxes, int32_t data_type,
|
|
560
|
+
int keep_axis)
|
|
561
|
+
{
|
|
562
|
+
if ( data_type < 0 || data_type >= CA_NTYPE ) {
|
|
563
|
+
rb_raise(rb_eArgError,
|
|
564
|
+
"rb_ca_new_reduced: data_type=%d out of range", (int) data_type);
|
|
565
|
+
}
|
|
566
|
+
ca_size_t bytes = ca_sizeof[data_type];
|
|
567
|
+
if ( bytes <= 0 ) {
|
|
568
|
+
rb_raise(rb_eArgError,
|
|
569
|
+
"rb_ca_new_reduced: data_type=%d unsupported (CA_FIXLEN/OBJECT not supported)",
|
|
570
|
+
(int) data_type);
|
|
571
|
+
}
|
|
572
|
+
return rb_ca_new_reduced_bytes(self, slab_axes, naxes, data_type, bytes, keep_axis);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/* ------------------------------------------------------------------- */
|
|
576
|
+
|
|
577
|
+
/* Parse the variadic axis argument of a reduction kernel into a
|
|
578
|
+
sort-ascending
|
|
579
|
+
int8_t array, with full validation.
|
|
580
|
+
|
|
581
|
+
Accepts:
|
|
582
|
+
- Integer args: kernel(0, 2, 3) → axes = {0, 2, 3}
|
|
583
|
+
- Single Array arg: kernel([0, 2, 3]) → axes = {0, 2, 3}
|
|
584
|
+
- Negative axes (Python-style): -1 = innermost, normalised to
|
|
585
|
+
positive in-range indices before validation
|
|
586
|
+
|
|
587
|
+
Validation (raises ArgumentError on failure):
|
|
588
|
+
- argc == 0 → no axes given
|
|
589
|
+
- naxes > ca->ndim → too many axes
|
|
590
|
+
- any axis out of [0, ca->ndim) → range
|
|
591
|
+
- duplicate axes → duplicate
|
|
592
|
+
|
|
593
|
+
Returns the validated naxes (= count of axes written to out_axes[]).
|
|
594
|
+
out_axes[] is filled with the parsed axes in *input order* (= NOT
|
|
595
|
+
pre-sorted; canonicalisation to ascending order happens inside
|
|
596
|
+
init_l2 CA_SLAB_AXES, so callers can pass the user's order directly). */
|
|
597
|
+
/* Core validation: takes raw items[] (each must be Integer / Symbol-able
|
|
598
|
+
to NUM2SIZE) of length `count`, normalises + range-checks + duplicate-
|
|
599
|
+
checks, fills out_axes[] in input order. Shared between the legacy
|
|
600
|
+
variadic entry (rb_ca_parse_reduce_axes) and the kwarg entry
|
|
601
|
+
(rb_ca_parse_reduce_axes_kw). ctx is a short label embedded in error
|
|
602
|
+
messages so callers can disambiguate which entry raised. */
|
|
603
|
+
static int8_t
|
|
604
|
+
parse_axes_items (const VALUE *items, int count, CArray *ca,
|
|
605
|
+
int8_t *out_axes, const char *ctx)
|
|
606
|
+
{
|
|
607
|
+
int i;
|
|
608
|
+
int8_t seen[CA_RANK_MAX];
|
|
609
|
+
|
|
610
|
+
if ( count <= 0 ) {
|
|
611
|
+
rb_raise(rb_eArgError, "%s: empty axes array", ctx);
|
|
612
|
+
}
|
|
613
|
+
if ( count > CA_RANK_MAX ) {
|
|
614
|
+
rb_raise(rb_eArgError,
|
|
615
|
+
"%s: too many axes (%d > CA_RANK_MAX=%d)",
|
|
616
|
+
ctx, count, CA_RANK_MAX);
|
|
617
|
+
}
|
|
618
|
+
if ( count > ca->ndim ) {
|
|
619
|
+
rb_raise(rb_eArgError,
|
|
620
|
+
"%s: too many axes (%d > ndim=%d)",
|
|
621
|
+
ctx, count, (int) ca->ndim);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
for ( i = 0; i < CA_RANK_MAX; i++ ) seen[i] = 0;
|
|
625
|
+
for ( i = 0; i < count; i++ ) {
|
|
626
|
+
ca_size_t a = NUM2SIZE(items[i]);
|
|
627
|
+
if ( a < 0 ) a += ca->ndim;
|
|
628
|
+
if ( a < 0 || a >= ca->ndim ) {
|
|
629
|
+
rb_raise(rb_eIndexError,
|
|
630
|
+
"%s: axis %ld out of range [0, %d)",
|
|
631
|
+
ctx, (long) a, (int) ca->ndim);
|
|
632
|
+
}
|
|
633
|
+
if ( seen[a] ) {
|
|
634
|
+
rb_raise(rb_eArgError,
|
|
635
|
+
"%s: duplicate axis %ld", ctx, (long) a);
|
|
636
|
+
}
|
|
637
|
+
seen[a] = 1;
|
|
638
|
+
out_axes[i] = (int8_t) a;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return (int8_t) count;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
int8_t
|
|
645
|
+
rb_ca_parse_reduce_axes (int argc, VALUE *argv, CArray *ca, int8_t *out_axes)
|
|
646
|
+
{
|
|
647
|
+
int i;
|
|
648
|
+
|
|
649
|
+
/* argc == 0 means "full reduction over all axes" -- matches legacy
|
|
650
|
+
CArray#sum etc. This contract keeps the ki kernels drop-in
|
|
651
|
+
replacements for the legacy stat dispatchers. */
|
|
652
|
+
if ( argc <= 0 ) {
|
|
653
|
+
for ( i = 0; i < ca->ndim; i++ ) {
|
|
654
|
+
out_axes[i] = (int8_t) i;
|
|
655
|
+
}
|
|
656
|
+
return (int8_t) ca->ndim;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/* Detect single-Array call form: foo([0, 2]) */
|
|
660
|
+
const VALUE *items = (const VALUE *) argv;
|
|
661
|
+
int count = argc;
|
|
662
|
+
if ( argc == 1 && TYPE(argv[0]) == T_ARRAY ) {
|
|
663
|
+
VALUE arr = argv[0];
|
|
664
|
+
count = (int) RARRAY_LEN(arr);
|
|
665
|
+
items = (const VALUE *) RARRAY_CONST_PTR(arr);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
return parse_axes_items(items, count, ca, out_axes,
|
|
669
|
+
"rb_ca_parse_reduce_axes");
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/* Kwarg form of rb_ca_parse_reduce_axes — accepts the `axis:` value as
|
|
673
|
+
extracted by the caller via rb_scan_args(..., "0:", &kw_hash) +
|
|
674
|
+
rb_get_kwargs (or equivalent), and dispatches:
|
|
675
|
+
|
|
676
|
+
axis_val == Qnil or Qundef → full reduction (= all axes)
|
|
677
|
+
axis_val Integer → single axis (negative normalised)
|
|
678
|
+
axis_val Array of Integer → multiple axes in input order
|
|
679
|
+
anything else → TypeError
|
|
680
|
+
|
|
681
|
+
Validation (range / duplicates / overflow) is identical to the
|
|
682
|
+
variadic entry. out_axes[] receives axes in input order. Returns
|
|
683
|
+
the validated naxes. */
|
|
684
|
+
int8_t
|
|
685
|
+
rb_ca_parse_reduce_axes_kw_ctx (VALUE axis_val, CArray *ca, int8_t *out_axes,
|
|
686
|
+
const char *ctx)
|
|
687
|
+
{
|
|
688
|
+
int i;
|
|
689
|
+
|
|
690
|
+
if ( axis_val == Qnil || axis_val == Qundef ) {
|
|
691
|
+
for ( i = 0; i < ca->ndim; i++ ) {
|
|
692
|
+
out_axes[i] = (int8_t) i;
|
|
693
|
+
}
|
|
694
|
+
return (int8_t) ca->ndim;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
if ( TYPE(axis_val) == T_ARRAY ) {
|
|
698
|
+
int count = (int) RARRAY_LEN(axis_val);
|
|
699
|
+
const VALUE *items = (const VALUE *) RARRAY_CONST_PTR(axis_val);
|
|
700
|
+
return parse_axes_items(items, count, ca, out_axes, ctx);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if ( rb_obj_is_kind_of(axis_val, rb_cInteger) ) {
|
|
704
|
+
return parse_axes_items(&axis_val, 1, ca, out_axes, ctx);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
rb_raise(rb_eTypeError,
|
|
708
|
+
"%s: axis: must be nil, Integer, or "
|
|
709
|
+
"Array of Integer (got %"PRIsVALUE")",
|
|
710
|
+
ctx, rb_obj_class(axis_val));
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
int8_t
|
|
714
|
+
rb_ca_parse_reduce_axes_kw (VALUE axis_val, CArray *ca, int8_t *out_axes)
|
|
715
|
+
{
|
|
716
|
+
return rb_ca_parse_reduce_axes_kw_ctx(axis_val, ca, out_axes,
|
|
717
|
+
"rb_ca_parse_reduce_axes_kw");
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/* ------------------------------------------------------------------- */
|
|
721
|
+
|
|
722
|
+
/* api: ca_wrap_struct_as
|
|
723
|
+
wraps CArray struct in C -> Ruby's object, with the Ruby class chosen
|
|
724
|
+
by the caller instead of taken from ca_class[obj_type].
|
|
725
|
+
|
|
726
|
+
The TypedData tag still comes from obj_type. Only the class is the
|
|
727
|
+
caller's; the tag is what GetCArray and every dispatch path look at,
|
|
728
|
+
so decoupling the two leaves those paths untouched. klass must be a
|
|
729
|
+
subclass of the class registered for obj_type -- that is the caller's
|
|
730
|
+
responsibility, not checked here.
|
|
731
|
+
*/
|
|
732
|
+
|
|
733
|
+
VALUE
|
|
734
|
+
ca_wrap_struct_as (void *ap, VALUE klass)
|
|
735
|
+
{
|
|
736
|
+
CArray *ca = (CArray *) ap;
|
|
737
|
+
return TypedData_Wrap_Struct(klass, ca_typeddata[ca->obj_type], ca);
|
|
738
|
+
}
|
|
739
|
+
|
|
399
740
|
/* api: ca_wrap_struct
|
|
400
|
-
wraps CArray struct in C -> Ruby's object
|
|
741
|
+
wraps CArray struct in C -> Ruby's object
|
|
401
742
|
*/
|
|
402
743
|
|
|
403
744
|
VALUE
|
|
404
745
|
ca_wrap_struct (void *ap)
|
|
405
746
|
{
|
|
406
747
|
CArray *ca = (CArray *) ap;
|
|
407
|
-
return
|
|
748
|
+
return ca_wrap_struct_as(ap, ca_class[ca->obj_type]);
|
|
408
749
|
}
|
|
409
750
|
|
|
410
751
|
/* ------------------------------------------------------------------- */
|
|
411
752
|
|
|
412
|
-
/* calculate index from address
|
|
753
|
+
/* calculate index from address.
|
|
754
|
+
Hot path: called per fetch_addr dispatch when the view has no
|
|
755
|
+
dedicated fetch_addr slot (= CAStride family and others that only
|
|
756
|
+
implement fetch_index). 1-D / 2-D / 3-D fast paths skip the
|
|
757
|
+
generic divmod loop; higher-ndim falls through. */
|
|
413
758
|
|
|
414
759
|
void
|
|
415
760
|
ca_addr2index (void *ap, ca_size_t addr, ca_size_t *idx)
|
|
416
761
|
{
|
|
417
762
|
CArray *ca = (CArray *) ap;
|
|
418
763
|
ca_size_t *dim = ca->dim;
|
|
764
|
+
int8_t ndim = ca->ndim;
|
|
419
765
|
int8_t i;
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
766
|
+
switch (ndim) {
|
|
767
|
+
case 1:
|
|
768
|
+
idx[0] = addr;
|
|
769
|
+
return;
|
|
770
|
+
case 2: {
|
|
771
|
+
ca_size_t d1 = dim[1];
|
|
772
|
+
idx[1] = addr % d1;
|
|
773
|
+
idx[0] = addr / d1;
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
case 3: {
|
|
777
|
+
ca_size_t d1 = dim[1], d2 = dim[2];
|
|
778
|
+
idx[2] = addr % d2;
|
|
779
|
+
addr /= d2;
|
|
780
|
+
idx[1] = addr % d1;
|
|
781
|
+
idx[0] = addr / d1;
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
default:
|
|
785
|
+
for (i = ndim - 1; i >= 0; i--) {
|
|
786
|
+
idx[i] = addr % dim[i];
|
|
787
|
+
addr /= dim[i];
|
|
788
|
+
}
|
|
423
789
|
}
|
|
424
790
|
}
|
|
425
791
|
|
|
426
|
-
/* calculate address from index
|
|
792
|
+
/* calculate address from index. 1-D / 2-D / 3-D fast paths skip the
|
|
793
|
+
loop entirely. Compiler typically inlines the generic loop fine
|
|
794
|
+
but the fast paths help where this function is called via pointer. */
|
|
427
795
|
|
|
428
796
|
ca_size_t
|
|
429
797
|
ca_index2addr (void *ap, ca_size_t *idx)
|
|
430
798
|
{
|
|
431
799
|
CArray *ca = (CArray *) ap;
|
|
432
800
|
ca_size_t *dim = ca->dim;
|
|
433
|
-
int8_t
|
|
434
|
-
|
|
435
|
-
n
|
|
436
|
-
|
|
437
|
-
|
|
801
|
+
int8_t ndim = ca->ndim;
|
|
802
|
+
int8_t i;
|
|
803
|
+
ca_size_t n;
|
|
804
|
+
switch (ndim) {
|
|
805
|
+
case 1:
|
|
806
|
+
return idx[0];
|
|
807
|
+
case 2:
|
|
808
|
+
return dim[1] * idx[0] + idx[1];
|
|
809
|
+
case 3:
|
|
810
|
+
return (dim[1] * idx[0] + idx[1]) * dim[2] + idx[2];
|
|
811
|
+
default:
|
|
812
|
+
n = idx[0];
|
|
813
|
+
for (i = 1; i < ndim; i++) {
|
|
814
|
+
n = dim[i] * n + idx[i];
|
|
815
|
+
}
|
|
816
|
+
return n;
|
|
438
817
|
}
|
|
439
|
-
return n;
|
|
440
818
|
}
|
|
441
819
|
|
|
442
820
|
/* ------------------------------------------------------------------- */
|
|
@@ -489,169 +867,750 @@ ca_test_cyclic_check(void *ap, void *ptr)
|
|
|
489
867
|
}
|
|
490
868
|
|
|
491
869
|
/* ------------------------------------------------------------------- */
|
|
870
|
+
/* CArray offers no per-cell ptr accessor (ca_ptr_at_addr /
|
|
871
|
+
ca_ptr_at_index): such a slot is structurally unsafe -- CABitarray /
|
|
872
|
+
CABitfield have no byte-addressable cell, and CAByteSwap / CAFake would
|
|
873
|
+
hand back bytes in the wrong data_type / endian. Internal code that
|
|
874
|
+
already holds an attached view uses direct `ca->ptr + ca->bytes * addr`
|
|
875
|
+
arithmetic; external ext gems use ca_fetch_addr / ca_fetch_index
|
|
876
|
+
(data_type-correct via the xfer_addrs / xfer_index dispatch) for
|
|
877
|
+
per-cell access. */
|
|
878
|
+
|
|
879
|
+
/* fetch / store at a single linear address: thin wrappers over
|
|
880
|
+
ca_xfer_addrs. */
|
|
492
881
|
|
|
493
|
-
|
|
882
|
+
void
|
|
883
|
+
ca_fetch_addr (void *ap, ca_size_t addr, void *pval)
|
|
884
|
+
{
|
|
885
|
+
ca_xfer_addrs(ap, 1, &addr, pval, CA_XFER_GET);
|
|
886
|
+
}
|
|
494
887
|
|
|
495
|
-
void
|
|
496
|
-
|
|
888
|
+
void
|
|
889
|
+
ca_store_addr (void *ap, ca_size_t addr, void *pval)
|
|
497
890
|
{
|
|
498
|
-
|
|
891
|
+
ca_xfer_addrs(ap, 1, &addr, pval, CA_XFER_PUT);
|
|
892
|
+
}
|
|
499
893
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
case CA_OBJ_REFER:
|
|
505
|
-
return ((CARefer*)ca)->parent->ptr + ca->bytes * addr;
|
|
506
|
-
default:
|
|
507
|
-
return ca->ptr + ca->bytes * addr;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
894
|
+
/* per-cell transfer by multi-dim index. ca_xfer_index is the primary
|
|
895
|
+
entry; ca_fetch_index / ca_store_index are thin wrappers (kept as
|
|
896
|
+
public C-API for external ext gems). Every view supplies an
|
|
897
|
+
xfer_index slot. */
|
|
510
898
|
|
|
511
|
-
|
|
899
|
+
static inline void
|
|
900
|
+
ca_xfer_index_dispatch (CArray *ca, ca_size_t *idx, void *data, int dir)
|
|
901
|
+
{
|
|
902
|
+
if ( ! ca_func[ca->obj_type].xfer_index ) {
|
|
903
|
+
rb_raise(rb_eRuntimeError,
|
|
904
|
+
"[BUG] xfer_index not defined for object type <%i>",
|
|
905
|
+
ca->obj_type);
|
|
906
|
+
}
|
|
907
|
+
ca_func[ca->obj_type].xfer_index(ca, idx, data, dir);
|
|
512
908
|
}
|
|
513
909
|
|
|
514
|
-
|
|
910
|
+
struct ca_xfer_index_args {
|
|
911
|
+
CArray *ca;
|
|
912
|
+
ca_size_t *idx;
|
|
913
|
+
char *ptr;
|
|
914
|
+
};
|
|
515
915
|
|
|
516
|
-
|
|
517
|
-
|
|
916
|
+
static VALUE
|
|
917
|
+
ca_xfer_index_get_body (VALUE arg)
|
|
518
918
|
{
|
|
519
|
-
|
|
520
|
-
|
|
919
|
+
struct ca_xfer_index_args *d = (struct ca_xfer_index_args *) arg;
|
|
920
|
+
ca_xfer_index_dispatch(d->ca, d->idx, d->ptr, CA_XFER_GET);
|
|
921
|
+
ca_test_cyclic_check(d->ca, d->ptr);
|
|
922
|
+
return Qnil;
|
|
521
923
|
}
|
|
522
924
|
|
|
523
|
-
/* fetch data of the element at given address to memory pointed by pval */
|
|
524
|
-
|
|
525
925
|
void
|
|
526
|
-
|
|
926
|
+
ca_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
|
|
527
927
|
{
|
|
528
928
|
CArray *ca = (CArray *) ap;
|
|
529
|
-
char *ptr = (char *)pval;
|
|
530
|
-
|
|
531
|
-
ca_set_cyclic_check(ca);
|
|
532
929
|
|
|
533
|
-
if ( ca
|
|
534
|
-
|
|
930
|
+
if ( dir == CA_XFER_PUT && ca_is_readonly(ca) ) {
|
|
931
|
+
rb_raise(rb_eRuntimeError, "can not store data to read-only array");
|
|
535
932
|
}
|
|
536
|
-
|
|
537
|
-
|
|
933
|
+
|
|
934
|
+
/* Fast path: non-CA_OBJECT element type needs no GC protection. */
|
|
935
|
+
if ( ca->data_type != CA_OBJECT ) {
|
|
936
|
+
ca_xfer_index_dispatch(ca, idx, data, dir);
|
|
937
|
+
return;
|
|
538
938
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
939
|
+
|
|
940
|
+
/* CA_OBJECT slow path: cyclic check (+ rb_protect on GET). */
|
|
941
|
+
if ( dir == CA_XFER_GET ) {
|
|
942
|
+
struct ca_xfer_index_args args;
|
|
943
|
+
int state = 0;
|
|
944
|
+
args.ca = ca;
|
|
945
|
+
args.idx = idx;
|
|
946
|
+
args.ptr = (char *) data;
|
|
947
|
+
ca_set_cyclic_check(ca);
|
|
948
|
+
rb_protect(ca_xfer_index_get_body, (VALUE) &args, &state);
|
|
949
|
+
ca_clear_cyclic_check(ca);
|
|
950
|
+
if ( state ) {
|
|
951
|
+
rb_jump_tag(state);
|
|
952
|
+
}
|
|
543
953
|
}
|
|
544
954
|
else {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
ca->obj_type);
|
|
955
|
+
ca_set_cyclic_check(ca);
|
|
956
|
+
ca_xfer_index_dispatch(ca, idx, data, CA_XFER_PUT);
|
|
957
|
+
ca_clear_cyclic_check(ca);
|
|
549
958
|
}
|
|
959
|
+
}
|
|
550
960
|
|
|
551
|
-
|
|
961
|
+
/* fetch data of the element at given index to memory pointed by pval */
|
|
552
962
|
|
|
553
|
-
|
|
963
|
+
void
|
|
964
|
+
ca_fetch_index (void *ap, ca_size_t *idx, void *pval)
|
|
965
|
+
{
|
|
966
|
+
ca_xfer_index(ap, idx, pval, CA_XFER_GET);
|
|
554
967
|
}
|
|
555
968
|
|
|
556
|
-
/* store value pointed by pval to the element at given
|
|
969
|
+
/* store value pointed by pval to the element at given index */
|
|
557
970
|
|
|
558
971
|
void
|
|
559
|
-
|
|
972
|
+
ca_store_index (void *ap, ca_size_t *idx, void *pval)
|
|
560
973
|
{
|
|
561
|
-
|
|
562
|
-
|
|
974
|
+
ca_xfer_index(ap, idx, pval, CA_XFER_PUT);
|
|
975
|
+
}
|
|
563
976
|
|
|
564
|
-
|
|
977
|
+
/* gather / scatter over a list of linear addresses. ca_xfer_addrs is the
|
|
978
|
+
primary addr entry; ca_fetch_addr / ca_store_addr are thin wrappers
|
|
979
|
+
(kept as public C-API for external ext gems).
|
|
980
|
+
|
|
981
|
+
Dispatch core (no GC protection; caller handles CA_OBJECT):
|
|
982
|
+
1. ca->ptr present (entity / attached / alias) -> direct memcpy at addr.
|
|
983
|
+
2. xfer_addrs slot -> use it (every view supplies one). */
|
|
984
|
+
|
|
985
|
+
/* Detect a sequential addr run (addrs[i] == addrs[0] + i for all i) so a
|
|
986
|
+
single bulk memcpy replaces the per-cell loop. O(n) integer compare
|
|
987
|
+
with early-exit on first mismatch. This fires for dominant-true mask
|
|
988
|
+
workloads (ca[:is_not_masked] += v etc.): when the boolean is mostly
|
|
989
|
+
TRUE, the view->parent addr remap degenerates to [0..n-1]. */
|
|
990
|
+
int
|
|
991
|
+
ca_xfer_addrs_is_sequential_run (ca_size_t n, ca_size_t *addrs,
|
|
992
|
+
ca_size_t *base_out)
|
|
993
|
+
{
|
|
994
|
+
ca_size_t base, i;
|
|
995
|
+
if ( n == 0 ) { *base_out = 0; return 1; }
|
|
996
|
+
base = addrs[0];
|
|
997
|
+
for ( i = 1; i < n; i++ ) {
|
|
998
|
+
if ( addrs[i] != base + i ) return 0;
|
|
999
|
+
}
|
|
1000
|
+
*base_out = base;
|
|
1001
|
+
return 1;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
static void
|
|
1005
|
+
ca_xfer_addrs_dispatch (CArray *ca, ca_size_t n, ca_size_t *addrs,
|
|
1006
|
+
void *data, int dir)
|
|
1007
|
+
{
|
|
1008
|
+
char *d = (char *) data;
|
|
1009
|
+
ca_size_t i, base;
|
|
1010
|
+
|
|
1011
|
+
if ( ca->ptr ) { /* attached / entity / alias: fast path */
|
|
1012
|
+
if ( ca_xfer_addrs_is_sequential_run(n, addrs, &base) ) {
|
|
1013
|
+
/* Sequential-run fast path: single bulk memcpy. Triggered by any
|
|
1014
|
+
sub-region run ([k..k+m-1] form), not whole-view limited. Safe:
|
|
1015
|
+
detection is view-structural (the addr shape, not a workload tag)
|
|
1016
|
+
and does not call xfer_all. */
|
|
1017
|
+
char *p = ca->ptr + ca->bytes * base;
|
|
1018
|
+
ca_size_t nbytes = n * ca->bytes;
|
|
1019
|
+
if ( dir == CA_XFER_GET ) memcpy(d, p, nbytes);
|
|
1020
|
+
else memcpy(p, d, nbytes);
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
/* Per-cell loop for arbitrary (non-sequential) addrs:
|
|
1024
|
+
fancy gather/scatter from CASelect 2-D mapper, CSA sparse mask, etc. */
|
|
1025
|
+
for ( i = 0; i < n; i++ ) {
|
|
1026
|
+
char *p = ca->ptr + ca->bytes * addrs[i];
|
|
1027
|
+
if ( dir == CA_XFER_GET ) memcpy(d + i * ca->bytes, p, ca->bytes);
|
|
1028
|
+
else memcpy(p, d + i * ca->bytes, ca->bytes);
|
|
1029
|
+
}
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
if ( ! ca_func[ca->obj_type].xfer_addrs ) {
|
|
565
1034
|
rb_raise(rb_eRuntimeError,
|
|
566
|
-
"
|
|
1035
|
+
"[BUG] xfer_addrs not defined for object type <%i>",
|
|
1036
|
+
ca->obj_type);
|
|
567
1037
|
}
|
|
1038
|
+
ca_func[ca->obj_type].xfer_addrs(ca, n, addrs, data, dir);
|
|
1039
|
+
}
|
|
568
1040
|
|
|
569
|
-
|
|
1041
|
+
struct ca_xfer_addrs_args {
|
|
1042
|
+
CArray *ca;
|
|
1043
|
+
ca_size_t n;
|
|
1044
|
+
ca_size_t *addrs;
|
|
1045
|
+
char *data;
|
|
1046
|
+
};
|
|
570
1047
|
|
|
571
|
-
|
|
572
|
-
|
|
1048
|
+
static VALUE
|
|
1049
|
+
ca_xfer_addrs_get_body (VALUE arg)
|
|
1050
|
+
{
|
|
1051
|
+
struct ca_xfer_addrs_args *d = (struct ca_xfer_addrs_args *) arg;
|
|
1052
|
+
ca_size_t i;
|
|
1053
|
+
ca_xfer_addrs_dispatch(d->ca, d->n, d->addrs, d->data, CA_XFER_GET);
|
|
1054
|
+
for ( i = 0; i < d->n; i++ ) {
|
|
1055
|
+
ca_test_cyclic_check(d->ca, d->data + i * d->ca->bytes);
|
|
573
1056
|
}
|
|
574
|
-
|
|
575
|
-
|
|
1057
|
+
return Qnil;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
void
|
|
1061
|
+
ca_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *data, int dir)
|
|
1062
|
+
{
|
|
1063
|
+
CArray *ca = (CArray *) ap;
|
|
1064
|
+
|
|
1065
|
+
if ( dir == CA_XFER_PUT && ca_is_readonly(ca) ) {
|
|
1066
|
+
rb_raise(rb_eRuntimeError, "can not store data to read-only array");
|
|
576
1067
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
1068
|
+
|
|
1069
|
+
/* Fast path: non-CA_OBJECT element type needs no GC protection. */
|
|
1070
|
+
if ( ca->data_type != CA_OBJECT ) {
|
|
1071
|
+
ca_xfer_addrs_dispatch(ca, n, addrs, data, dir);
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/* CA_OBJECT slow path: cyclic check (+ rb_protect on GET). */
|
|
1076
|
+
if ( dir == CA_XFER_GET ) {
|
|
1077
|
+
struct ca_xfer_addrs_args args;
|
|
1078
|
+
int state = 0;
|
|
1079
|
+
args.ca = ca;
|
|
1080
|
+
args.n = n;
|
|
1081
|
+
args.addrs = addrs;
|
|
1082
|
+
args.data = (char *) data;
|
|
1083
|
+
ca_set_cyclic_check(ca);
|
|
1084
|
+
rb_protect(ca_xfer_addrs_get_body, (VALUE) &args, &state);
|
|
1085
|
+
ca_clear_cyclic_check(ca);
|
|
1086
|
+
if ( state ) {
|
|
1087
|
+
rb_jump_tag(state);
|
|
1088
|
+
}
|
|
581
1089
|
}
|
|
582
1090
|
else {
|
|
1091
|
+
ca_set_cyclic_check(ca);
|
|
1092
|
+
ca_xfer_addrs_dispatch(ca, n, addrs, data, CA_XFER_PUT);
|
|
583
1093
|
ca_clear_cyclic_check(ca);
|
|
584
|
-
rb_raise(rb_eRuntimeError,
|
|
585
|
-
"[BUG] store_addr or store_index "\
|
|
586
|
-
"are not defined for object type <%i>",
|
|
587
|
-
ca->obj_type);
|
|
588
1094
|
}
|
|
589
|
-
|
|
590
|
-
ca_clear_cyclic_check(ca);
|
|
591
|
-
|
|
592
1095
|
}
|
|
593
1096
|
|
|
594
|
-
/*
|
|
595
|
-
|
|
1097
|
+
/* gather / scatter over a STRIDED region of the view. The region is
|
|
1098
|
+
described in the view's own byte space:
|
|
1099
|
+
|
|
1100
|
+
base = Σ starts[k] * native_byte_stride[k] (the region's first cell,
|
|
1101
|
+
starts[] given as a per-axis index into the view's row-major layout)
|
|
1102
|
+
cell(idx) byte offset = base + Σ idx[k] * strides[k] for idx in [0,counts)
|
|
1103
|
+
strides[] = SRC access byte strides into the view (NOT a contiguous region;
|
|
1104
|
+
carries sub-sampling / transpose from the composed leaf access)
|
|
1105
|
+
|
|
1106
|
+
data is a CONTIGUOUS caller buffer in row-major order over counts[]. Local
|
|
1107
|
+
materialise of the requested region only -- never the whole view.
|
|
1108
|
+
|
|
1109
|
+
One example is CASelect (step = strides[0]/bytes is a view
|
|
1110
|
+
access step, data delivered contiguously to the parent). The boundary wiring
|
|
1111
|
+
passes a CAStride leaf's composed access (composed_base, composed_strides,
|
|
1112
|
+
leaf->dim) straight through.
|
|
1113
|
+
|
|
1114
|
+
Dispatch core (no GC protection; caller handles CA_OBJECT):
|
|
1115
|
+
1. ca->ptr present (entity / attached / alias) -> strided memcpy.
|
|
1116
|
+
2. xfer_stride slot -> the view delivers its own region (recurse / cast /
|
|
1117
|
+
gather-translate).
|
|
1118
|
+
3. else per cell: byte offset -> flat addr -> index -> ca_xfer_index_dispatch
|
|
1119
|
+
(universal fallback, no whole-view attach). */
|
|
1120
|
+
|
|
1121
|
+
/* Cache-tiled 2-D transpose fast path (helper for ca_xfer_stride_dispatch).
|
|
1122
|
+
|
|
1123
|
+
------------------------------------------------------------------------
|
|
1124
|
+
PROBLEM
|
|
1125
|
+
------------------------------------------------------------------------
|
|
1126
|
+
When the dispatcher detects that slab merge fails (innermost stride is
|
|
1127
|
+
not contig at ca->bytes) AND the access pattern looks like a 2-D
|
|
1128
|
+
transpose (outer view axis IS source-contig: strides[0] == bytes, inner
|
|
1129
|
+
view axis is non-contig: strides[1] != bytes), the naive prefix
|
|
1130
|
+
odometer issues counts[0] * counts[1] independent memcpy(_,_,bytes)
|
|
1131
|
+
calls. Each call reads one cell of `bytes` from ca->ptr at a different
|
|
1132
|
+
row of source -- with strides[1] huge (e.g. N * 8 for f64), every read
|
|
1133
|
+
touches a different cache line and often a different 4 KB page.
|
|
1134
|
+
|
|
1135
|
+
At N=2000 / bytes=8 (parent data_type = float64), this is 4M random
|
|
1136
|
+
reads. DRAM random-access bandwidth bottoms out around 1-2 GB/s, so
|
|
1137
|
+
the per-cell loop is dominated by cache-line / page misses on a large
|
|
1138
|
+
working set.
|
|
1139
|
+
|
|
1140
|
+
------------------------------------------------------------------------
|
|
1141
|
+
TECHNIQUE: cache-tiled transpose with L1-resident scratch
|
|
1142
|
+
------------------------------------------------------------------------
|
|
1143
|
+
Process the iteration space in 32x32 tiles, staging each tile through
|
|
1144
|
+
a stack-allocated `scratch` buffer that fits in L1 (32 * 32 * 16 =
|
|
1145
|
+
16 KB; bytes <= 16 ceiling).
|
|
1146
|
+
|
|
1147
|
+
Load pass:
|
|
1148
|
+
Read `Tj` rows of source contiguously, each `Ti * bytes` long, into
|
|
1149
|
+
scratch[j_t * Ti + i_t]. Per-tile DRAM traffic: Tj sequential
|
|
1150
|
+
reads of small (256 B at bytes=8) runs. Outer loop carries source
|
|
1151
|
+
row band [sr0..sr0+Tj) -- those rows stay resident in L2 across
|
|
1152
|
+
the inner sc0 sweep (32 rows * N * bytes = 512 KB at N=2000, fits
|
|
1153
|
+
in any modern L2).
|
|
1154
|
+
|
|
1155
|
+
Store pass:
|
|
1156
|
+
For each output row (sc0+i_t), write `Tj` cells contiguously to
|
|
1157
|
+
the data buffer. The source side is the L1-resident scratch read
|
|
1158
|
+
at byte stride `Ti * bytes` -- a small constant stride into a
|
|
1159
|
+
16 KB region, effectively free.
|
|
1160
|
+
|
|
1161
|
+
The key invariant: BOTH DRAM-facing transfers (the load-pass source
|
|
1162
|
+
read and the store-pass data write) are sequential. Random access is
|
|
1163
|
+
confined to the L1 scratch.
|
|
1164
|
+
|
|
1165
|
+
------------------------------------------------------------------------
|
|
1166
|
+
bytes specialisation
|
|
1167
|
+
------------------------------------------------------------------------
|
|
1168
|
+
The store pass's inner loop is the hottest path (Ti * Tj memcpy calls
|
|
1169
|
+
per tile). memcpy(_,_,bytes) with a runtime `bytes` defeats the
|
|
1170
|
+
compiler's small-constant inlining heuristic, so we dispatch on
|
|
1171
|
+
bytes ∈ {1, 2, 4, 8} to a TILED_*_TYPED macro that uses typed pointer
|
|
1172
|
+
arithmetic and explicit stores. At bytes=8 (float64 / int64 -- the
|
|
1173
|
+
dominant case for large 2-D workloads) Clang / gcc generate vectorised
|
|
1174
|
+
loads/stores for the strided scratch reads.
|
|
1175
|
+
|
|
1176
|
+
bytes=16 (cmplx128) and other unusual widths fall through to a generic
|
|
1177
|
+
memcpy loop; correctness is preserved, only the typed-store benefit
|
|
1178
|
+
is lost.
|
|
1179
|
+
|
|
1180
|
+
------------------------------------------------------------------------
|
|
1181
|
+
Why not always tile?
|
|
1182
|
+
------------------------------------------------------------------------
|
|
1183
|
+
When strides[1] IS contig (== bytes), the slab merge already collapses
|
|
1184
|
+
the iteration to a single bulk memcpy. When strides[0] is also non-
|
|
1185
|
+
contig (e.g. strided sub-sampling on BOTH axes), tiling still helps
|
|
1186
|
+
but the gains are smaller; we conservatively limit the trigger to
|
|
1187
|
+
strides[0] == bytes to keep the fast-path predicate cheap and the
|
|
1188
|
+
guarantees unambiguous.
|
|
1189
|
+
|
|
1190
|
+
------------------------------------------------------------------------
|
|
1191
|
+
No attach inside xfer_stride
|
|
1192
|
+
------------------------------------------------------------------------
|
|
1193
|
+
CAREFUL: xfer_stride is a per-region delivery primitive; it must not
|
|
1194
|
+
invoke ca_attach on `ca` itself or any ancestor. Doing so would
|
|
1195
|
+
short-circuit CAStack's multi-parent design and the general "partial
|
|
1196
|
+
materialise instead of whole-view attach" goal. This helper operates
|
|
1197
|
+
strictly on ca->ptr in place. */
|
|
1198
|
+
|
|
1199
|
+
#define CA_TILED_TRANSPOSE_2D_TILE 32
|
|
1200
|
+
|
|
1201
|
+
/* Non-static so cross-file callers (ca_obj_stride.c) can reuse the same
|
|
1202
|
+
tile-block algorithm. `src_base` points at the strided side's [0,0]
|
|
1203
|
+
cell; `dst` is the row-major contig side (M x N over bytes). `strides[0]`
|
|
1204
|
+
= source-contig stride (must equal `bytes`), `strides[1]` = source-strided
|
|
1205
|
+
stride. dir = CA_XFER_GET (strided->contig) / CA_XFER_PUT (contig->strided).
|
|
1206
|
+
|
|
1207
|
+
3 caller sites:
|
|
1208
|
+
- ca_xfer_stride_dispatch ca->ptr path (this file)
|
|
1209
|
+
- ca_stride_func_xfer_stride root-direct (ca_obj_stride.c)
|
|
1210
|
+
- ca_stride_xfer_with_layout general driver (ca_obj_stride.c) */
|
|
596
1211
|
void
|
|
597
|
-
|
|
1212
|
+
ca_xfer_stride_tiled_transpose_2d (char *src_base,
|
|
1213
|
+
ca_size_t bytes_,
|
|
1214
|
+
ca_size_t *counts,
|
|
1215
|
+
ca_size_t *strides,
|
|
1216
|
+
char *data,
|
|
1217
|
+
int dir)
|
|
598
1218
|
{
|
|
599
|
-
|
|
600
|
-
char *
|
|
1219
|
+
enum { TILE = CA_TILED_TRANSPOSE_2D_TILE };
|
|
1220
|
+
char scratch[TILE * TILE * 16]; /* L1-resident, max bytes = 16 */
|
|
1221
|
+
ca_size_t M = counts[0]; /* view outer = source contig direction */
|
|
1222
|
+
ca_size_t N = counts[1]; /* view inner = source non-contig direction */
|
|
1223
|
+
ca_size_t sr0, sc0, i_t, j_t;
|
|
1224
|
+
|
|
1225
|
+
for ( sr0 = 0; sr0 < N; sr0 += TILE ) {
|
|
1226
|
+
ca_size_t Tj = (N - sr0 < TILE) ? (N - sr0) : TILE;
|
|
1227
|
+
for ( sc0 = 0; sc0 < M; sc0 += TILE ) {
|
|
1228
|
+
ca_size_t Ti = (M - sc0 < TILE) ? (M - sc0) : TILE;
|
|
1229
|
+
|
|
1230
|
+
if ( dir == CA_XFER_GET ) {
|
|
1231
|
+
/* Load pass: contig source reads -> scratch[j_t * Ti + i_t]. */
|
|
1232
|
+
for ( j_t = 0; j_t < Tj; j_t++ ) {
|
|
1233
|
+
char *src_row = src_base
|
|
1234
|
+
+ (sr0 + j_t) * strides[1]
|
|
1235
|
+
+ sc0 * bytes_;
|
|
1236
|
+
memcpy(scratch + j_t * Ti * bytes_, src_row, Ti * bytes_);
|
|
1237
|
+
}
|
|
1238
|
+
/* Store pass: strided read from L1 scratch + contig write to data
|
|
1239
|
+
buffer, dispatched by element width. bytes={1,2,4,8} use
|
|
1240
|
+
typed pointer arithmetic so the compiler can vectorise. */
|
|
1241
|
+
#define TILED_GET_TYPED(T) \
|
|
1242
|
+
do { \
|
|
1243
|
+
T *scr = (T *) scratch; \
|
|
1244
|
+
for ( i_t = 0; i_t < Ti; i_t++ ) { \
|
|
1245
|
+
T *out = (T *) (data + ((sc0 + i_t) * N + sr0) * sizeof(T)); \
|
|
1246
|
+
for ( j_t = 0; j_t < Tj; j_t++ ) { \
|
|
1247
|
+
out[j_t] = scr[j_t * Ti + i_t]; \
|
|
1248
|
+
} \
|
|
1249
|
+
} \
|
|
1250
|
+
} while (0)
|
|
1251
|
+
switch ( bytes_ ) {
|
|
1252
|
+
case 1: TILED_GET_TYPED(uint8_t); break;
|
|
1253
|
+
case 2: TILED_GET_TYPED(uint16_t); break;
|
|
1254
|
+
case 4: TILED_GET_TYPED(uint32_t); break;
|
|
1255
|
+
case 8: TILED_GET_TYPED(uint64_t); break;
|
|
1256
|
+
default:
|
|
1257
|
+
for ( i_t = 0; i_t < Ti; i_t++ ) {
|
|
1258
|
+
char *out_row = data + ((sc0 + i_t) * N + sr0) * bytes_;
|
|
1259
|
+
for ( j_t = 0; j_t < Tj; j_t++ ) {
|
|
1260
|
+
memcpy(out_row + j_t * bytes_,
|
|
1261
|
+
scratch + (j_t * Ti + i_t) * bytes_, bytes_);
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
break;
|
|
1265
|
+
}
|
|
1266
|
+
#undef TILED_GET_TYPED
|
|
1267
|
+
}
|
|
1268
|
+
else { /* CA_XFER_PUT: mirror of GET, data drives writes to ca->ptr. */
|
|
1269
|
+
/* Load pass: contig data reads -> scratch (transposed layout). */
|
|
1270
|
+
#define TILED_PUT_TYPED(T) \
|
|
1271
|
+
do { \
|
|
1272
|
+
T *scr = (T *) scratch; \
|
|
1273
|
+
for ( i_t = 0; i_t < Ti; i_t++ ) { \
|
|
1274
|
+
T *in = (T *) (data + ((sc0 + i_t) * N + sr0) * sizeof(T)); \
|
|
1275
|
+
for ( j_t = 0; j_t < Tj; j_t++ ) { \
|
|
1276
|
+
scr[j_t * Ti + i_t] = in[j_t]; \
|
|
1277
|
+
} \
|
|
1278
|
+
} \
|
|
1279
|
+
} while (0)
|
|
1280
|
+
switch ( bytes_ ) {
|
|
1281
|
+
case 1: TILED_PUT_TYPED(uint8_t); break;
|
|
1282
|
+
case 2: TILED_PUT_TYPED(uint16_t); break;
|
|
1283
|
+
case 4: TILED_PUT_TYPED(uint32_t); break;
|
|
1284
|
+
case 8: TILED_PUT_TYPED(uint64_t); break;
|
|
1285
|
+
default:
|
|
1286
|
+
for ( i_t = 0; i_t < Ti; i_t++ ) {
|
|
1287
|
+
char *data_row = data + ((sc0 + i_t) * N + sr0) * bytes_;
|
|
1288
|
+
for ( j_t = 0; j_t < Tj; j_t++ ) {
|
|
1289
|
+
memcpy(scratch + (j_t * Ti + i_t) * bytes_,
|
|
1290
|
+
data_row + j_t * bytes_, bytes_);
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
break;
|
|
1294
|
+
}
|
|
1295
|
+
#undef TILED_PUT_TYPED
|
|
1296
|
+
/* Store pass: contig source writes from scratch. */
|
|
1297
|
+
for ( j_t = 0; j_t < Tj; j_t++ ) {
|
|
1298
|
+
char *src_row = src_base
|
|
1299
|
+
+ (sr0 + j_t) * strides[1]
|
|
1300
|
+
+ sc0 * bytes_;
|
|
1301
|
+
memcpy(src_row, scratch + j_t * Ti * bytes_, Ti * bytes_);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
601
1307
|
|
|
602
|
-
|
|
1308
|
+
/* Shared strided-region walker for the dispatcher (this file) and the CAStride
|
|
1309
|
+
root-direct path (ca_obj_stride.c::ca_stride_func_xfer_stride). This
|
|
1310
|
+
helper consolidates the slab-merge + tile-block + general-driver logic
|
|
1311
|
+
both paths use.
|
|
1312
|
+
|
|
1313
|
+
Callers responsibility: compute `src_base` to already include any per-axis
|
|
1314
|
+
base offset, supply `src_strides[]` as byte strides matching `counts[]`,
|
|
1315
|
+
and provide `data` as a row-major contig buffer over counts in `bytes`-
|
|
1316
|
+
per-cell layout. `dir` is CA_XFER_GET (src -> data) or CA_XFER_PUT
|
|
1317
|
+
(data -> src).
|
|
1318
|
+
|
|
1319
|
+
Inner-loop strategy: slab merge (innermost contig run) + 2-D tile-block
|
|
1320
|
+
transpose at the inner pair + outer-prefix odometer with per-iter memcpy.
|
|
1321
|
+
Inner-loop strategy is NOT shared with ca_stride_xfer_with_layout, which
|
|
1322
|
+
uses ca_stride_gather_run / scatter_run typed runs -- intentionally kept
|
|
1323
|
+
separate to avoid abstraction over two structurally distinct inner
|
|
1324
|
+
strategies. */
|
|
1325
|
+
void
|
|
1326
|
+
ca_xfer_strided_walk (char *src_base,
|
|
1327
|
+
ca_size_t bytes,
|
|
1328
|
+
int8_t ndim,
|
|
1329
|
+
const ca_size_t *counts,
|
|
1330
|
+
const ca_size_t *src_strides,
|
|
1331
|
+
char *data,
|
|
1332
|
+
int dir)
|
|
1333
|
+
{
|
|
1334
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
1335
|
+
ca_size_t doff = 0;
|
|
1336
|
+
int8_t k;
|
|
1337
|
+
|
|
1338
|
+
/* slab merge -- scan innermost contig run (src_strides[k] equals the
|
|
1339
|
+
accumulated slab byte size). This collapses per-cell memcpy(_,_,
|
|
1340
|
+
bytes) loops into per-slab memcpy when the source region is row-major
|
|
1341
|
+
contig. Critical for transform views (CAFake / CAByteSwap / CATile)
|
|
1342
|
+
calling parent.xfer_stride on (N-2)x(N-2)-style interior regions: the
|
|
1343
|
+
inner axis is parent-contig and merges into a single row memcpy. */
|
|
1344
|
+
int8_t slab_start = ndim;
|
|
1345
|
+
ca_size_t slab_bytes = bytes;
|
|
1346
|
+
for ( k = ndim - 1; k >= 0; k-- ) {
|
|
1347
|
+
if ( src_strides[k] != slab_bytes ) break;
|
|
1348
|
+
slab_bytes *= counts[k];
|
|
1349
|
+
slab_start = k;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
if ( slab_start == 0 ) { /* whole region is one contig slab */
|
|
1353
|
+
if ( dir == CA_XFER_GET ) memcpy(data, src_base, slab_bytes);
|
|
1354
|
+
else memcpy(src_base, data, slab_bytes);
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
603
1357
|
|
|
604
|
-
|
|
605
|
-
|
|
1358
|
+
/* Innermost-2-axis tile-block transpose (ndim >= 2 generalisation).
|
|
1359
|
+
When slab merge cannot
|
|
1360
|
+
collapse the innermost axis but the innermost-1 axis is source-contig
|
|
1361
|
+
(= transpose-like at the inner pair), iterate the outer (ndim-2) axes
|
|
1362
|
+
on an odometer and apply the 2-D cache-tiled helper to each inner
|
|
1363
|
+
(counts[ndim-2] x counts[ndim-1]) block. ndim == 2 reduces to
|
|
1364
|
+
outer_n == 0 -- the odometer runs exactly once with soff == 0 (relative
|
|
1365
|
+
to src_base) -- so it is byte-equivalent to the 2-D-only case.
|
|
1366
|
+
Helper operates in place on src_base; no attach is invoked. */
|
|
1367
|
+
if ( ndim >= 2 && bytes <= 16 &&
|
|
1368
|
+
slab_start == ndim &&
|
|
1369
|
+
src_strides[ndim-2] == bytes && src_strides[ndim-1] != bytes ) {
|
|
1370
|
+
int8_t outer_n = ndim - 2;
|
|
1371
|
+
ca_size_t inner_counts[2] = { counts[ndim-2], counts[ndim-1] };
|
|
1372
|
+
ca_size_t inner_strides[2] = { src_strides[ndim-2], src_strides[ndim-1] };
|
|
1373
|
+
ca_size_t inner_dst_bytes = counts[ndim-2] * counts[ndim-1] * bytes;
|
|
1374
|
+
|
|
1375
|
+
for ( k = 0; k < outer_n; k++ ) idx[k] = 0;
|
|
1376
|
+
while ( 1 ) {
|
|
1377
|
+
ca_size_t soff = 0;
|
|
1378
|
+
for ( k = 0; k < outer_n; k++ ) soff += idx[k] * src_strides[k];
|
|
1379
|
+
ca_xfer_stride_tiled_transpose_2d(src_base + soff, bytes,
|
|
1380
|
+
inner_counts, inner_strides,
|
|
1381
|
+
data + doff, dir);
|
|
1382
|
+
doff += inner_dst_bytes;
|
|
1383
|
+
if ( outer_n == 0 ) break;
|
|
1384
|
+
k = outer_n - 1;
|
|
1385
|
+
while ( k >= 0 ) { if ( ++idx[k] < counts[k] ) break; idx[k] = 0; k--; }
|
|
1386
|
+
if ( k < 0 ) break;
|
|
1387
|
+
}
|
|
1388
|
+
return;
|
|
606
1389
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
1390
|
+
|
|
1391
|
+
/* prefix axes [0..slab_start-1] on odometer, slab-sized memcpy per iter. */
|
|
1392
|
+
for ( k = 0; k < slab_start; k++ ) idx[k] = 0;
|
|
1393
|
+
while ( 1 ) {
|
|
1394
|
+
ca_size_t soff = 0;
|
|
1395
|
+
for ( k = 0; k < slab_start; k++ ) soff += idx[k] * src_strides[k];
|
|
1396
|
+
if ( dir == CA_XFER_GET ) memcpy(data + doff, src_base + soff, slab_bytes);
|
|
1397
|
+
else memcpy(src_base + soff, data + doff, slab_bytes);
|
|
1398
|
+
doff += slab_bytes;
|
|
1399
|
+
k = slab_start - 1;
|
|
1400
|
+
while ( k >= 0 ) { if ( ++idx[k] < counts[k] ) break; idx[k] = 0; k--; }
|
|
1401
|
+
if ( k < 0 ) break;
|
|
610
1402
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
/* See the comment on the prototype in carray.h. */
|
|
1406
|
+
int
|
|
1407
|
+
ca_xfer_stride_request_is_axis_box (void *ap, ca_size_t *starts,
|
|
1408
|
+
ca_size_t *counts, ca_size_t *strides)
|
|
1409
|
+
{
|
|
1410
|
+
CArray *ca = (CArray *) ap;
|
|
1411
|
+
ca_size_t native[CA_RANK_MAX];
|
|
1412
|
+
ca_size_t s = ca->bytes;
|
|
1413
|
+
int8_t ndim = ca->ndim, k;
|
|
1414
|
+
|
|
1415
|
+
for ( k = ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
|
|
1416
|
+
|
|
1417
|
+
for ( k = 0; k < ndim; k++ ) {
|
|
1418
|
+
ca_size_t q;
|
|
1419
|
+
if ( counts[k] <= 1 ) continue; /* moves nothing */
|
|
1420
|
+
if ( strides[k] <= 0 ) return 0; /* zero / negative: not an axis walk */
|
|
1421
|
+
if ( strides[k] % native[k] != 0 ) return 0;
|
|
1422
|
+
q = strides[k] / native[k];
|
|
1423
|
+
if ( q < 1 ) return 0;
|
|
1424
|
+
if ( starts[k] + (counts[k] - 1) * q >= ca->dim[k] ) return 0; /* runs off axis k */
|
|
1425
|
+
}
|
|
1426
|
+
return 1;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
static void
|
|
1430
|
+
ca_xfer_stride_dispatch (CArray *ca, ca_size_t *starts, ca_size_t *counts,
|
|
1431
|
+
ca_size_t *strides, void *data, int dir)
|
|
1432
|
+
{
|
|
1433
|
+
char *d = (char *) data;
|
|
1434
|
+
int8_t ndim = ca->ndim;
|
|
1435
|
+
ca_size_t native[CA_RANK_MAX];
|
|
1436
|
+
ca_size_t base = 0;
|
|
1437
|
+
ca_size_t doff = 0;
|
|
1438
|
+
ca_size_t s;
|
|
1439
|
+
int8_t k;
|
|
1440
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
1441
|
+
|
|
1442
|
+
s = ca->bytes;
|
|
1443
|
+
for ( k = ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
|
|
1444
|
+
for ( k = 0; k < ndim; k++ ) base += starts[k] * native[k];
|
|
1445
|
+
|
|
1446
|
+
if ( ca->ptr && d != (char *)ca->ptr + base ) {
|
|
1447
|
+
/* attached / entity / alias: strided memcpy.
|
|
1448
|
+
CAREFUL: the `d != (char *)ca->ptr + base` guard in the branch
|
|
1449
|
+
condition above is load-bearing. It blocks the lazy-view self-fill
|
|
1450
|
+
pattern where data == ca->ptr + base would degenerate into a
|
|
1451
|
+
self-memcpy and leave the buffer garbage. Lazy-view attach funcs
|
|
1452
|
+
bypass this dispatcher, but the guard catches any future caller that
|
|
1453
|
+
re-introduces the same category error. */
|
|
1454
|
+
ca_xfer_strided_walk(ca->ptr + base, ca->bytes, ndim, counts, strides,
|
|
1455
|
+
d, dir);
|
|
1456
|
+
return;
|
|
617
1457
|
}
|
|
618
1458
|
|
|
619
|
-
|
|
1459
|
+
if ( ca_func[ca->obj_type].xfer_stride ) {
|
|
1460
|
+
ca_func[ca->obj_type].xfer_stride(ca, starts, counts, strides, data, dir);
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
620
1463
|
|
|
621
|
-
|
|
1464
|
+
/* fallback: per-cell via byte offset -> flat addr -> index -> xfer_index. */
|
|
1465
|
+
for ( k = 0; k < ndim; k++ ) idx[k] = 0;
|
|
1466
|
+
while ( 1 ) {
|
|
1467
|
+
ca_size_t soff = base, vidx[CA_RANK_MAX];
|
|
1468
|
+
for ( k = 0; k < ndim; k++ ) soff += idx[k] * strides[k];
|
|
1469
|
+
ca_addr2index(ca, soff / ca->bytes, vidx);
|
|
1470
|
+
ca_xfer_index_dispatch(ca, vidx, d + doff, dir);
|
|
1471
|
+
doff += ca->bytes;
|
|
1472
|
+
k = ndim - 1;
|
|
1473
|
+
while ( k >= 0 ) { if ( ++idx[k] < counts[k] ) break; idx[k] = 0; k--; }
|
|
1474
|
+
if ( k < 0 ) break;
|
|
1475
|
+
}
|
|
622
1476
|
}
|
|
623
1477
|
|
|
624
|
-
|
|
1478
|
+
struct ca_xfer_stride_args {
|
|
1479
|
+
CArray *ca;
|
|
1480
|
+
ca_size_t *starts;
|
|
1481
|
+
ca_size_t *counts;
|
|
1482
|
+
ca_size_t *strides;
|
|
1483
|
+
char *data;
|
|
1484
|
+
};
|
|
1485
|
+
|
|
1486
|
+
static VALUE
|
|
1487
|
+
ca_xfer_stride_get_body (VALUE arg)
|
|
1488
|
+
{
|
|
1489
|
+
struct ca_xfer_stride_args *a = (struct ca_xfer_stride_args *) arg;
|
|
1490
|
+
ca_size_t n = 1, i;
|
|
1491
|
+
int8_t k;
|
|
1492
|
+
ca_xfer_stride_dispatch(a->ca, a->starts, a->counts, a->strides, a->data,
|
|
1493
|
+
CA_XFER_GET);
|
|
1494
|
+
/* cyclic check over the delivered cells (CA_OBJECT only). dst is contiguous
|
|
1495
|
+
row-major over counts (semantics b), so cell i is at data + i*bytes. */
|
|
1496
|
+
for ( k = 0; k < a->ca->ndim; k++ ) n *= a->counts[k];
|
|
1497
|
+
for ( i = 0; i < n; i++ ) {
|
|
1498
|
+
ca_test_cyclic_check(a->ca, a->data + i * a->ca->bytes);
|
|
1499
|
+
}
|
|
1500
|
+
return Qnil;
|
|
1501
|
+
}
|
|
625
1502
|
|
|
626
1503
|
void
|
|
627
|
-
|
|
1504
|
+
ca_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
1505
|
+
ca_size_t *strides, void *data, int dir)
|
|
628
1506
|
{
|
|
629
1507
|
CArray *ca = (CArray *) ap;
|
|
630
|
-
char *ptr = (char *) pval;
|
|
631
1508
|
|
|
632
|
-
if ( ca_is_readonly(ca) ) {
|
|
633
|
-
rb_raise(rb_eRuntimeError,
|
|
634
|
-
"can not store data to read-only array");
|
|
1509
|
+
if ( dir == CA_XFER_PUT && ca_is_readonly(ca) ) {
|
|
1510
|
+
rb_raise(rb_eRuntimeError, "can not store data to read-only array");
|
|
635
1511
|
}
|
|
636
1512
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
ca_func[ca->obj_type].store_index(ca, idx, ptr);
|
|
1513
|
+
if ( ca->data_type != CA_OBJECT ) {
|
|
1514
|
+
ca_xfer_stride_dispatch(ca, starts, counts, strides, data, dir);
|
|
1515
|
+
return;
|
|
641
1516
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
1517
|
+
|
|
1518
|
+
if ( dir == CA_XFER_GET ) {
|
|
1519
|
+
struct ca_xfer_stride_args args;
|
|
1520
|
+
int state = 0;
|
|
1521
|
+
args.ca = ca; args.starts = starts; args.counts = counts;
|
|
1522
|
+
args.strides = strides; args.data = (char *) data;
|
|
1523
|
+
ca_set_cyclic_check(ca);
|
|
1524
|
+
rb_protect(ca_xfer_stride_get_body, (VALUE) &args, &state);
|
|
1525
|
+
ca_clear_cyclic_check(ca);
|
|
1526
|
+
if ( state ) rb_jump_tag(state);
|
|
645
1527
|
}
|
|
646
1528
|
else {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
ca->obj_type);
|
|
1529
|
+
ca_set_cyclic_check(ca);
|
|
1530
|
+
ca_xfer_stride_dispatch(ca, starts, counts, strides, data, CA_XFER_PUT);
|
|
1531
|
+
ca_clear_cyclic_check(ca);
|
|
651
1532
|
}
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
/* whole-view transfer: direction-unified replacement of copy_data /
|
|
1536
|
+
sync_data. Pure dispatch to the view's xfer_all slot. Readonly /
|
|
1537
|
+
nosync policy lives in ca_sync_data (the PUT entry), not here -- this
|
|
1538
|
+
is the raw dispatcher.
|
|
652
1539
|
|
|
653
|
-
|
|
1540
|
+
CAREFUL: the dispatcher is a thin wrapper (no ca_attach here), and each
|
|
1541
|
+
view's xfer_all slot must not ca_attach(parent) either. That is what
|
|
1542
|
+
gives ca_xfer_all and all internal callers (ca_update / ca_copy_data /
|
|
1543
|
+
ca_sync_data / kernel_iterator SRC_ATTACH path) their cheap-attach
|
|
1544
|
+
semantics; re-adding an attach into a slot silently reintroduces a
|
|
1545
|
+
whole-parent materialise. External ext gems calling ca_xfer_all should
|
|
1546
|
+
likewise expect a thin dispatcher. */
|
|
654
1547
|
|
|
1548
|
+
typedef struct {
|
|
1549
|
+
CArray *ca;
|
|
1550
|
+
void *data;
|
|
1551
|
+
int dir;
|
|
1552
|
+
} ca_xfer_all_args_t;
|
|
1553
|
+
|
|
1554
|
+
static VALUE
|
|
1555
|
+
ca_xfer_all_body (VALUE arg)
|
|
1556
|
+
{
|
|
1557
|
+
ca_xfer_all_args_t *a = (ca_xfer_all_args_t *) arg;
|
|
1558
|
+
ca_func[a->ca->obj_type].xfer_all(a->ca, a->data, a->dir);
|
|
1559
|
+
return Qnil;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
/* `arg` carries the hold depth to unwind to, so nested ca_xfer_all calls
|
|
1563
|
+
each drop their own hold rather than the whole stack. */
|
|
1564
|
+
static VALUE
|
|
1565
|
+
ca_xfer_all_ensure (VALUE arg)
|
|
1566
|
+
{
|
|
1567
|
+
ca_gc_hold_pop_to(NUM2INT(arg));
|
|
1568
|
+
ca_lazy_arena_exit();
|
|
1569
|
+
return Qnil;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
void
|
|
1573
|
+
ca_xfer_all (void *ap, void *data, int dir)
|
|
1574
|
+
{
|
|
1575
|
+
CArray *ca = (CArray *) ap;
|
|
1576
|
+
ca_xfer_all_args_t args;
|
|
1577
|
+
int guard = -1;
|
|
1578
|
+
if ( ! ca_func[ca->obj_type].xfer_all ) {
|
|
1579
|
+
rb_raise(rb_eRuntimeError,
|
|
1580
|
+
"[BUG] xfer_all not defined for object type <%i>",
|
|
1581
|
+
ca->obj_type);
|
|
1582
|
+
}
|
|
1583
|
+
/* Universal arena lifetime hook. ca_xfer_all is the single universal
|
|
1584
|
+
entry for materialise (to_ca -> ca_copy -> ca_copy_data ->
|
|
1585
|
+
ca_xfer_all), so wrapping here covers every outermost view type,
|
|
1586
|
+
including an affine view wrapping a lazy view ((a.lazy+b).transpose).
|
|
1587
|
+
|
|
1588
|
+
CAREFUL: the arena _exit must run under rb_ensure. If an exception
|
|
1589
|
+
skips it, the arena depth stays stuck at +1 and the reset trigger
|
|
1590
|
+
(a depth==0 entry) never fires -- a silent failure. With rb_ensure
|
|
1591
|
+
the depth returns to 0 on exit and the reset fires correctly at the
|
|
1592
|
+
next entry. */
|
|
1593
|
+
ca_lazy_arena_enter();
|
|
1594
|
+
args.ca = ca; args.data = data; args.dir = dir;
|
|
1595
|
+
|
|
1596
|
+
/* A CA_OBJECT cell is a VALUE, and this buffer belongs to no Ruby
|
|
1597
|
+
object yet -- it is the destination `copy` will hand out, or a
|
|
1598
|
+
view's own freshly allocated one. The object lane calls rb_funcall
|
|
1599
|
+
per cell, so a collection partway through would free what has been
|
|
1600
|
+
written so far. ca_xfer_all is the whole-view entry, so the window
|
|
1601
|
+
is exactly ca->elements contiguous cells: the one place where the
|
|
1602
|
+
extent is known without trusting a caller's strides. Sub-windows
|
|
1603
|
+
written by nested transfers land inside it. */
|
|
1604
|
+
if ( ca->data_type == CA_OBJECT && dir == CA_XFER_GET ) {
|
|
1605
|
+
if ( data != (void *) ca->ptr ) {
|
|
1606
|
+
VALUE *p = (VALUE *) data;
|
|
1607
|
+
ca_size_t i;
|
|
1608
|
+
for ( i = 0; i < ca->elements; i++ ) *p++ = Qnil;
|
|
1609
|
+
}
|
|
1610
|
+
guard = ca_gc_hold_push(data, ca->elements);
|
|
1611
|
+
}
|
|
1612
|
+
rb_ensure(ca_xfer_all_body, (VALUE) &args,
|
|
1613
|
+
ca_xfer_all_ensure, INT2NUM(guard));
|
|
655
1614
|
}
|
|
656
1615
|
|
|
657
1616
|
/* ------------------------------------------------------------------- */
|
|
@@ -667,12 +1626,12 @@ ca_allocate (void *ap)
|
|
|
667
1626
|
return;
|
|
668
1627
|
}
|
|
669
1628
|
|
|
670
|
-
if (
|
|
1629
|
+
if ( ca_is_view(ca) ) { /* view array */
|
|
671
1630
|
|
|
672
|
-
|
|
673
|
-
if (
|
|
1631
|
+
CAVIEW(ca)->attach += 1; /* increments attach level */
|
|
1632
|
+
if ( CAVIEW(ca)->attach > CA_ATTACH_MAX ) {
|
|
674
1633
|
rb_raise(rb_eRuntimeError,
|
|
675
|
-
"too large attach count of
|
|
1634
|
+
"too large attach count of view array");
|
|
676
1635
|
}
|
|
677
1636
|
|
|
678
1637
|
if ( ! ca->ptr ) {
|
|
@@ -707,12 +1666,12 @@ ca_attach (void *ap)
|
|
|
707
1666
|
return;
|
|
708
1667
|
}
|
|
709
1668
|
|
|
710
|
-
if (
|
|
1669
|
+
if ( ca_is_view(ca) ) { /* view array */
|
|
711
1670
|
|
|
712
|
-
|
|
713
|
-
if (
|
|
1671
|
+
CAVIEW(ca)->attach += 1; /* increments attach level */
|
|
1672
|
+
if ( CAVIEW(ca)->attach > CA_ATTACH_MAX ) {
|
|
714
1673
|
rb_raise(rb_eRuntimeError,
|
|
715
|
-
"too large attach count of
|
|
1674
|
+
"too large attach count of view array");
|
|
716
1675
|
}
|
|
717
1676
|
|
|
718
1677
|
if ( ! ca->ptr ) {
|
|
@@ -738,13 +1697,13 @@ ca_update (void *ap)
|
|
|
738
1697
|
return;
|
|
739
1698
|
}
|
|
740
1699
|
|
|
741
|
-
if (
|
|
1700
|
+
if ( ca_is_view(ca) ) { /* view array */
|
|
742
1701
|
|
|
743
1702
|
if ( ca->ptr ) {
|
|
744
|
-
|
|
1703
|
+
ca_xfer_all(ca, ca->ptr, CA_XFER_GET); /* re-gather into own ptr (step 4) */
|
|
745
1704
|
}
|
|
746
1705
|
else {
|
|
747
|
-
rb_raise(rb_eRuntimeError,
|
|
1706
|
+
rb_raise(rb_eRuntimeError,
|
|
748
1707
|
"[BUG] ca_update() called for not-attached virtal array");
|
|
749
1708
|
}
|
|
750
1709
|
|
|
@@ -778,8 +1737,8 @@ ca_sync (void *ap)
|
|
|
778
1737
|
ca_update_mask(ca);
|
|
779
1738
|
ca_sync(ca->mask);
|
|
780
1739
|
|
|
781
|
-
if (
|
|
782
|
-
if ( !
|
|
1740
|
+
if ( ca_is_view(ca) ) { /* view array */
|
|
1741
|
+
if ( ! CAVIEW(ca)->nosync ) { /* FIXME : */
|
|
783
1742
|
ca_func[ca->obj_type].sync(ap);
|
|
784
1743
|
}
|
|
785
1744
|
}
|
|
@@ -805,11 +1764,11 @@ ca_detach (void *ap)
|
|
|
805
1764
|
"[BUG] tried to detach a detached array");
|
|
806
1765
|
}
|
|
807
1766
|
|
|
808
|
-
if (
|
|
809
|
-
if (
|
|
1767
|
+
if ( ca_is_view(ca) ) { /* view array */
|
|
1768
|
+
if ( CAVIEW(ca)->attach == 1 ) {
|
|
810
1769
|
ca_func[ca->obj_type].detach(ap);
|
|
811
1770
|
}
|
|
812
|
-
|
|
1771
|
+
CAVIEW(ca)->attach -= 1;
|
|
813
1772
|
}
|
|
814
1773
|
else { /* entity array */
|
|
815
1774
|
ca_func[ca->obj_type].detach(ap);
|
|
@@ -882,8 +1841,7 @@ ca_detach_n (int n, ...)
|
|
|
882
1841
|
void
|
|
883
1842
|
ca_copy_data (void *ap, char *ptr)
|
|
884
1843
|
{
|
|
885
|
-
|
|
886
|
-
ca_func[ca->obj_type].copy_data(ap, ptr); /* delegate */
|
|
1844
|
+
ca_xfer_all(ap, ptr, CA_XFER_GET); /* whole-view gather (step 4) */
|
|
887
1845
|
}
|
|
888
1846
|
|
|
889
1847
|
/* synchronize the data pointed by given pointer to parent's data */
|
|
@@ -898,16 +1856,12 @@ ca_sync_data (void *ap, char *ptr)
|
|
|
898
1856
|
"can not sync data to read-only array");
|
|
899
1857
|
}
|
|
900
1858
|
|
|
901
|
-
if (
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
}
|
|
905
|
-
else {
|
|
906
|
-
ca_func[ca->obj_type].sync_data(ap, ptr);
|
|
907
|
-
}
|
|
1859
|
+
if ( ca_is_view(ca) && CAVIEW(ca)->nosync ) {
|
|
1860
|
+
/* ca is to be attached: treat ca->ptr as an owned entity buffer */
|
|
1861
|
+
ca_array_func_xfer_all(ap, ptr, CA_XFER_PUT);
|
|
908
1862
|
}
|
|
909
|
-
else {
|
|
910
|
-
|
|
1863
|
+
else {
|
|
1864
|
+
ca_xfer_all(ap, ptr, CA_XFER_PUT); /* whole-view scatter (step 4) */
|
|
911
1865
|
}
|
|
912
1866
|
}
|
|
913
1867
|
|
|
@@ -924,7 +1878,7 @@ ca_fill_data (void *ap, void *aptr)
|
|
|
924
1878
|
"can not fill data to read-only array");
|
|
925
1879
|
}
|
|
926
1880
|
|
|
927
|
-
if (
|
|
1881
|
+
if ( ca_is_view(ca) ) { /* view array */
|
|
928
1882
|
if ( ca_is_attached(ca) ) { /* ca is to be attached */
|
|
929
1883
|
ca_func[CA_OBJ_ARRAY].fill_data(ap, ptr);
|
|
930
1884
|
}
|
|
@@ -937,6 +1891,174 @@ ca_fill_data (void *ap, void *aptr)
|
|
|
937
1891
|
}
|
|
938
1892
|
}
|
|
939
1893
|
|
|
1894
|
+
/* Write one value into part of a view.
|
|
1895
|
+
|
|
1896
|
+
The default walks the region and hands each cell to xfer_index, which
|
|
1897
|
+
composes one hop and delegates to the parent. It is per-cell, so it is the
|
|
1898
|
+
floor rather than the path: a view that can pass the region on fills in the
|
|
1899
|
+
slot and the walk never happens. What the default guarantees is that a view
|
|
1900
|
+
with no slot still touches only the region. */
|
|
1901
|
+
|
|
1902
|
+
void
|
|
1903
|
+
ca_fill_stride_default (void *ap, ca_size_t base, int8_t ndim,
|
|
1904
|
+
ca_size_t *counts, ca_size_t *steps, void *ptr)
|
|
1905
|
+
{
|
|
1906
|
+
CArray *ca = (CArray *) ap;
|
|
1907
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
1908
|
+
int8_t k;
|
|
1909
|
+
|
|
1910
|
+
for ( k = 0; k < ndim; k++ ) idx[k] = 0;
|
|
1911
|
+
while ( 1 ) {
|
|
1912
|
+
ca_size_t addr = base, vidx[CA_RANK_MAX];
|
|
1913
|
+
for ( k = 0; k < ndim; k++ ) addr += idx[k] * steps[k];
|
|
1914
|
+
ca_addr2index(ca, addr, vidx);
|
|
1915
|
+
ca_xfer_index_dispatch(ca, vidx, ptr, CA_XFER_PUT);
|
|
1916
|
+
k = ndim - 1;
|
|
1917
|
+
while ( k >= 0 ) { if ( ++idx[k] < counts[k] ) break; idx[k] = 0; k--; }
|
|
1918
|
+
if ( k < 0 ) break;
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
void
|
|
1923
|
+
ca_fill_stride (void *ap, ca_size_t base, int8_t ndim,
|
|
1924
|
+
ca_size_t *counts, ca_size_t *steps, void *ptr)
|
|
1925
|
+
{
|
|
1926
|
+
CArray *ca = (CArray *) ap;
|
|
1927
|
+
int8_t k;
|
|
1928
|
+
|
|
1929
|
+
if ( ca_is_readonly(ca) ) {
|
|
1930
|
+
rb_raise(rb_eRuntimeError, "can not fill data to read-only array");
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
for ( k = 0; k < ndim; k++ ) {
|
|
1934
|
+
if ( counts[k] <= 0 ) return;
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
if ( ca_func[ca->obj_type].fill_stride ) {
|
|
1938
|
+
ca_func[ca->obj_type].fill_stride(ap, base, ndim, counts, steps, ptr);
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
ca_fill_stride_default(ap, base, ndim, counts, steps, ptr);
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
/* True if the region is exactly `ca`'s own extent in row-major order. A view
|
|
1946
|
+
that composes its axes into its parent's space can only do so for the whole
|
|
1947
|
+
of itself: a sub-box arrives as addresses, and recovering which axis each
|
|
1948
|
+
step belongs to is not something addresses can answer once the view has
|
|
1949
|
+
reordered or dropped axes. In practice that is the only region a view is
|
|
1950
|
+
asked for -- ca_fill_stride_whole is the caller -- so the check is a
|
|
1951
|
+
precondition rather than a fast path. */
|
|
1952
|
+
|
|
1953
|
+
int
|
|
1954
|
+
ca_fill_stride_is_whole (void *ap, ca_size_t base, int8_t ndim,
|
|
1955
|
+
ca_size_t *counts, ca_size_t *steps)
|
|
1956
|
+
{
|
|
1957
|
+
CArray *ca = (CArray *) ap;
|
|
1958
|
+
ca_size_t s = 1;
|
|
1959
|
+
int8_t k;
|
|
1960
|
+
|
|
1961
|
+
if ( base != 0 || ndim != ca->ndim ) return 0;
|
|
1962
|
+
for ( k = ndim - 1; k >= 0; k-- ) {
|
|
1963
|
+
if ( counts[k] != ca->dim[k] || steps[k] != s ) return 0;
|
|
1964
|
+
s *= ca->dim[k];
|
|
1965
|
+
}
|
|
1966
|
+
return 1;
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
/* "All of me" as a region: the whole extent in row-major order. */
|
|
1970
|
+
|
|
1971
|
+
void
|
|
1972
|
+
ca_fill_stride_whole (void *ap, void *ptr)
|
|
1973
|
+
{
|
|
1974
|
+
CArray *ca = (CArray *) ap;
|
|
1975
|
+
ca_size_t counts[CA_RANK_MAX], steps[CA_RANK_MAX];
|
|
1976
|
+
ca_size_t s = 1;
|
|
1977
|
+
int8_t k;
|
|
1978
|
+
|
|
1979
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) {
|
|
1980
|
+
counts[k] = ca->dim[k];
|
|
1981
|
+
steps[k] = s;
|
|
1982
|
+
s *= ca->dim[k];
|
|
1983
|
+
}
|
|
1984
|
+
ca_fill_stride(ap, 0, ca->ndim, counts, steps, ptr);
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
void
|
|
1988
|
+
ca_fill_addrs_default (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
|
|
1989
|
+
{
|
|
1990
|
+
CArray *ca = (CArray *) ap;
|
|
1991
|
+
ca_size_t i;
|
|
1992
|
+
|
|
1993
|
+
for ( i = 0; i < n; i++ ) {
|
|
1994
|
+
ca_size_t vidx[CA_RANK_MAX];
|
|
1995
|
+
ca_addr2index(ca, addrs[i], vidx);
|
|
1996
|
+
ca_xfer_index_dispatch(ca, vidx, ptr, CA_XFER_PUT);
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
/* Walk a region and hand its addresses on in windows.
|
|
2001
|
+
|
|
2002
|
+
For a view whose fill is a read-modify-write of the parent -- the sub-byte
|
|
2003
|
+
ones, where a cell carries bits the fill must leave alone -- there is no
|
|
2004
|
+
region to pass down: the parent has to be read before it can be written.
|
|
2005
|
+
What there is to save is being asked for it one cell at a time, each cell
|
|
2006
|
+
descending the chain on its own. The batched address slot already does the
|
|
2007
|
+
read and the write in one call each; this only feeds it.
|
|
2008
|
+
|
|
2009
|
+
The window is fixed so the scratch does not follow the region's size. */
|
|
2010
|
+
|
|
2011
|
+
#define CA_FILL_ADDR_WINDOW 1024
|
|
2012
|
+
|
|
2013
|
+
void
|
|
2014
|
+
ca_fill_stride_via_addrs (void *ap, ca_size_t base, int8_t ndim,
|
|
2015
|
+
ca_size_t *counts, ca_size_t *steps, void *ptr)
|
|
2016
|
+
{
|
|
2017
|
+
CArray *ca = (CArray *) ap;
|
|
2018
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
2019
|
+
ca_size_t window[CA_FILL_ADDR_WINDOW];
|
|
2020
|
+
ca_size_t n = 0;
|
|
2021
|
+
int8_t k;
|
|
2022
|
+
|
|
2023
|
+
for ( k = 0; k < ndim; k++ ) {
|
|
2024
|
+
if ( counts[k] <= 0 ) return;
|
|
2025
|
+
idx[k] = 0;
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
while ( 1 ) {
|
|
2029
|
+
ca_size_t addr = base;
|
|
2030
|
+
for ( k = 0; k < ndim; k++ ) addr += idx[k] * steps[k];
|
|
2031
|
+
window[n++] = addr;
|
|
2032
|
+
if ( n == CA_FILL_ADDR_WINDOW ) {
|
|
2033
|
+
ca_fill_addrs(ca, n, window, ptr);
|
|
2034
|
+
n = 0;
|
|
2035
|
+
}
|
|
2036
|
+
k = ndim - 1;
|
|
2037
|
+
while ( k >= 0 ) { if ( ++idx[k] < counts[k] ) break; idx[k] = 0; k--; }
|
|
2038
|
+
if ( k < 0 ) break;
|
|
2039
|
+
}
|
|
2040
|
+
if ( n ) {
|
|
2041
|
+
ca_fill_addrs(ca, n, window, ptr);
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
void
|
|
2046
|
+
ca_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
|
|
2047
|
+
{
|
|
2048
|
+
CArray *ca = (CArray *) ap;
|
|
2049
|
+
|
|
2050
|
+
if ( ca_is_readonly(ca) ) {
|
|
2051
|
+
rb_raise(rb_eRuntimeError, "can not fill data to read-only array");
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
if ( ca_func[ca->obj_type].fill_addrs ) {
|
|
2055
|
+
ca_func[ca->obj_type].fill_addrs(ap, n, addrs, ptr);
|
|
2056
|
+
return;
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
ca_fill_addrs_default(ap, n, addrs, ptr);
|
|
2060
|
+
}
|
|
2061
|
+
|
|
940
2062
|
/* ------------------------------------------------------------------- */
|
|
941
2063
|
|
|
942
2064
|
/* clone CArray struct */
|
|
@@ -975,9 +2097,9 @@ rb_ca_attach_i (VALUE self)
|
|
|
975
2097
|
if ( rb_obj_is_carray(self) ) {
|
|
976
2098
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
977
2099
|
ca_attach(ca);
|
|
978
|
-
if (
|
|
979
|
-
|
|
980
|
-
if (
|
|
2100
|
+
if ( ca_is_view(ca) ) {
|
|
2101
|
+
CAVIEW(ca)->nosync += 1;
|
|
2102
|
+
if ( CAVIEW(ca)->nosync > 64 ) {
|
|
981
2103
|
rb_raise(rb_eRuntimeError, "nosync count exceeds 64");
|
|
982
2104
|
}
|
|
983
2105
|
}
|
|
@@ -990,10 +2112,10 @@ rb_ca_sync_i (VALUE self)
|
|
|
990
2112
|
CArray *ca;
|
|
991
2113
|
if ( rb_obj_is_carray(self) ) {
|
|
992
2114
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
993
|
-
if (
|
|
994
|
-
|
|
2115
|
+
if ( ca_is_view(ca) ) {
|
|
2116
|
+
CAVIEW(ca)->nosync -= 1;
|
|
995
2117
|
ca_sync(ca);
|
|
996
|
-
|
|
2118
|
+
CAVIEW(ca)->nosync += 1;
|
|
997
2119
|
}
|
|
998
2120
|
else {
|
|
999
2121
|
ca_sync(ca);
|
|
@@ -1007,8 +2129,8 @@ rb_ca_detach_i (VALUE self)
|
|
|
1007
2129
|
CArray *ca;
|
|
1008
2130
|
if ( rb_obj_is_carray(self) ) {
|
|
1009
2131
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
1010
|
-
if (
|
|
1011
|
-
|
|
2132
|
+
if ( ca_is_view(ca) ) { /* view array */
|
|
2133
|
+
CAVIEW(ca)->nosync -= 1;
|
|
1012
2134
|
ca_detach(ca);
|
|
1013
2135
|
}
|
|
1014
2136
|
else { /* entity array */
|
|
@@ -1092,7 +2214,7 @@ rb_ca_s_attach_bang (int argc, VALUE *argv, VALUE self)
|
|
|
1092
2214
|
|
|
1093
2215
|
for (i=0; i<RARRAY_LEN(list); i++) {
|
|
1094
2216
|
obj = rb_ary_entry(list, i);
|
|
1095
|
-
|
|
2217
|
+
rb_check_frozen(obj);
|
|
1096
2218
|
rb_ca_attach_i(obj);
|
|
1097
2219
|
}
|
|
1098
2220
|
|
|
@@ -1142,7 +2264,7 @@ It is ensured the syncing the memory block at the end of the block evaluation.
|
|
|
1142
2264
|
static VALUE
|
|
1143
2265
|
rb_ca_attach_bang (VALUE self)
|
|
1144
2266
|
{
|
|
1145
|
-
|
|
2267
|
+
rb_check_frozen(self);
|
|
1146
2268
|
rb_ca_attach_i(self);
|
|
1147
2269
|
return rb_ensure(rb_yield, self, rb_ca_ensure_sync_detach, self);
|
|
1148
2270
|
}
|
|
@@ -1168,7 +2290,7 @@ rb_ca__attach__ (VALUE self)
|
|
|
1168
2290
|
static VALUE
|
|
1169
2291
|
rb_ca__sync__ (VALUE self)
|
|
1170
2292
|
{
|
|
1171
|
-
|
|
2293
|
+
rb_check_frozen(self);
|
|
1172
2294
|
rb_ca_sync_i(self);
|
|
1173
2295
|
return self;
|
|
1174
2296
|
}
|
|
@@ -1232,23 +2354,39 @@ rb_ca_members (VALUE self)
|
|
|
1232
2354
|
}
|
|
1233
2355
|
}
|
|
1234
2356
|
|
|
2357
|
+
/* Projects a struct member of `self` to its CAField view. data_class
|
|
2358
|
+
lives only on a Face, so this accepts both a Face and a plain FIXLEN
|
|
2359
|
+
entity. For a Face, the field-view receiver is swapped to the parent
|
|
2360
|
+
(= Face strip): the @member cache lives on self (the Face) while the
|
|
2361
|
+
actual field view is the CAField on the parent. A plain FIXLEN entity
|
|
2362
|
+
keeps self == receiver. */
|
|
1235
2363
|
VALUE
|
|
1236
|
-
|
|
2364
|
+
rb_ca_face_field (VALUE self, VALUE sym)
|
|
1237
2365
|
{
|
|
1238
2366
|
volatile VALUE data_class = rb_ca_data_class(self);
|
|
1239
2367
|
volatile VALUE member;
|
|
1240
2368
|
volatile VALUE obj;
|
|
2369
|
+
volatile VALUE receiver;
|
|
2370
|
+
CArray *ca;
|
|
1241
2371
|
|
|
1242
2372
|
if ( NIL_P(data_class) ) {
|
|
1243
2373
|
rb_raise(rb_eRuntimeError, "carray doesn't have data class");
|
|
1244
2374
|
}
|
|
1245
2375
|
|
|
2376
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
2377
|
+
receiver = ca_is_face(ca) ? rb_ca_parent(self) : self;
|
|
2378
|
+
|
|
1246
2379
|
member = rb_ivar_get(self, rb_intern("member"));
|
|
1247
2380
|
|
|
1248
2381
|
if ( NIL_P(member) ) {
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
2382
|
+
/* Derived CARecord views (= arr[range] / arr.transpose etc.) inherit
|
|
2383
|
+
data_class via inherit_data_class but not @member, since only
|
|
2384
|
+
ca_record_build initialises it. Lazy-init here so chain field
|
|
2385
|
+
projection (arr[range]["lat"]) works without a [BUG] raise. Cache
|
|
2386
|
+
is local to the derived view; field views are still on its parent
|
|
2387
|
+
(which itself is on the entity). */
|
|
2388
|
+
member = rb_hash_new();
|
|
2389
|
+
rb_ivar_set(self, rb_intern("member"), member);
|
|
1252
2390
|
}
|
|
1253
2391
|
|
|
1254
2392
|
if ( rb_obj_is_kind_of(sym, rb_cInteger) ) {
|
|
@@ -1278,7 +2416,45 @@ rb_ca_field_as_member (VALUE self, VALUE sym)
|
|
|
1278
2416
|
"can't find data_member named <%s>", StringValuePtr(sym));
|
|
1279
2417
|
}
|
|
1280
2418
|
Check_Type(info, T_ARRAY);
|
|
1281
|
-
|
|
2419
|
+
/* Bit-typed members route through a CAField power-of-2 byte
|
|
2420
|
+
projection + CABitfield, mirroring the
|
|
2421
|
+
per-record dispatch in CAStruct#[]. MEMBER_TABLE entry shape
|
|
2422
|
+
for bits is `[byte_offset, :bitfield, {bits:, bit_offset:}]`
|
|
2423
|
+
where bit_offset is the struct-relative *bit* offset. Plain
|
|
2424
|
+
byte-typed members fall through to the original `.field(...)`
|
|
2425
|
+
path. */
|
|
2426
|
+
{
|
|
2427
|
+
volatile VALUE type_val = rb_ary_entry(info, 1);
|
|
2428
|
+
if ( SYMBOL_P(type_val) &&
|
|
2429
|
+
SYM2ID(type_val) == rb_intern("bitfield") ) {
|
|
2430
|
+
volatile VALUE opts = rb_ary_entry(info, 2);
|
|
2431
|
+
volatile VALUE word_view, range, vtype_sym;
|
|
2432
|
+
ca_size_t bit_offset = NUM2SIZE(rb_hash_aref(opts,
|
|
2433
|
+
ID2SYM(rb_intern("bit_offset"))));
|
|
2434
|
+
ca_size_t bits = NUM2SIZE(rb_hash_aref(opts,
|
|
2435
|
+
ID2SYM(rb_intern("bits"))));
|
|
2436
|
+
ca_size_t start_byte = bit_offset / 8;
|
|
2437
|
+
int bit_in_word = (int)(bit_offset % 8);
|
|
2438
|
+
ca_size_t end_byte_excl = (bit_offset + bits + 7) / 8;
|
|
2439
|
+
ca_size_t span = end_byte_excl - start_byte;
|
|
2440
|
+
int view_bytes;
|
|
2441
|
+
const char *vtype_name;
|
|
2442
|
+
if (span <= 1) { view_bytes = 1; vtype_name = "uint8"; }
|
|
2443
|
+
else if (span <= 2) { view_bytes = 2; vtype_name = "uint16"; }
|
|
2444
|
+
else if (span <= 4) { view_bytes = 4; vtype_name = "uint32"; }
|
|
2445
|
+
else { view_bytes = 8; vtype_name = "uint64"; }
|
|
2446
|
+
(void) view_bytes;
|
|
2447
|
+
vtype_sym = ID2SYM(rb_intern(vtype_name));
|
|
2448
|
+
word_view = rb_funcall(receiver, rb_intern("field"), 2,
|
|
2449
|
+
SIZE2NUM(start_byte), vtype_sym);
|
|
2450
|
+
range = rb_range_new(LONG2NUM(bit_in_word),
|
|
2451
|
+
SIZE2NUM(bit_in_word + bits - 1), 0);
|
|
2452
|
+
obj = rb_funcall(word_view, rb_intern("bitfield"), 1, range);
|
|
2453
|
+
rb_hash_aset(member, sym, obj);
|
|
2454
|
+
return obj;
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
obj = rb_apply(receiver, rb_intern("field"), info);
|
|
1282
2458
|
rb_hash_aset(member, sym, obj);
|
|
1283
2459
|
return obj;
|
|
1284
2460
|
}
|
|
@@ -1302,7 +2478,7 @@ rb_ca_fields (VALUE self)
|
|
|
1302
2478
|
list = rb_ary_new2(RARRAY_LEN(member_names));
|
|
1303
2479
|
for (i=0; i<RARRAY_LEN(member_names); i++) {
|
|
1304
2480
|
VALUE name = rb_ary_entry(member_names, i);
|
|
1305
|
-
rb_ary_store(list, i,
|
|
2481
|
+
rb_ary_store(list, i, rb_ca_face_field(self, name));
|
|
1306
2482
|
}
|
|
1307
2483
|
return list;
|
|
1308
2484
|
}
|
|
@@ -1325,15 +2501,14 @@ rb_ca_fields_at (int argc, VALUE *argv, VALUE self)
|
|
|
1325
2501
|
list = rb_ary_new2(RARRAY_LEN(member_names));
|
|
1326
2502
|
for (i=0; i<RARRAY_LEN(member_names); i++) {
|
|
1327
2503
|
VALUE name = rb_ary_entry(member_names, i);
|
|
1328
|
-
rb_ary_store(list, i,
|
|
2504
|
+
rb_ary_store(list, i, rb_ca_face_field(self, name));
|
|
1329
2505
|
}
|
|
1330
2506
|
return list;
|
|
1331
2507
|
}
|
|
1332
2508
|
|
|
1333
|
-
/* ------------------------------------------------------------------- */
|
|
1334
2509
|
|
|
1335
2510
|
void
|
|
1336
|
-
Init_carray_core ()
|
|
2511
|
+
Init_carray_core (void)
|
|
1337
2512
|
{
|
|
1338
2513
|
id_decode = rb_intern("decode");
|
|
1339
2514
|
id_encode = rb_intern("encode");
|
|
@@ -1354,5 +2529,20 @@ Init_carray_core ()
|
|
|
1354
2529
|
|
|
1355
2530
|
rb_define_method(rb_cCArray, "fields", rb_ca_fields, 0);
|
|
1356
2531
|
rb_define_method(rb_cCArray, "fields_at", rb_ca_fields_at, -1);
|
|
2532
|
+
|
|
1357
2533
|
}
|
|
1358
2534
|
|
|
2535
|
+
|
|
2536
|
+
/* ------------------------------------------------------------------- */
|
|
2537
|
+
|
|
2538
|
+
/* The out-of-build form of the ca_is_entity macro (see carray.h). Defined
|
|
2539
|
+
last so the macro stays in force for the rest of this file. */
|
|
2540
|
+
|
|
2541
|
+
#undef ca_is_entity
|
|
2542
|
+
|
|
2543
|
+
int
|
|
2544
|
+
ca_is_entity (const void *ap)
|
|
2545
|
+
{
|
|
2546
|
+
const CArray *ca = (const CArray *) ap;
|
|
2547
|
+
return ( ca_func[ca->obj_type].entity_type == CA_REAL_ARRAY );
|
|
2548
|
+
}
|