carray 3.0.0 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +2 -1
- data/CHANGELOG.md +845 -3
- data/{NEWS.md → CHANGELOG.v1.md} +1 -1
- data/README.md +33 -33
- data/carray.gemspec +12 -8
- data/ext/ca_axis_dispatch.c +33 -4
- data/ext/ca_axis_group.c +256 -114
- data/ext/ca_binop_dispatch.c +5 -6
- data/ext/ca_binop_dispatch.h +0 -7
- data/ext/ca_categorical_iterator.c +121 -67
- data/ext/ca_for_each_element.h +6 -8
- data/ext/ca_group_iter.c +1 -1
- data/ext/ca_kernel_iterator.c +386 -74
- data/ext/ca_kernel_iterator.h +145 -45
- data/ext/ca_obj_array.c +62 -20
- data/ext/ca_obj_bincmp.c +57 -27
- data/ext/ca_obj_binop.c +86 -30
- data/ext/ca_obj_block.c +4 -4
- data/ext/ca_obj_const_string.c +88 -27
- data/ext/ca_obj_face.c +104 -0
- data/ext/ca_obj_face.h +48 -1
- data/ext/ca_obj_fake.c +11 -0
- data/ext/ca_obj_fixlen_string.c +21 -6
- data/ext/ca_obj_grid.c +11 -3
- data/ext/ca_obj_meld.c +125 -22
- data/ext/ca_obj_moncmp.c +33 -14
- data/ext/ca_obj_monop.c +19 -7
- data/ext/ca_obj_object.c +107 -4
- data/ext/ca_obj_record.c +3 -1
- data/ext/ca_obj_refer.c +30 -6
- data/ext/ca_obj_roll.c +13 -5
- data/ext/ca_obj_select.c +49 -34
- data/ext/ca_obj_select_axis.c +16 -6
- data/ext/ca_obj_shift.c +3 -3
- data/ext/ca_obj_stack.c +3 -8
- data/ext/ca_obj_stride.c +178 -24
- data/ext/ca_obj_string.c +11 -5
- data/ext/ca_obj_tile.c +13 -5
- data/ext/ca_obj_time.c +3 -1
- data/ext/ca_obj_timedelta.c +3 -1
- data/ext/ca_obj_transpose.c +1 -1
- data/ext/ca_obj_triop.c +62 -23
- data/ext/ca_obj_window.c +94 -19
- data/ext/ca_op_cmplx64.h +123 -0
- data/ext/ca_op_ipower.c +1 -5
- data/ext/ca_rng_normal.h +42 -0
- data/ext/ca_rng_xoshiro256pp.h +105 -0
- data/ext/ca_sort_kernels.h +5 -5
- data/ext/ca_sweep_engine.c +362 -155
- data/ext/ca_sweep_engine.h +39 -9
- data/ext/ca_transform_common.c +7 -0
- data/ext/carray.h +148 -132
- data/ext/carray_access.c +83 -49
- data/ext/carray_address_basis.c +590 -0
- data/ext/carray_bincount.c +8 -8
- data/ext/carray_broadcast.c +102 -9
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +2010 -335
- data/ext/carray_call_cfunc.h +153 -0
- data/ext/carray_cast.c +184 -72
- data/ext/carray_conversion.c +22 -22
- data/ext/carray_copy.c +67 -32
- data/ext/carray_core.c +140 -13
- data/ext/carray_count.c +10 -11
- data/ext/carray_element.c +7 -7
- data/ext/carray_factorize.c +72 -51
- data/ext/carray_hold.c +1 -1
- data/ext/carray_index_classifier.c +7 -21
- data/ext/carray_internal.h +36 -3
- data/ext/carray_kernels_bincmp.c +1 -0
- data/ext/carray_kernels_binop.c +320 -298
- data/ext/carray_kernels_init.c +1095 -0
- data/ext/carray_kernels_map.c +1 -0
- data/ext/carray_kernels_moncmp.c +1 -0
- data/ext/carray_kernels_monop.c +412 -411
- data/ext/carray_kernels_reduce_aggregate.c +256 -87
- data/ext/carray_kernels_reduce_boolean.c +1 -0
- data/ext/carray_kernels_reduce_cumulative.c +324 -54
- data/ext/carray_kernels_reduce_extreme.c +605 -58
- data/ext/carray_kernels_reduce_variance.c +1 -0
- data/ext/carray_kernels_scan.c +5 -4
- data/ext/carray_kernels_search.c +187 -106
- data/ext/carray_kernels_sort.c +1 -0
- data/ext/carray_kernels_triop.c +1 -0
- data/ext/carray_lazy.c +175 -5
- data/ext/carray_loop.c +7 -1
- data/ext/carray_mask.c +29 -13
- data/ext/carray_median_percentile.c +56 -1
- data/ext/carray_memory_view.c +46 -21
- data/ext/carray_operator.c +50 -53
- data/ext/carray_order.c +1 -1
- data/ext/carray_partition.c +4 -4
- data/ext/carray_random.c +391 -47
- data/ext/carray_scatter.c +1 -1
- data/ext/carray_slab.c +13 -0
- data/ext/carray_sort.c +22 -24
- data/ext/carray_sort_kernel.c +10 -10
- data/ext/carray_test.c +1 -1
- data/ext/extconf.rb +18 -0
- data/ext/mk_call_cfunc.rb +323 -145
- data/ext/mkkernel.rb +651 -129
- data/ext/ruby_carray.c +28 -17
- data/ext/version.h +4 -4
- data/lib/carray/autoload_carray.rb +24 -3
- data/lib/carray/autoload_method_extension.rb +14 -1
- data/lib/carray/axis_group.rb +84 -7
- data/lib/carray/basics.rb +63 -59
- data/lib/carray/bincount_nd.rb +12 -12
- data/lib/carray/block_iterator.rb +111 -25
- data/lib/carray/categorical.rb +154 -37
- data/lib/carray/categorical_iterator.rb +300 -151
- data/lib/carray/conditional.rb +14 -14
- data/lib/carray/const_string.rb +131 -27
- data/lib/carray/construct.rb +61 -0
- data/lib/carray/core_extensions.rb +62 -5
- data/lib/carray/data_type_extension.rb +21 -1
- data/lib/carray/data_type_limits.rb +91 -0
- data/lib/carray/fixlen_string.rb +1 -1
- data/lib/carray/frame/concat.rb +3 -3
- data/lib/carray/frame/convert.rb +1 -1
- data/lib/carray/frame/csv_parser.rb +11 -4
- data/lib/carray/frame/frame.rb +85 -21
- data/lib/carray/frame/group.rb +36 -3
- data/lib/carray/frame/io.rb +224 -15
- data/lib/carray/frame/records.rb +18 -4
- data/lib/carray/frame/sort.rb +1 -1
- data/lib/carray/frame/verbs.rb +32 -12
- data/lib/carray/fuse_source.rb +123 -0
- data/lib/carray/fusion.rb +218 -0
- data/lib/carray/histogram.rb +16 -16
- data/lib/carray/inspect.rb +43 -16
- data/lib/carray/iterator.rb +147 -3
- data/lib/carray/lazy.rb +88 -73
- data/lib/carray/mask_gap_fill.rb +3 -1
- data/lib/carray/meld_reduce.rb +2 -2
- data/lib/carray/methods/align_addr.rb +1 -1
- data/lib/carray/methods/composition.rb +1 -1
- data/lib/carray/methods/discovery_along.rb +74 -0
- data/lib/carray/methods/factorize.rb +50 -0
- data/lib/carray/methods/is_in.rb +25 -14
- data/lib/carray/methods/locate_addr.rb +81 -3
- data/lib/carray/methods/mask_duplicates.rb +36 -2
- data/lib/carray/methods/meshgrid.rb +4 -5
- data/lib/carray/methods/mode.rb +2 -2
- data/lib/carray/methods/nunique.rb +23 -2
- data/lib/carray/methods/repeat.rb +110 -0
- data/lib/carray/methods/snap.rb +7 -2
- data/lib/carray/methods/unique.rb +44 -4
- data/lib/carray/methods/value_counts.rb +2 -2
- data/lib/carray/rng.rb +86 -0
- data/lib/carray/runtime.rb +0 -19
- data/lib/carray/slab_iterator.rb +77 -19
- data/lib/carray/string_operation_extension.rb +10 -6
- data/lib/carray/time.rb +906 -491
- data/lib/carray/window_iterator.rb +421 -27
- data/lib/carray.rb +7 -5
- data/yard-stubs/ca_obj_array.rb +385 -0
- data/yard-stubs/ca_obj_bitarray.rb +38 -0
- data/yard-stubs/ca_obj_bitfield.rb +43 -0
- data/yard-stubs/ca_obj_block.rb +68 -0
- data/yard-stubs/ca_obj_byte_swap.rb +56 -0
- data/yard-stubs/ca_obj_fake.rb +31 -0
- data/yard-stubs/ca_obj_farray.rb +32 -0
- data/yard-stubs/ca_obj_field.rb +45 -0
- data/yard-stubs/ca_obj_grid.rb +35 -0
- data/yard-stubs/ca_obj_refer.rb +72 -0
- data/yard-stubs/ca_obj_roll.rb +45 -0
- data/yard-stubs/ca_obj_shift.rb +43 -0
- data/yard-stubs/ca_obj_stride.rb +181 -0
- data/yard-stubs/ca_obj_tile.rb +29 -0
- data/yard-stubs/ca_obj_transpose.rb +40 -0
- data/yard-stubs/ca_obj_window.rb +57 -0
- data/yard-stubs/carray_access.rb +131 -0
- data/yard-stubs/carray_attribute.rb +246 -0
- data/yard-stubs/carray_broadcast.rb +37 -0
- data/yard-stubs/carray_cast.rb +489 -0
- data/yard-stubs/carray_class.rb +65 -0
- data/yard-stubs/carray_conversion.rb +76 -0
- data/yard-stubs/carray_copy.rb +79 -0
- data/yard-stubs/carray_core.rb +34 -0
- data/yard-stubs/carray_count.rb +84 -0
- data/yard-stubs/carray_element.rb +108 -0
- data/yard-stubs/carray_generate.rb +66 -0
- data/yard-stubs/carray_lazy.rb +228 -0
- data/yard-stubs/carray_loop.rb +140 -0
- data/yard-stubs/carray_mask.rb +259 -0
- data/yard-stubs/carray_math.rb +1615 -0
- data/yard-stubs/carray_mathfunc.rb +45 -0
- data/yard-stubs/carray_median_percentile.rb +103 -0
- data/yard-stubs/carray_memory_view.rb +163 -0
- data/yard-stubs/carray_order.rb +252 -0
- data/yard-stubs/carray_random.rb +89 -0
- data/yard-stubs/carray_scatter.rb +106 -0
- data/yard-stubs/carray_slab.rb +59 -0
- data/yard-stubs/carray_sort.rb +165 -0
- data/yard-stubs/carray_test.rb +85 -0
- data/yard-stubs/carray_undef.rb +64 -0
- data/yard-stubs/carray_utils.rb +97 -0
- data/yard-stubs/ruby_carray.rb +193 -0
- metadata +67 -10
- data/ext/ca_obj_unbound_repeat.c +0 -496
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
#include "carray.h"
|
|
2
|
+
|
|
3
|
+
/* ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
Address bases -- CArray::AddressBasis.
|
|
6
|
+
|
|
7
|
+
Some code addresses cells itself. A generated kernel reads a[i-1] and
|
|
8
|
+
writes a[i] from its own loop, so what it needs from CArray is not element
|
|
9
|
+
delivery but an addressing basis: a pointer, already shifted to cell zero,
|
|
10
|
+
and one byte stride per axis. That is why this does not sit on the kernel
|
|
11
|
+
iterator (per-cell / per-slab delivery, and no N-ary form) or on the sweep
|
|
12
|
+
ELEMENT family (which flattens the array and cannot recover the axis
|
|
13
|
+
structure a stencil needs).
|
|
14
|
+
|
|
15
|
+
This is a runtime facility at the same layer as ca_attach, not a user
|
|
16
|
+
surface. It hands raw addresses to Ruby, and nothing decodes them except a
|
|
17
|
+
consumer that already knows what to do with them -- today carray-jit, the
|
|
18
|
+
one companion carray knows by name, and the gem that runs its output
|
|
19
|
+
ahead of time. It is documented in guides/devel/, not in the user-facing
|
|
20
|
+
docs/ tree, and it is not advertised as API.
|
|
21
|
+
|
|
22
|
+
Arrays are classified in the order the public predicates suggest:
|
|
23
|
+
|
|
24
|
+
1. ca_is_entity -> the buffer is already the basis
|
|
25
|
+
2. ca_is_stride_family, -> ca_stride_compose_to_root folds the whole
|
|
26
|
+
and the fold reaches view chain into root + base + strides, so a
|
|
27
|
+
an entity transpose or a column slice is addressed in
|
|
28
|
+
place, with no gather and no scatter. A
|
|
29
|
+
fold that stops short of an entity is not
|
|
30
|
+
this tier; see folds_to_an_entity below
|
|
31
|
+
3. otherwise -> ca_xfer_stride moves only the box the kernel
|
|
32
|
+
actually touches -- the loop range grown by
|
|
33
|
+
how far the kernel reaches, per array and per
|
|
34
|
+
axis -- into a packed buffer, and writes that
|
|
35
|
+
box back
|
|
36
|
+
|
|
37
|
+
Tier 3 deliberately never calls ca_attach on the view. A whole-view
|
|
38
|
+
materialise costs the same whether the kernel touches ten cells or ten
|
|
39
|
+
million: measured on a four-million-element gather view, ca_attach was
|
|
40
|
+
2.7 ms regardless, while the region transfer was 0.001 ms for a hundred
|
|
41
|
+
cells and 2.3 ms for a million. A cost that does not scale with the work
|
|
42
|
+
is a cost the caller cannot reason about, and hiding one behind a JIT
|
|
43
|
+
would make its promise meaningless. What tier 3 does cost is proportional
|
|
44
|
+
to what the kernel asked to touch.
|
|
45
|
+
|
|
46
|
+
The block form (`open` with four arguments) yields one descriptive Hash per
|
|
47
|
+
array. The packed form (a fifth argument that is true) yields four byte
|
|
48
|
+
buffers instead, in a layout the consumer reads by offset; that layout is a
|
|
49
|
+
contract between the two and is written out as such in
|
|
50
|
+
guides/devel/21_address_basis.md. See packed_body below.
|
|
51
|
+
|
|
52
|
+
--------------------------------------------------------------------------- */
|
|
53
|
+
|
|
54
|
+
/* Rounded so that each section of the block below starts where the widest
|
|
55
|
+
thing in it may be read. */
|
|
56
|
+
#define ROUND_UP_8(n) (((n) + 7u) & ~(size_t) 7u)
|
|
57
|
+
|
|
58
|
+
#define TIER_ENTITY 1
|
|
59
|
+
#define TIER_STRIDE 2
|
|
60
|
+
#define TIER_XFER 3
|
|
61
|
+
|
|
62
|
+
/* Slot layout: slot i is array i, slot count + i is that array's mask.
|
|
63
|
+
A mask is a CArray of the same shape as its parent and, for a view, the
|
|
64
|
+
same kind of view -- a CABlock's mask is a CABlockMask -- so it is opened
|
|
65
|
+
by exactly the same tier logic as the data. */
|
|
66
|
+
typedef struct {
|
|
67
|
+
char *block; /* the one allocation the arrays below carve up */
|
|
68
|
+
int count;
|
|
69
|
+
int slots;
|
|
70
|
+
VALUE arrays;
|
|
71
|
+
CArray **carrays;
|
|
72
|
+
CArray **roots;
|
|
73
|
+
int *tier;
|
|
74
|
+
int *writable;
|
|
75
|
+
int *attached_root;
|
|
76
|
+
char **region; /* tier 3 packed buffer, NULL otherwise */
|
|
77
|
+
ca_size_t *region_start; /* count * CA_RANK_MAX */
|
|
78
|
+
ca_size_t *region_count;
|
|
79
|
+
VALUE bases;
|
|
80
|
+
VALUE box_starts; /* per array, per axis; nil for "all of it" */
|
|
81
|
+
VALUE box_counts;
|
|
82
|
+
} open_state;
|
|
83
|
+
|
|
84
|
+
/* The view's own row-major byte layout, which is the address space
|
|
85
|
+
ca_xfer_stride describes a region in. */
|
|
86
|
+
static void
|
|
87
|
+
native_steps (CArray *ca, ca_size_t *steps)
|
|
88
|
+
{
|
|
89
|
+
ca_size_t step = ca->bytes;
|
|
90
|
+
int8_t k;
|
|
91
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) {
|
|
92
|
+
steps[k] = step;
|
|
93
|
+
step *= ca->dim[k];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Checks one array's box description, all of it: that it is one start and one
|
|
98
|
+
count per axis, that they are numbers, and that the box they describe is
|
|
99
|
+
inside the array.
|
|
100
|
+
|
|
101
|
+
Run for every array before any of them is opened, whatever tier each turns
|
|
102
|
+
out to land in. Only tier 3 goes on to read the box -- tiers 1 and 2
|
|
103
|
+
address the whole array, which covers any box inside it -- so this is the
|
|
104
|
+
only place a caller's description is looked at at all for two of the three
|
|
105
|
+
tiers. Checking it there too is what keeps the same call refused the same
|
|
106
|
+
way whatever the arrays turn out to be, rather than a box that is wrong
|
|
107
|
+
about a plain array being noticed only once the same code is handed a
|
|
108
|
+
gather view. And it is a C extension, where a wrong type has to be a
|
|
109
|
+
message and not a crash. */
|
|
110
|
+
static void
|
|
111
|
+
verify_box (VALUE box_starts, VALUE box_counts, int index, CArray *ca)
|
|
112
|
+
{
|
|
113
|
+
VALUE starts, counts;
|
|
114
|
+
int8_t k;
|
|
115
|
+
if ( NIL_P(box_starts) ) return;
|
|
116
|
+
starts = rb_ary_entry(box_starts, index);
|
|
117
|
+
counts = rb_ary_entry(box_counts, index);
|
|
118
|
+
if ( NIL_P(starts) && NIL_P(counts) ) return;
|
|
119
|
+
Check_Type(starts, T_ARRAY);
|
|
120
|
+
Check_Type(counts, T_ARRAY);
|
|
121
|
+
if ( RARRAY_LEN(starts) != ca->ndim || RARRAY_LEN(counts) != ca->ndim ) {
|
|
122
|
+
rb_raise(rb_eArgError,
|
|
123
|
+
"a region is described by one start and one count per axis; "
|
|
124
|
+
"this array has %d", (int) ca->ndim);
|
|
125
|
+
}
|
|
126
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
127
|
+
ca_size_t start = NUM2LL(rb_ary_entry(starts, k));
|
|
128
|
+
ca_size_t count = NUM2LL(rb_ary_entry(counts, k));
|
|
129
|
+
if ( start < 0 || count < 0 || start + count > ca->dim[k] ) {
|
|
130
|
+
rb_raise(rb_eArgError,
|
|
131
|
+
"the requested region falls outside the array on axis %d", (int) k);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* Reads one array's box out of the Ruby-side description, defaulting to the
|
|
137
|
+
whole array. verify_box checked it, and checks it again here because this
|
|
138
|
+
is the last thing between a caller's numbers and pointer arithmetic. */
|
|
139
|
+
static void
|
|
140
|
+
read_box (open_state *state, int index, CArray *ca,
|
|
141
|
+
ca_size_t *starts, ca_size_t *counts)
|
|
142
|
+
{
|
|
143
|
+
VALUE per_array_start = Qnil, per_array_count = Qnil;
|
|
144
|
+
int8_t k;
|
|
145
|
+
|
|
146
|
+
if ( ! NIL_P(state->box_starts) ) {
|
|
147
|
+
per_array_start = rb_ary_entry(state->box_starts, index);
|
|
148
|
+
per_array_count = rb_ary_entry(state->box_counts, index);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
152
|
+
if ( NIL_P(per_array_start) ) {
|
|
153
|
+
starts[k] = 0;
|
|
154
|
+
counts[k] = ca->dim[k];
|
|
155
|
+
} else {
|
|
156
|
+
starts[k] = NUM2LL(rb_ary_entry(per_array_start, k));
|
|
157
|
+
counts[k] = NUM2LL(rb_ary_entry(per_array_count, k));
|
|
158
|
+
}
|
|
159
|
+
if ( starts[k] < 0 || counts[k] < 0 || starts[k] + counts[k] > ca->dim[k] ) {
|
|
160
|
+
rb_raise(rb_eArgError,
|
|
161
|
+
"the requested region falls outside the array on axis %d", (int) k);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
static void
|
|
167
|
+
row_major_strides (CArray *ca, ca_size_t *strides)
|
|
168
|
+
{
|
|
169
|
+
ca_size_t step = ca->bytes;
|
|
170
|
+
int8_t k;
|
|
171
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) {
|
|
172
|
+
strides[k] = step;
|
|
173
|
+
step *= ca->dim[k];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
static VALUE
|
|
178
|
+
size_array (ca_size_t *values, int8_t count)
|
|
179
|
+
{
|
|
180
|
+
VALUE list = rb_ary_new_capa(count);
|
|
181
|
+
int8_t k;
|
|
182
|
+
for ( k = 0; k < count; k++ ) {
|
|
183
|
+
rb_ary_push(list, LL2NUM((long long) values[k]));
|
|
184
|
+
}
|
|
185
|
+
return list;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Refuses what a generated kernel cannot express, rather than letting it
|
|
189
|
+
produce quietly wrong numbers. */
|
|
190
|
+
static void
|
|
191
|
+
verify_usable (VALUE object, CArray *ca, int writable)
|
|
192
|
+
{
|
|
193
|
+
if ( ca->data_type == CA_OBJECT ) {
|
|
194
|
+
rb_raise(rb_eArgError, "object arrays hold Ruby values, not numbers");
|
|
195
|
+
}
|
|
196
|
+
if ( writable && ca_is_readonly(ca) ) {
|
|
197
|
+
rb_raise(rb_eRuntimeError, "%"PRIsVALUE" is read-only",
|
|
198
|
+
rb_obj_class(object));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* The stride tier addresses the fold's root directly, which is only sound
|
|
203
|
+
when that root owns its memory. ca_stride_compose_to_root stops at the
|
|
204
|
+
first thing it cannot fold through, and that need not be an entity: a
|
|
205
|
+
CARefer over a gather view (`whole[whole >= 0].reshape(4, 4)`) folds one
|
|
206
|
+
step and lands on the CASelect. Attaching a root like that materialises a
|
|
207
|
+
temporary, and detaching it throws the kernel's writes away -- silently.
|
|
208
|
+
So a fold that does not reach an entity is not the stride tier; the box
|
|
209
|
+
transfer handles it, and moves only the cells the kernel asked for. */
|
|
210
|
+
static int
|
|
211
|
+
folds_to_an_entity (CArray *ca)
|
|
212
|
+
{
|
|
213
|
+
CArray *root;
|
|
214
|
+
ca_size_t strides[CA_RANK_MAX];
|
|
215
|
+
ca_size_t base = 0;
|
|
216
|
+
ca_stride_compose_to_root((CAStride *) ca, &root, strides, &base);
|
|
217
|
+
return ca_is_entity(root);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
static int
|
|
221
|
+
tier_for (CArray *ca)
|
|
222
|
+
{
|
|
223
|
+
if ( ca_is_entity(ca) ) return TIER_ENTITY;
|
|
224
|
+
if ( ca_is_stride_family(ca) && folds_to_an_entity(ca) ) return TIER_STRIDE;
|
|
225
|
+
return TIER_XFER;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* Attaches or transfers one array and answers where its first cell is,
|
|
229
|
+
filling `strides` with how far apart the rest are. This is everything a
|
|
230
|
+
basis says that a kernel actually reads; the hash around it is for the
|
|
231
|
+
callers that want to look. */
|
|
232
|
+
static char *
|
|
233
|
+
acquire_basis (open_state *state, int index, ca_size_t *strides)
|
|
234
|
+
{
|
|
235
|
+
CArray *ca = state->carrays[index];
|
|
236
|
+
ca_size_t base = 0;
|
|
237
|
+
char *pointer;
|
|
238
|
+
|
|
239
|
+
switch ( state->tier[index] ) {
|
|
240
|
+
case TIER_ENTITY: {
|
|
241
|
+
CArray *root = ca;
|
|
242
|
+
ca_attach(root);
|
|
243
|
+
state->roots[index] = root;
|
|
244
|
+
state->attached_root[index] = 1;
|
|
245
|
+
row_major_strides(ca, strides);
|
|
246
|
+
pointer = ca->ptr;
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
case TIER_STRIDE: {
|
|
251
|
+
CArray *root;
|
|
252
|
+
ca_stride_compose_to_root((CAStride *) ca, &root, strides, &base);
|
|
253
|
+
/* A view that reinterprets the element size -- refer(CA_INT32, ...) over
|
|
254
|
+
a float64 array -- gets a mask of its own shape, but one mask cell of
|
|
255
|
+
it covers a fraction of a parent cell, so writing cell i's mask also
|
|
256
|
+
marks its neighbour. A per-cell kernel writes cells independently and
|
|
257
|
+
cannot express that. */
|
|
258
|
+
if ( ca->mask && ca->bytes != root->bytes ) {
|
|
259
|
+
rb_raise(rb_eArgError,
|
|
260
|
+
"%"PRIsVALUE" reinterprets the element size and carries a mask; "
|
|
261
|
+
"its mask cells do not map one to one onto the parent's",
|
|
262
|
+
rb_obj_class(rb_ary_entry(state->arrays, index)));
|
|
263
|
+
}
|
|
264
|
+
ca_attach(root);
|
|
265
|
+
state->roots[index] = root;
|
|
266
|
+
state->attached_root[index] = 1;
|
|
267
|
+
pointer = root->ptr + base;
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
default: {
|
|
272
|
+
/* Only the requested box crosses, never the whole view. */
|
|
273
|
+
ca_size_t starts[CA_RANK_MAX], counts[CA_RANK_MAX], steps[CA_RANK_MAX];
|
|
274
|
+
ca_size_t elements = 1, shift = 0, step;
|
|
275
|
+
int8_t k;
|
|
276
|
+
char *buffer;
|
|
277
|
+
|
|
278
|
+
native_steps(ca, steps);
|
|
279
|
+
read_box(state, index, ca, starts, counts);
|
|
280
|
+
for ( k = 0; k < ca->ndim; k++ ) elements *= counts[k];
|
|
281
|
+
|
|
282
|
+
/* ca_xfer_stride packs the box row-major, so the buffer's strides come
|
|
283
|
+
from the box's own extents, not the view's. */
|
|
284
|
+
step = ca->bytes;
|
|
285
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) {
|
|
286
|
+
strides[k] = step;
|
|
287
|
+
step *= counts[k];
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
buffer = ALLOC_N(char, (elements > 0 ? elements : 1) * ca->bytes);
|
|
291
|
+
if ( elements > 0 ) {
|
|
292
|
+
ca_xfer_stride(ca, starts, counts, steps, buffer, CA_XFER_GET);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
state->region[index] = buffer;
|
|
296
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
297
|
+
state->region_start[index * CA_RANK_MAX + k] = starts[k];
|
|
298
|
+
state->region_count[index * CA_RANK_MAX + k] = counts[k];
|
|
299
|
+
shift += starts[k] * strides[k];
|
|
300
|
+
}
|
|
301
|
+
/* Shifted so that the box's first cell lands on buffer[0], the way a
|
|
302
|
+
view's base_offset shifts its parent's pointer. */
|
|
303
|
+
pointer = buffer - shift;
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return pointer;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
static VALUE
|
|
312
|
+
basis_for (open_state *state, int index)
|
|
313
|
+
{
|
|
314
|
+
CArray *ca = state->carrays[index];
|
|
315
|
+
ca_size_t strides[CA_RANK_MAX];
|
|
316
|
+
char *pointer = acquire_basis(state, index, strides);
|
|
317
|
+
VALUE result;
|
|
318
|
+
|
|
319
|
+
result = rb_hash_new();
|
|
320
|
+
rb_hash_aset(result, ID2SYM(rb_intern("tier")), INT2NUM(state->tier[index]));
|
|
321
|
+
rb_hash_aset(result, ID2SYM(rb_intern("pointer")),
|
|
322
|
+
ULL2NUM((unsigned long long)(uintptr_t) pointer));
|
|
323
|
+
rb_hash_aset(result, ID2SYM(rb_intern("strides")), size_array(strides, ca->ndim));
|
|
324
|
+
rb_hash_aset(result, ID2SYM(rb_intern("dim")), size_array(ca->dim, ca->ndim));
|
|
325
|
+
rb_hash_aset(result, ID2SYM(rb_intern("bytes")), LL2NUM((long long) ca->bytes));
|
|
326
|
+
rb_hash_aset(result, ID2SYM(rb_intern("data_type")), INT2NUM(ca->data_type));
|
|
327
|
+
rb_hash_aset(result, ID2SYM(rb_intern("writable")),
|
|
328
|
+
state->writable[index] ? Qtrue : Qfalse);
|
|
329
|
+
return result;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/* Each array's basis, with its mask's basis folded in under :mask_pointer
|
|
333
|
+
and :mask_strides (nil when the array carries no mask). */
|
|
334
|
+
static VALUE
|
|
335
|
+
open_body (VALUE argument)
|
|
336
|
+
{
|
|
337
|
+
open_state *state = (open_state *) argument;
|
|
338
|
+
int i;
|
|
339
|
+
for ( i = 0; i < state->count; i++ ) {
|
|
340
|
+
VALUE basis = basis_for(state, i);
|
|
341
|
+
if ( state->carrays[state->count + i] ) {
|
|
342
|
+
VALUE mask = basis_for(state, state->count + i);
|
|
343
|
+
rb_hash_aset(basis, ID2SYM(rb_intern("mask_pointer")),
|
|
344
|
+
rb_hash_aref(mask, ID2SYM(rb_intern("pointer"))));
|
|
345
|
+
rb_hash_aset(basis, ID2SYM(rb_intern("mask_strides")),
|
|
346
|
+
rb_hash_aref(mask, ID2SYM(rb_intern("strides"))));
|
|
347
|
+
} else {
|
|
348
|
+
rb_hash_aset(basis, ID2SYM(rb_intern("mask_pointer")), Qnil);
|
|
349
|
+
rb_hash_aset(basis, ID2SYM(rb_intern("mask_strides")), Qnil);
|
|
350
|
+
}
|
|
351
|
+
rb_ary_push(state->bases, basis);
|
|
352
|
+
}
|
|
353
|
+
return rb_yield(state->bases);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/* Closes in reverse order, and runs whether or not the kernel raised. */
|
|
357
|
+
static VALUE
|
|
358
|
+
open_ensure (VALUE argument)
|
|
359
|
+
{
|
|
360
|
+
open_state *state = (open_state *) argument;
|
|
361
|
+
int i;
|
|
362
|
+
for ( i = state->slots - 1; i >= 0; i-- ) {
|
|
363
|
+
CArray *ca = state->carrays[i];
|
|
364
|
+
if ( ca == NULL ) continue;
|
|
365
|
+
/* A tier-1 or tier-2 basis addresses the root's own memory, so a write is
|
|
366
|
+
already where it belongs. Only a region buffer has to be sent back. */
|
|
367
|
+
if ( state->region[i] ) {
|
|
368
|
+
ca_size_t elements = 1;
|
|
369
|
+
int8_t k;
|
|
370
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
371
|
+
elements *= state->region_count[i * CA_RANK_MAX + k];
|
|
372
|
+
}
|
|
373
|
+
if ( state->writable[i] && elements > 0 ) {
|
|
374
|
+
ca_size_t steps[CA_RANK_MAX];
|
|
375
|
+
native_steps(ca, steps);
|
|
376
|
+
ca_xfer_stride(ca, &state->region_start[i * CA_RANK_MAX],
|
|
377
|
+
&state->region_count[i * CA_RANK_MAX],
|
|
378
|
+
steps, state->region[i], CA_XFER_PUT);
|
|
379
|
+
}
|
|
380
|
+
xfree(state->region[i]);
|
|
381
|
+
}
|
|
382
|
+
if ( state->attached_root[i] ) {
|
|
383
|
+
ca_detach(state->roots[i]);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
xfree(state->block);
|
|
387
|
+
return Qnil;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/* What a kernel is handed, rather than what a reader wants to see.
|
|
391
|
+
*
|
|
392
|
+
* The hash form above exists so that a caller can ask an array how it was
|
|
393
|
+
* opened. A kernel never asks: it packs the pointers and the strides into
|
|
394
|
+
* four buffers and passes their addresses to the C. Building a Hash and an
|
|
395
|
+
* Array per array so that Ruby can immediately pack them back into bytes is
|
|
396
|
+
* a round trip through the object heap that nothing looks at, and it cost
|
|
397
|
+
* more than the opening did. So this writes the four buffers directly.
|
|
398
|
+
*
|
|
399
|
+
* An array with no mask still takes its slots in the mask strides, one per
|
|
400
|
+
* axis of its own, zero. Its own rank and not the kernel's: the generated C
|
|
401
|
+
* finds an array's mask strides at the sum of the ranks of the arrays before
|
|
402
|
+
* it, and an operand of lower rank than the kernel -- a row broadcast over a
|
|
403
|
+
* grid -- padded to the kernel's rank moved every mask after it.
|
|
404
|
+
*/
|
|
405
|
+
static VALUE
|
|
406
|
+
packed_body (VALUE argument)
|
|
407
|
+
{
|
|
408
|
+
open_state *state = (open_state *) argument;
|
|
409
|
+
int count = state->count;
|
|
410
|
+
int i;
|
|
411
|
+
long stride_slots = 0, mask_stride_slots = 0;
|
|
412
|
+
VALUE pointers, strides, mask_pointers, mask_strides;
|
|
413
|
+
uint64_t *pointer_slot, *mask_pointer_slot;
|
|
414
|
+
int64_t *stride_slot, *mask_stride_slot;
|
|
415
|
+
|
|
416
|
+
for ( i = 0; i < count; i++ ) {
|
|
417
|
+
stride_slots += state->carrays[i]->ndim;
|
|
418
|
+
mask_stride_slots += state->carrays[count + i]
|
|
419
|
+
? state->carrays[count + i]->ndim
|
|
420
|
+
: state->carrays[i]->ndim;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
pointers = rb_str_new(NULL, (long) (count * sizeof(uint64_t)));
|
|
424
|
+
mask_pointers = rb_str_new(NULL, (long) (count * sizeof(uint64_t)));
|
|
425
|
+
strides = rb_str_new(NULL, stride_slots * (long) sizeof(int64_t));
|
|
426
|
+
mask_strides = rb_str_new(NULL, mask_stride_slots * (long) sizeof(int64_t));
|
|
427
|
+
|
|
428
|
+
pointer_slot = (uint64_t *) RSTRING_PTR(pointers);
|
|
429
|
+
mask_pointer_slot = (uint64_t *) RSTRING_PTR(mask_pointers);
|
|
430
|
+
stride_slot = (int64_t *) RSTRING_PTR(strides);
|
|
431
|
+
mask_stride_slot = (int64_t *) RSTRING_PTR(mask_strides);
|
|
432
|
+
|
|
433
|
+
for ( i = 0; i < count; i++ ) {
|
|
434
|
+
CArray *ca = state->carrays[i];
|
|
435
|
+
ca_size_t own[CA_RANK_MAX];
|
|
436
|
+
char *pointer = acquire_basis(state, i, own);
|
|
437
|
+
int8_t k;
|
|
438
|
+
|
|
439
|
+
*pointer_slot++ = (uint64_t)(uintptr_t) pointer;
|
|
440
|
+
for ( k = 0; k < ca->ndim; k++ ) *stride_slot++ = (int64_t) own[k];
|
|
441
|
+
|
|
442
|
+
if ( state->carrays[count + i] ) {
|
|
443
|
+
CArray *mask = state->carrays[count + i];
|
|
444
|
+
ca_size_t mask_own[CA_RANK_MAX];
|
|
445
|
+
char *mask_pointer = acquire_basis(state, count + i, mask_own);
|
|
446
|
+
*mask_pointer_slot++ = (uint64_t)(uintptr_t) mask_pointer;
|
|
447
|
+
for ( k = 0; k < mask->ndim; k++ ) *mask_stride_slot++ = (int64_t) mask_own[k];
|
|
448
|
+
} else {
|
|
449
|
+
*mask_pointer_slot++ = 0;
|
|
450
|
+
for ( k = 0; k < ca->ndim; k++ ) *mask_stride_slot++ = 0;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return rb_yield_values(4, pointers, strides, mask_pointers, mask_strides);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/*
|
|
458
|
+
* Opens every array, yields what it opened, and closes them all on the way
|
|
459
|
+
* out -- including when the block raises.
|
|
460
|
+
*
|
|
461
|
+
* With four arguments the block is handed one basis hash per array, which is
|
|
462
|
+
* the form to read an array's opening in. Given a fifth that is true, it is
|
|
463
|
+
* handed the four packed buffers a kernel passes to the C instead: pointers,
|
|
464
|
+
* strides, mask pointers, mask strides.
|
|
465
|
+
*/
|
|
466
|
+
static VALUE
|
|
467
|
+
address_basis_open (int argc, VALUE *argv, VALUE module)
|
|
468
|
+
{
|
|
469
|
+
VALUE arrays, writable_flags, box_start, box_count, packed;
|
|
470
|
+
open_state state;
|
|
471
|
+
int i;
|
|
472
|
+
|
|
473
|
+
rb_scan_args(argc, argv, "23", &arrays, &writable_flags, &box_start,
|
|
474
|
+
&box_count, &packed);
|
|
475
|
+
Check_Type(arrays, T_ARRAY);
|
|
476
|
+
Check_Type(writable_flags, T_ARRAY);
|
|
477
|
+
if ( NIL_P(box_start) != NIL_P(box_count) ) {
|
|
478
|
+
rb_raise(rb_eArgError, "a region needs both starts and counts");
|
|
479
|
+
}
|
|
480
|
+
if ( ! NIL_P(box_start) ) {
|
|
481
|
+
Check_Type(box_start, T_ARRAY);
|
|
482
|
+
Check_Type(box_count, T_ARRAY);
|
|
483
|
+
if ( RARRAY_LEN(box_start) != RARRAY_LEN(arrays) ||
|
|
484
|
+
RARRAY_LEN(box_count) != RARRAY_LEN(arrays) ) {
|
|
485
|
+
rb_raise(rb_eArgError, "one region per array is required");
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if ( RARRAY_LEN(arrays) != RARRAY_LEN(writable_flags) ) {
|
|
489
|
+
rb_raise(rb_eArgError, "one writable flag per array is required");
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
state.count = (int) RARRAY_LEN(arrays);
|
|
493
|
+
state.slots = state.count * 2;
|
|
494
|
+
state.arrays = arrays;
|
|
495
|
+
state.bases = rb_ary_new_capa(state.count);
|
|
496
|
+
/* One allocation, carved up, rather than eight. Every array here is the
|
|
497
|
+
same length in slots and lives exactly as long as the open, so there is
|
|
498
|
+
nothing for eight separate lifetimes to buy -- and this is a per-call
|
|
499
|
+
cost on a path whose whole point is to be cheap enough to cross often.
|
|
500
|
+
Zeroed once as a block, which is also what the slots want to start as:
|
|
501
|
+
a NULL array, a NULL root, not writable, not attached, no region. */
|
|
502
|
+
{
|
|
503
|
+
size_t n = (size_t) state.slots + 1;
|
|
504
|
+
size_t pointers = ROUND_UP_8(n * sizeof(CArray *));
|
|
505
|
+
size_t chars = ROUND_UP_8(n * sizeof(char *));
|
|
506
|
+
size_t sizes = ROUND_UP_8(n * CA_RANK_MAX * sizeof(ca_size_t));
|
|
507
|
+
size_t ints = ROUND_UP_8(n * sizeof(int));
|
|
508
|
+
size_t total = 2 * pointers + chars + 2 * sizes + 3 * ints;
|
|
509
|
+
char *p;
|
|
510
|
+
state.block = ALLOC_N(char, total);
|
|
511
|
+
MEMZERO(state.block, char, total);
|
|
512
|
+
p = state.block;
|
|
513
|
+
state.carrays = (CArray **) p; p += pointers;
|
|
514
|
+
state.roots = (CArray **) p; p += pointers;
|
|
515
|
+
state.region = (char **) p; p += chars;
|
|
516
|
+
state.region_start = (ca_size_t *) p; p += sizes;
|
|
517
|
+
state.region_count = (ca_size_t *) p; p += sizes;
|
|
518
|
+
state.tier = (int *) p; p += ints;
|
|
519
|
+
state.writable = (int *) p; p += ints;
|
|
520
|
+
state.attached_root = (int *) p;
|
|
521
|
+
}
|
|
522
|
+
state.box_starts = box_start;
|
|
523
|
+
state.box_counts = box_count;
|
|
524
|
+
|
|
525
|
+
for ( i = 0; i < state.count; i++ ) {
|
|
526
|
+
VALUE object = rb_ary_entry(arrays, i);
|
|
527
|
+
CArray *ca;
|
|
528
|
+
GetCArray(object, ca);
|
|
529
|
+
state.carrays[i] = ca;
|
|
530
|
+
state.writable[i] = RTEST(rb_ary_entry(writable_flags, i));
|
|
531
|
+
verify_usable(object, ca, state.writable[i]);
|
|
532
|
+
verify_box(box_start, box_count, i, ca);
|
|
533
|
+
state.tier[i] = tier_for(ca);
|
|
534
|
+
|
|
535
|
+
if ( ca->mask ) {
|
|
536
|
+
CArray *mask = ca->mask;
|
|
537
|
+
state.carrays[state.count + i] = mask;
|
|
538
|
+
state.writable[state.count + i] = state.writable[i];
|
|
539
|
+
state.tier[state.count + i] = tier_for(mask);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if ( RTEST(packed) ) {
|
|
544
|
+
return rb_ensure(packed_body, (VALUE) &state, open_ensure, (VALUE) &state);
|
|
545
|
+
} else {
|
|
546
|
+
return rb_ensure(open_body, (VALUE) &state, open_ensure, (VALUE) &state);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/* Reports how an array would be opened, without opening it. */
|
|
551
|
+
static VALUE
|
|
552
|
+
address_basis_classify (VALUE module, VALUE object)
|
|
553
|
+
{
|
|
554
|
+
CArray *ca;
|
|
555
|
+
VALUE result;
|
|
556
|
+
int tier;
|
|
557
|
+
|
|
558
|
+
GetCArray(object, ca);
|
|
559
|
+
tier = tier_for(ca);
|
|
560
|
+
|
|
561
|
+
result = rb_hash_new();
|
|
562
|
+
rb_hash_aset(result, ID2SYM(rb_intern("tier")), INT2NUM(tier));
|
|
563
|
+
rb_hash_aset(result, ID2SYM(rb_intern("entity")), ca_is_entity(ca) ? Qtrue : Qfalse);
|
|
564
|
+
rb_hash_aset(result, ID2SYM(rb_intern("stride_family")),
|
|
565
|
+
ca_is_stride_family(ca) ? Qtrue : Qfalse);
|
|
566
|
+
rb_hash_aset(result, ID2SYM(rb_intern("read_only")), ca_is_readonly(ca) ? Qtrue : Qfalse);
|
|
567
|
+
rb_hash_aset(result, ID2SYM(rb_intern("masked")), ca_has_mask(ca) ? Qtrue : Qfalse);
|
|
568
|
+
rb_hash_aset(result, ID2SYM(rb_intern("dim")), size_array(ca->dim, ca->ndim));
|
|
569
|
+
rb_hash_aset(result, ID2SYM(rb_intern("bytes")), LL2NUM((long long) ca->bytes));
|
|
570
|
+
rb_hash_aset(result, ID2SYM(rb_intern("data_type")), INT2NUM(ca->data_type));
|
|
571
|
+
return result;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/* ------------------------------------------------------------------- */
|
|
575
|
+
/* Init_carray_address_basis */
|
|
576
|
+
/* ------------------------------------------------------------------- */
|
|
577
|
+
|
|
578
|
+
void
|
|
579
|
+
Init_carray_address_basis (void)
|
|
580
|
+
{
|
|
581
|
+
VALUE mAddressBasis = rb_define_module_under(rb_cCArray, "AddressBasis");
|
|
582
|
+
|
|
583
|
+
rb_define_singleton_method(mAddressBasis, "open", address_basis_open, -1);
|
|
584
|
+
rb_define_singleton_method(mAddressBasis, "classify",
|
|
585
|
+
address_basis_classify, 1);
|
|
586
|
+
|
|
587
|
+
rb_define_const(mAddressBasis, "TIER_ENTITY", INT2NUM(TIER_ENTITY));
|
|
588
|
+
rb_define_const(mAddressBasis, "TIER_STRIDE", INT2NUM(TIER_STRIDE));
|
|
589
|
+
rb_define_const(mAddressBasis, "TIER_XFER", INT2NUM(TIER_XFER));
|
|
590
|
+
}
|
data/ext/carray_bincount.c
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
carray_bincount.c — dedicated bincount kernels (count-only + weighted)
|
|
4
4
|
|
|
5
5
|
Tight per-element scatter into a pre-sized 1-D output buffer. The
|
|
6
|
-
inner loop reads labels in their native integer
|
|
6
|
+
inner loop reads labels in their native integer data type (no cast to
|
|
7
7
|
int64), skips per-iteration bounds checking (caller validates label
|
|
8
|
-
range), and emits the output
|
|
8
|
+
range), and emits the output data type directly.
|
|
9
9
|
|
|
10
|
-
Output
|
|
10
|
+
Output data type:
|
|
11
11
|
- count-only: UInt32 if length < 2^32, else UInt64.
|
|
12
12
|
- weighted: weights.data_type.
|
|
13
13
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
weight is also skipped (its label contributes 0).
|
|
16
16
|
|
|
17
17
|
Caller contract (= lib/carray/methods/bincount.rb's CArray#bincount):
|
|
18
|
-
- self is integer
|
|
18
|
+
- self is integer data type.
|
|
19
19
|
- length is the output size, pre-sized to max(length, label_max+1)
|
|
20
20
|
with label_min >= 0 already verified.
|
|
21
21
|
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
|
|
31
31
|
#include "carray.h"
|
|
32
32
|
|
|
33
|
-
/* Tight inner loop: 2 mask-aware variants × 8 label
|
|
34
|
-
|
|
33
|
+
/* Tight inner loop: 2 mask-aware variants × 8 label data types × 2 output
|
|
34
|
+
data types = 32 specializations. Generated via macro expansion.
|
|
35
35
|
|
|
36
36
|
Layout:
|
|
37
37
|
COUNT_KERNEL(LABEL_T, OUT_T)
|
|
@@ -91,7 +91,7 @@ rb_ca_bincount_count_kernel (VALUE self, VALUE rlength)
|
|
|
91
91
|
rb_raise(rb_eArgError, "bincount: length must be non-negative");
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
/* Output
|
|
94
|
+
/* Output data type: UInt32 default; UInt64 if length doesn't fit. */
|
|
95
95
|
out_type = (length > 0xFFFFFFFFLL) ? CA_UINT64 : CA_UINT32;
|
|
96
96
|
shape_out[0] = length;
|
|
97
97
|
vout = rb_carray_new(out_type, 1, shape_out, 0, NULL);
|
|
@@ -125,7 +125,7 @@ rb_ca_bincount_count_kernel (VALUE self, VALUE rlength)
|
|
|
125
125
|
|
|
126
126
|
/* --------------------------------------------------------------- */
|
|
127
127
|
|
|
128
|
-
/* Weighted variant: output
|
|
128
|
+
/* Weighted variant: output data type = weights data type.
|
|
129
129
|
Inner: out[label[i]] += weight[i].
|
|
130
130
|
Mask: skip if label[i] masked OR weight[i] masked. */
|
|
131
131
|
|