carray 3.0.4 → 3.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +438 -0
- data/README.md +1 -1
- data/carray.gemspec +3 -2
- data/ext/ca_array_pool.c +25 -1
- data/ext/ca_axis_dispatch.c +19 -20
- data/ext/ca_axis_group.c +1 -1
- data/ext/ca_categorical_iterator.c +34 -27
- data/ext/ca_compare.c +4 -4
- data/ext/ca_composite_dispatch.c +9 -9
- data/ext/ca_kernel_iterator.c +122 -237
- data/ext/ca_kernel_iterator.h +522 -337
- data/ext/ca_obj_array.c +69 -19
- data/ext/ca_obj_bincmp.c +10 -1
- data/ext/ca_obj_binop.c +11 -1
- data/ext/ca_obj_bitarray.c +5 -1
- data/ext/ca_obj_bitfield.c +60 -43
- data/ext/ca_obj_byte_swap.c +4 -1
- data/ext/ca_obj_const_string.c +15 -3
- data/ext/ca_obj_face.c +44 -1
- data/ext/ca_obj_fake.c +3 -1
- data/ext/ca_obj_field.c +13 -3
- data/ext/ca_obj_grid.c +219 -17
- data/ext/ca_obj_meld.c +69 -113
- data/ext/ca_obj_moncmp.c +9 -1
- data/ext/ca_obj_monop.c +86 -12
- data/ext/ca_obj_object.c +118 -32
- data/ext/ca_obj_record.c +3 -0
- data/ext/ca_obj_reduce.c +19 -1
- data/ext/ca_obj_refer.c +80 -26
- data/ext/ca_obj_remap.c +69 -10
- data/ext/ca_obj_roll.c +27 -6
- data/ext/ca_obj_select.c +38 -6
- data/ext/ca_obj_select_axis.c +1 -0
- data/ext/ca_obj_shift.c +32 -7
- data/ext/ca_obj_stack.c +311 -111
- data/ext/ca_obj_stride.c +159 -64
- data/ext/ca_obj_string.c +27 -2
- data/ext/ca_obj_tile.c +28 -7
- data/ext/ca_obj_transpose.c +8 -4
- data/ext/ca_obj_triop.c +12 -1
- data/ext/ca_obj_window.c +80 -50
- data/ext/ca_op_ipower.c +12 -12
- data/ext/ca_sort_kernels.h +6 -3
- data/ext/caframe_csv_reader.c +1 -1
- data/ext/carray.h +35 -1
- data/ext/carray_access.c +43 -67
- data/ext/carray_address_basis.c +134 -0
- data/ext/carray_attribute.c +32 -0
- data/ext/carray_broadcast.c +3 -4
- data/ext/carray_build_flags.h +1 -1
- data/ext/carray_cast.c +251 -93
- data/ext/carray_cast_func.rb +56 -26
- data/ext/carray_conversion.c +11 -7
- data/ext/carray_core.c +814 -72
- data/ext/carray_count.c +6 -0
- data/ext/carray_element.c +22 -13
- data/ext/carray_factorize.c +20 -20
- data/ext/carray_generate.c +8 -2
- data/ext/carray_histogram.c +26 -4
- data/ext/carray_index_classifier.c +4 -4
- data/ext/carray_internal.h +159 -12
- data/ext/carray_kernels_bincmp.c +2174 -170
- data/ext/carray_kernels_binop.c +25653 -5487
- data/ext/carray_kernels_init.c +8 -8
- data/ext/carray_kernels_moncmp.c +96 -96
- data/ext/carray_kernels_monop.c +12253 -5235
- data/ext/carray_kernels_reduce_aggregate.c +6150 -2423
- data/ext/carray_kernels_reduce_boolean.c +1533 -15
- data/ext/carray_kernels_reduce_cumulative.c +2442 -882
- data/ext/carray_kernels_reduce_extreme.c +5658 -1661
- data/ext/carray_kernels_reduce_variance.c +2534 -402
- data/ext/carray_kernels_scan.c +368 -368
- data/ext/carray_kernels_search.c +4551 -604
- data/ext/carray_kernels_sort.c +659 -751
- data/ext/carray_kernels_triop.c +114 -114
- data/ext/carray_key_windows.c +4 -0
- data/ext/carray_lazy.c +47 -0
- data/ext/carray_loop.c +1 -1
- data/ext/carray_mask.c +175 -161
- data/ext/carray_median_percentile.c +4 -4
- data/ext/carray_memory_view.c +15 -9
- data/ext/carray_operator.c +152 -49
- data/ext/carray_order.c +230 -10
- data/ext/carray_random.c +22 -19
- data/ext/carray_scatter.c +24 -7
- data/ext/carray_slab.c +128 -29
- data/ext/carray_sort.c +224 -16
- data/ext/carray_sort_kernel.c +5 -7
- data/ext/carray_struct.c +35 -43
- data/ext/carray_test.c +74 -19
- data/ext/carray_time_text.c +6 -4
- data/ext/carray_utils.c +63 -21
- data/ext/extconf.rb +11 -0
- data/ext/mkkernel.rb +546 -199
- data/ext/ruby_carray.c +4 -1
- data/ext/version.h +4 -4
- data/lib/carray/attribute.rb +42 -5
- data/lib/carray/autoload_carray.rb +3 -3
- data/lib/carray/autoload_method_extension.rb +5 -3
- data/lib/carray/axis_group.rb +13 -7
- data/lib/carray/basics.rb +28 -8
- data/lib/carray/block_iterator.rb +17 -3
- data/lib/carray/categorical.rb +35 -23
- data/lib/carray/categorical_iterator.rb +8 -8
- data/lib/carray/complex.rb +2 -2
- data/lib/carray/conditional.rb +3 -2
- data/lib/carray/const_string.rb +10 -0
- data/lib/carray/data_type_extension.rb +8 -1
- data/lib/carray/fixlen_string.rb +41 -2
- data/lib/carray/frame/column_verbs.rb +48 -16
- data/lib/carray/frame/concat.rb +17 -3
- data/lib/carray/frame/convert.rb +19 -0
- data/lib/carray/frame/csv_parser.rb +32 -32
- data/lib/carray/frame/describe.rb +4 -2
- data/lib/carray/frame/frame.rb +405 -85
- data/lib/carray/frame/group.rb +777 -64
- data/lib/carray/frame/io.rb +212 -72
- data/lib/carray/frame/join.rb +146 -37
- data/lib/carray/frame/records.rb +3 -3
- data/lib/carray/frame/reshape.rb +89 -11
- data/lib/carray/frame/sort.rb +57 -9
- data/lib/carray/frame/verbs.rb +384 -49
- data/lib/carray/frame/windows.rb +44 -6
- data/lib/carray/fuse_source.rb +31 -2
- data/lib/carray/inspect.rb +7 -2
- data/lib/carray/iterator.rb +32 -0
- data/lib/carray/lazy.rb +52 -8
- data/lib/carray/mask_gap_fill.rb +37 -17
- data/lib/carray/meld_reduce.rb +1 -1
- data/lib/carray/methods/choose.rb +3 -2
- data/lib/carray/methods/is_in.rb +8 -1
- data/lib/carray/methods/meshgrid.rb +3 -1
- data/lib/carray/runtime.rb +47 -8
- data/lib/carray/segment_iterator.rb +35 -16
- data/lib/carray/serialize.rb +53 -19
- data/lib/carray/stack.rb +33 -18
- data/lib/carray/string.rb +8 -0
- data/lib/carray/string_operation_extension.rb +13 -5
- data/lib/carray/struct_builder.rb +5 -3
- data/lib/carray/time.rb +112 -68
- data/lib/carray/window_iterator.rb +16 -4
- data/yard-stubs/ca_obj_array.rb +26 -1
- data/yard-stubs/ca_obj_transpose.rb +5 -4
- data/yard-stubs/carray_attribute.rb +3 -1
- data/yard-stubs/carray_cast.rb +10 -5
- data/yard-stubs/carray_conversion.rb +13 -3
- data/yard-stubs/carray_element.rb +7 -6
- data/yard-stubs/carray_mask.rb +28 -4
- data/yard-stubs/carray_math.rb +45 -20
- data/yard-stubs/carray_memory_view.rb +7 -0
- data/yard-stubs/carray_order.rb +100 -4
- data/yard-stubs/carray_scatter.rb +7 -5
- data/yard-stubs/carray_test.rb +7 -4
- metadata +18 -4
data/ext/carray_slab.c
CHANGED
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
|
|
14
14
|
Internals:
|
|
15
15
|
- ca_slab_iter_state_t (see carray_slab.h) is the per-call state struct.
|
|
16
|
-
- Each entry point
|
|
16
|
+
- Each entry point heap-allocates a zeroed state under a window guard
|
|
17
|
+
(slab_state_new), populates it via
|
|
17
18
|
slab_state_init (= nofail; only validation + scalar field fills),
|
|
18
19
|
then rb_ensure-runs slab_state_run_body (= acquires T1 substrate +
|
|
19
20
|
scratch + output, runs per-form loop) and slab_state_finish (= frees
|
|
@@ -21,9 +22,8 @@
|
|
|
21
22
|
- All heap acquisition lives in run_body so init-time rb_raise paths
|
|
22
23
|
cannot leak; rb_ensure guarantees finish on both normal and
|
|
23
24
|
exception exit.
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
not Ruby-wrapped.
|
|
25
|
+
- The guard marks the state's VALUE fields, and runs slab_state_finish
|
|
26
|
+
from a finalizer if the block's Fiber is abandoned mid-walk.
|
|
27
27
|
|
|
28
28
|
--------------------------------------------------------------------------- */
|
|
29
29
|
|
|
@@ -122,9 +122,9 @@ slab_state_init (ca_slab_iter_state_t *st, VALUE source, int8_t form,
|
|
|
122
122
|
{
|
|
123
123
|
CArray *src;
|
|
124
124
|
|
|
125
|
-
/*
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
/* The state arrives zeroed, which leaves VALUE fields Qfalse (= 0).
|
|
126
|
+
Body code uses `== Qnil` to distinguish unset slots from populated
|
|
127
|
+
CArray handles, so we must promote them to Qnil here. */
|
|
128
128
|
st->self = Qnil;
|
|
129
129
|
st->slab_view = Qnil;
|
|
130
130
|
st->carrier = Qnil;
|
|
@@ -346,6 +346,26 @@ ca_slab_build_output_view (int8_t data_type, ca_size_t bytes,
|
|
|
346
346
|
return TypedData_Wrap_Struct(rb_cCAWrap, &cawrap_data_type, cw);
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
+
/* The array the slab views move onto when the walk ends (see
|
|
350
|
+
slab_state_release_views), made with the carrier and held by it. It is
|
|
351
|
+
made here rather than at the end: the end runs in an ensure, after the
|
|
352
|
+
block, and an allocation failing there would leave the walk open and
|
|
353
|
+
the views pointing into freed scratch. */
|
|
354
|
+
static void
|
|
355
|
+
ca_slab_prepare_backing (ca_slab_iter_state_t *st)
|
|
356
|
+
{
|
|
357
|
+
CArray *cw, *backing;
|
|
358
|
+
volatile VALUE bv;
|
|
359
|
+
TypedData_Get_Struct(st->carrier, CArray, &carray_data_type, cw);
|
|
360
|
+
bv = rb_carray_new(cw->data_type, cw->ndim, cw->dim, cw->bytes, NULL);
|
|
361
|
+
TypedData_Get_Struct(bv, CArray, &carray_data_type, backing);
|
|
362
|
+
if ( cw->mask ) {
|
|
363
|
+
ca_create_mask(backing);
|
|
364
|
+
}
|
|
365
|
+
rb_ivar_set(st->carrier, rb_intern("__slab_backing__"), bv);
|
|
366
|
+
RB_GC_GUARD(bv);
|
|
367
|
+
}
|
|
368
|
+
|
|
349
369
|
/* Common setup of the input slab view for all 4 forms. Decides ALIAS
|
|
350
370
|
vs SCRATCH from T1's slab_strides[0] vs src->bytes equality, builds
|
|
351
371
|
the slab_view + (when SCRATCH) carrier in place. The mask travels
|
|
@@ -465,6 +485,10 @@ ca_slab_setup_input_slab_view (ca_slab_iter_state_t *st, CArray *src,
|
|
|
465
485
|
}
|
|
466
486
|
}
|
|
467
487
|
|
|
488
|
+
if ( st->carrier != Qnil ) {
|
|
489
|
+
ca_slab_prepare_backing(st);
|
|
490
|
+
}
|
|
491
|
+
|
|
468
492
|
/* The slab is a window onto the source when the fiber is contiguous and
|
|
469
493
|
a copy in the iterator's scratch when it is not, so writing through it
|
|
470
494
|
landed on one axis and vanished on the next. Nothing in the surface
|
|
@@ -502,8 +526,12 @@ ca_slab_advance_slab_view (ca_slab_iter_state_t *st,
|
|
|
502
526
|
if ( slab_cs->mask != NULL ) {
|
|
503
527
|
CAStride *mask_cs = (CAStride *) slab_cs->mask;
|
|
504
528
|
if ( m_in != NULL ) {
|
|
529
|
+
/* m_in points into the walk's copy of the source's mask, not into
|
|
530
|
+
the source's mask itself, so the offset is taken from the data
|
|
531
|
+
cell: it is what the slab reads once the copy is gone. */
|
|
505
532
|
mask_cs->ptr = (char *) m_in;
|
|
506
|
-
mask_cs->base_offset = (ca_size_t) ((
|
|
533
|
+
mask_cs->base_offset = (ca_size_t) ((p_in - src->ptr) / src->bytes)
|
|
534
|
+
* src->mask->bytes;
|
|
507
535
|
}
|
|
508
536
|
else {
|
|
509
537
|
/* The walk was opened on a source without a mask and the block
|
|
@@ -861,7 +889,6 @@ ca_slab_reduce_setup (ca_slab_iter_state_t *st, CArray **out_src,
|
|
|
861
889
|
ca_size_t *out_n)
|
|
862
890
|
{
|
|
863
891
|
CArray *src;
|
|
864
|
-
CArray *out;
|
|
865
892
|
ca_size_t fiber_len;
|
|
866
893
|
ca_size_t out_dim[CA_RANK_MAX];
|
|
867
894
|
int8_t out_ndim;
|
|
@@ -904,7 +931,6 @@ ca_slab_reduce_setup (ca_slab_iter_state_t *st, CArray **out_src,
|
|
|
904
931
|
|
|
905
932
|
st->output = rb_carray_new(st->out_data_type, out_ndim, out_dim,
|
|
906
933
|
st->out_bytes, NULL);
|
|
907
|
-
TypedData_Get_Struct(st->output, CArray, &carray_data_type, out);
|
|
908
934
|
|
|
909
935
|
/* FIBER_CONTIG is naxes==1 only (T1 substrate contract). Multi-axis
|
|
910
936
|
omits the flag and uses bare CA_SLAB_AXES; T1 picks an in-src
|
|
@@ -1178,8 +1204,11 @@ slab_state_run_body (VALUE arg)
|
|
|
1178
1204
|
as it has a ptr. Before the scratch they point into is freed, the
|
|
1179
1205
|
input slab and its carrier are moved onto a copy of the last slab,
|
|
1180
1206
|
held by the carrier, and the internal output view lets go of its
|
|
1181
|
-
buffer.
|
|
1182
|
-
|
|
1207
|
+
buffer. The copy is made into the array ca_slab_prepare_backing set
|
|
1208
|
+
aside, so nothing here allocates. A slab that is a window onto the
|
|
1209
|
+
source keeps reading the source's data, but its mask points into the
|
|
1210
|
+
walk's copy of the mask, which finish frees: it lets go of that copy
|
|
1211
|
+
and reads the source's mask from then on. */
|
|
1183
1212
|
static void
|
|
1184
1213
|
slab_state_release_views (ca_slab_iter_state_t *st)
|
|
1185
1214
|
{
|
|
@@ -1188,6 +1217,13 @@ slab_state_release_views (ca_slab_iter_state_t *st)
|
|
|
1188
1217
|
TypedData_Get_Struct(st->output_slab_view, CArray, &carray_data_type, ov);
|
|
1189
1218
|
ov->ptr = NULL;
|
|
1190
1219
|
}
|
|
1220
|
+
if ( st->carrier == Qnil && st->slab_view != Qnil ) {
|
|
1221
|
+
CArray *cs;
|
|
1222
|
+
TypedData_Get_Struct(st->slab_view, CArray, &carray_data_type, cs);
|
|
1223
|
+
if ( cs->mask ) {
|
|
1224
|
+
cs->mask->ptr = NULL;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1191
1227
|
if ( st->carrier != Qnil && st->slab_view != Qnil ) {
|
|
1192
1228
|
CArray *cw, *cs, *backing;
|
|
1193
1229
|
volatile VALUE bv;
|
|
@@ -1196,12 +1232,12 @@ slab_state_release_views (ca_slab_iter_state_t *st)
|
|
|
1196
1232
|
if ( cw->ptr == NULL ) {
|
|
1197
1233
|
return;
|
|
1198
1234
|
}
|
|
1199
|
-
|
|
1200
|
-
bv
|
|
1201
|
-
|
|
1202
|
-
|
|
1235
|
+
bv = rb_ivar_get(st->carrier, rb_intern("__slab_backing__"));
|
|
1236
|
+
TypedData_Get_Struct(bv, CArray, &carray_data_type, backing);
|
|
1237
|
+
memcpy(backing->ptr, cw->ptr, (size_t) (cw->elements * cw->bytes));
|
|
1238
|
+
if ( cw->mask && cw->mask->ptr ) {
|
|
1239
|
+
memcpy(backing->mask->ptr, cw->mask->ptr, (size_t) cw->elements);
|
|
1203
1240
|
}
|
|
1204
|
-
rb_ivar_set(st->carrier, rb_intern("__slab_backing__"), bv);
|
|
1205
1241
|
if ( cs->ptr ) {
|
|
1206
1242
|
cs->ptr = backing->ptr;
|
|
1207
1243
|
}
|
|
@@ -1252,6 +1288,72 @@ slab_state_finish (VALUE arg)
|
|
|
1252
1288
|
/* Entry points: CArray#map_slab / #reduce_slab / #each_slab */
|
|
1253
1289
|
/* ------------------------------------------------------------------- */
|
|
1254
1290
|
|
|
1291
|
+
/* The walk yields to the block with its windows open. The state lives on
|
|
1292
|
+
the heap under a window guard, so that if the block's Fiber is abandoned
|
|
1293
|
+
(an Enumerator stopped with #next and dropped) the guard's finalizer
|
|
1294
|
+
still runs slab_state_finish and the source is let go. */
|
|
1295
|
+
|
|
1296
|
+
static void
|
|
1297
|
+
slab_state_mark (void *p)
|
|
1298
|
+
{
|
|
1299
|
+
ca_slab_iter_state_t *st = (ca_slab_iter_state_t *) p;
|
|
1300
|
+
rb_gc_mark(st->self);
|
|
1301
|
+
rb_gc_mark(st->slab_view);
|
|
1302
|
+
rb_gc_mark(st->carrier);
|
|
1303
|
+
rb_gc_mark(st->mask_carrier);
|
|
1304
|
+
rb_gc_mark(st->output);
|
|
1305
|
+
rb_gc_mark(st->output_slab_view);
|
|
1306
|
+
rb_gc_mark(st->init_val);
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
static void
|
|
1310
|
+
slab_state_finish_abandoned (void *p)
|
|
1311
|
+
{
|
|
1312
|
+
slab_state_finish((VALUE)(uintptr_t) p);
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
typedef struct {
|
|
1316
|
+
ca_slab_iter_state_t *st;
|
|
1317
|
+
VALUE guard;
|
|
1318
|
+
VALUE sentinel;
|
|
1319
|
+
} slab_run_t;
|
|
1320
|
+
|
|
1321
|
+
static VALUE
|
|
1322
|
+
slab_run_body (VALUE arg)
|
|
1323
|
+
{
|
|
1324
|
+
return slab_state_run_body((VALUE)(uintptr_t) ((slab_run_t *) arg)->st);
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
static VALUE
|
|
1328
|
+
slab_run_close (VALUE arg)
|
|
1329
|
+
{
|
|
1330
|
+
slab_run_t *r = (slab_run_t *) arg;
|
|
1331
|
+
ca_window_guard_disarm(r->guard, r->sentinel);
|
|
1332
|
+
return slab_state_finish((VALUE)(uintptr_t) r->st);
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
/* Allocates the state; slab_state_init fills it, then slab_run walks. */
|
|
1336
|
+
static ca_slab_iter_state_t *
|
|
1337
|
+
slab_state_new (slab_run_t *r)
|
|
1338
|
+
{
|
|
1339
|
+
ca_slab_iter_state_t *st = ZALLOC(ca_slab_iter_state_t);
|
|
1340
|
+
st->self = st->slab_view = st->carrier = st->mask_carrier = Qnil;
|
|
1341
|
+
st->output = st->output_slab_view = st->init_val = Qnil;
|
|
1342
|
+
r->st = st;
|
|
1343
|
+
r->guard = ca_window_guard_arm(slab_state_finish_abandoned, slab_state_mark,
|
|
1344
|
+
st, &r->sentinel);
|
|
1345
|
+
return st;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
static VALUE
|
|
1349
|
+
slab_run (slab_run_t *r)
|
|
1350
|
+
{
|
|
1351
|
+
VALUE result = rb_ensure(slab_run_body, (VALUE) r, slab_run_close, (VALUE) r);
|
|
1352
|
+
RB_GC_GUARD(r->guard);
|
|
1353
|
+
RB_GC_GUARD(r->sentinel);
|
|
1354
|
+
return result;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1255
1357
|
/* Shared kwarg parse: returns axis (required) + data_type / init via
|
|
1256
1358
|
out-params. init_out is set to Qundef if the `:init` key was absent
|
|
1257
1359
|
(= caller distinguishes per-slab vs per-element reduce forms). */
|
|
@@ -1299,7 +1401,7 @@ slab_extract_kwargs (int argc, VALUE *argv, const char *method_name)
|
|
|
1299
1401
|
static VALUE
|
|
1300
1402
|
rb_ca_map_slab (int argc, VALUE *argv, VALUE self)
|
|
1301
1403
|
{
|
|
1302
|
-
|
|
1404
|
+
slab_run_t r;
|
|
1303
1405
|
VALUE kw, axis, data_type, init_unused;
|
|
1304
1406
|
VALUE result;
|
|
1305
1407
|
|
|
@@ -1312,10 +1414,9 @@ rb_ca_map_slab (int argc, VALUE *argv, VALUE self)
|
|
|
1312
1414
|
rb_raise(rb_eLocalJumpError, "CArray#map_slab: block required");
|
|
1313
1415
|
}
|
|
1314
1416
|
|
|
1315
|
-
slab_state_init(&
|
|
1417
|
+
slab_state_init(slab_state_new(&r), self, CA_SLAB_FORM_MAP, axis, data_type, Qundef);
|
|
1316
1418
|
|
|
1317
|
-
result =
|
|
1318
|
-
slab_state_finish, (VALUE)(uintptr_t)&st);
|
|
1419
|
+
result = slab_run(&r);
|
|
1319
1420
|
RB_GC_GUARD(self);
|
|
1320
1421
|
return result;
|
|
1321
1422
|
}
|
|
@@ -1323,7 +1424,7 @@ rb_ca_map_slab (int argc, VALUE *argv, VALUE self)
|
|
|
1323
1424
|
static VALUE
|
|
1324
1425
|
rb_ca_reduce_slab (int argc, VALUE *argv, VALUE self)
|
|
1325
1426
|
{
|
|
1326
|
-
|
|
1427
|
+
slab_run_t r;
|
|
1327
1428
|
VALUE kw, axis, data_type, init_val;
|
|
1328
1429
|
VALUE result;
|
|
1329
1430
|
int8_t form;
|
|
@@ -1341,10 +1442,9 @@ rb_ca_reduce_slab (int argc, VALUE *argv, VALUE self)
|
|
|
1341
1442
|
form = (init_val == Qundef) ? CA_SLAB_FORM_REDUCE_SLAB
|
|
1342
1443
|
: CA_SLAB_FORM_REDUCE_FIBER;
|
|
1343
1444
|
|
|
1344
|
-
slab_state_init(&
|
|
1445
|
+
slab_state_init(slab_state_new(&r), self, form, axis, data_type, init_val);
|
|
1345
1446
|
|
|
1346
|
-
result =
|
|
1347
|
-
slab_state_finish, (VALUE)(uintptr_t)&st);
|
|
1447
|
+
result = slab_run(&r);
|
|
1348
1448
|
RB_GC_GUARD(self);
|
|
1349
1449
|
return result;
|
|
1350
1450
|
}
|
|
@@ -1352,7 +1452,7 @@ rb_ca_reduce_slab (int argc, VALUE *argv, VALUE self)
|
|
|
1352
1452
|
static VALUE
|
|
1353
1453
|
rb_ca_each_slab (int argc, VALUE *argv, VALUE self)
|
|
1354
1454
|
{
|
|
1355
|
-
|
|
1455
|
+
slab_run_t r;
|
|
1356
1456
|
VALUE kw, axis, data_type_unused, init_unused;
|
|
1357
1457
|
VALUE result;
|
|
1358
1458
|
|
|
@@ -1374,10 +1474,9 @@ rb_ca_each_slab (int argc, VALUE *argv, VALUE self)
|
|
|
1374
1474
|
2, args, RB_PASS_KEYWORDS);
|
|
1375
1475
|
}
|
|
1376
1476
|
|
|
1377
|
-
slab_state_init(&
|
|
1477
|
+
slab_state_init(slab_state_new(&r), self, CA_SLAB_FORM_EACH, axis, Qnil, Qundef);
|
|
1378
1478
|
|
|
1379
|
-
result =
|
|
1380
|
-
slab_state_finish, (VALUE)(uintptr_t)&st);
|
|
1479
|
+
result = slab_run(&r);
|
|
1381
1480
|
RB_GC_GUARD(self);
|
|
1382
1481
|
return result;
|
|
1383
1482
|
}
|
data/ext/carray_sort.c
CHANGED
|
@@ -103,16 +103,207 @@ sort_addr_merge (struct sort_addr_key *a, struct sort_addr_key *aux,
|
|
|
103
103
|
ca_size_t n, int (*cmp)(const void *, const void *))
|
|
104
104
|
CA_SORT_MERGE_BY_CMP(struct sort_addr_key)
|
|
105
105
|
|
|
106
|
+
/* -------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
Multi-key sort by refinement (MSD), for keys of the numeric data types.
|
|
109
|
+
|
|
110
|
+
The first key is sorted whole by the typed pair kernel
|
|
111
|
+
(ca_sort_quick_pair_*); then only the runs of rows that tie on it are
|
|
112
|
+
sorted by the second key, and so on, until no run is left or the keys
|
|
113
|
+
run out. The answer is the one sort_addr_cmp gives:
|
|
114
|
+
|
|
115
|
+
- a pair sorts by (value, row), so rows that tie on every key stay in
|
|
116
|
+
row order (the stable tie-break of sort_addr_cmp);
|
|
117
|
+
- each run is split into numbers, NaN and masked cells, in the order
|
|
118
|
+
sort_addr_cmp puts them (numbers, NaN, masked for masked_last;
|
|
119
|
+
masked, numbers, NaN otherwise). The NaN and the masked cells of a
|
|
120
|
+
run tie among themselves (CA_FLOAT_ORDER_CMP / sort_addr_cmp), so
|
|
121
|
+
each is a run for the next key. The split is a stable one done in
|
|
122
|
+
the load, not ca_partition_nan_pair_*, which allocates on every call
|
|
123
|
+
and is called here once per run;
|
|
124
|
+
- -0.0 and 0.0 tie, by == as by CA_FLOAT_ORDER_CMP.
|
|
125
|
+
|
|
126
|
+
A run is (lo, n): the rows perm[lo..lo+n). Within a run the rows are
|
|
127
|
+
in row order, since they tie on every key before it, so the stable
|
|
128
|
+
split keeps them so.
|
|
129
|
+
|
|
130
|
+
------------------------------------------------------------------- */
|
|
131
|
+
|
|
132
|
+
typedef struct {
|
|
133
|
+
ca_size_t lo;
|
|
134
|
+
ca_size_t n;
|
|
135
|
+
} msd_run_t;
|
|
136
|
+
|
|
137
|
+
typedef struct {
|
|
138
|
+
msd_run_t *runs;
|
|
139
|
+
ca_size_t count;
|
|
140
|
+
} msd_runs_t;
|
|
141
|
+
|
|
142
|
+
static void
|
|
143
|
+
msd_add_run (msd_runs_t *out, ca_size_t lo, ca_size_t n)
|
|
144
|
+
{
|
|
145
|
+
if ( out && n > 1 ) {
|
|
146
|
+
out->runs[out->count].lo = lo;
|
|
147
|
+
out->runs[out->count].n = n;
|
|
148
|
+
out->count++;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Sort each run of +in+ by +key+, writing the new order into perm, and
|
|
153
|
+
add the runs that still tie to +out+ (NULL for the last key). */
|
|
154
|
+
#define DEFINE_MSD_REFINE(TYPE, SUFFIX, IS_FLOAT) \
|
|
155
|
+
static void \
|
|
156
|
+
msd_refine_##SUFFIX (CArray *key, ca_size_t *perm, void *vbuf, \
|
|
157
|
+
msd_runs_t *in, msd_runs_t *out, int masked_last) \
|
|
158
|
+
{ \
|
|
159
|
+
const CA_U(TYPE) *ptr = (const CA_U(TYPE) *) key->ptr; \
|
|
160
|
+
const boolean8_t *m = ( key->mask ) ? (boolean8_t *) key->mask->ptr : NULL; \
|
|
161
|
+
ca_pair_##SUFFIX *buf = (ca_pair_##SUFFIX *) vbuf; \
|
|
162
|
+
ca_size_t r; \
|
|
163
|
+
\
|
|
164
|
+
for (r=0; r<in->count; r++) { \
|
|
165
|
+
ca_size_t lo = in->runs[r].lo; \
|
|
166
|
+
ca_size_t n = in->runs[r].n; \
|
|
167
|
+
ca_size_t *p = perm + lo; \
|
|
168
|
+
ca_size_t n_num = 0, n_nan = 0, n_mask = 0; \
|
|
169
|
+
ca_size_t o_num, o_nan, o_mask; \
|
|
170
|
+
ca_size_t k, s; \
|
|
171
|
+
\
|
|
172
|
+
for (k=0; k<n; k++) { \
|
|
173
|
+
ca_size_t row = p[k]; \
|
|
174
|
+
if ( m && m[row] ) n_mask++; \
|
|
175
|
+
else if ( IS_FLOAT && isnan((double) ptr[row]) ) n_nan++; \
|
|
176
|
+
else n_num++; \
|
|
177
|
+
} \
|
|
178
|
+
o_mask = masked_last ? n_num + n_nan : 0; \
|
|
179
|
+
o_num = masked_last ? 0 : n_mask; \
|
|
180
|
+
o_nan = o_num + n_num; \
|
|
181
|
+
{ \
|
|
182
|
+
ca_size_t i_num = o_num, i_nan = o_nan, i_mask = o_mask; \
|
|
183
|
+
for (k=0; k<n; k++) { \
|
|
184
|
+
ca_size_t row = p[k]; \
|
|
185
|
+
TYPE v = ptr[row]; \
|
|
186
|
+
ca_size_t at; \
|
|
187
|
+
if ( m && m[row] ) at = i_mask++; \
|
|
188
|
+
else if ( IS_FLOAT && isnan((double) v) ) at = i_nan++; \
|
|
189
|
+
else at = i_num++; \
|
|
190
|
+
buf[at].v = v; \
|
|
191
|
+
buf[at].i = row; \
|
|
192
|
+
} \
|
|
193
|
+
} \
|
|
194
|
+
ca_sort_quick_pair_##SUFFIX(buf + o_num, n_num); \
|
|
195
|
+
for (k=0; k<n; k++) { \
|
|
196
|
+
p[k] = buf[k].i; \
|
|
197
|
+
} \
|
|
198
|
+
if ( ! out ) continue; \
|
|
199
|
+
for (s=0, k=1; k<=n_num; k++) { \
|
|
200
|
+
if ( k == n_num || buf[o_num + k].v != buf[o_num + s].v ) { \
|
|
201
|
+
msd_add_run(out, lo + o_num + s, k - s); \
|
|
202
|
+
s = k; \
|
|
203
|
+
} \
|
|
204
|
+
} \
|
|
205
|
+
msd_add_run(out, lo + o_nan, n_nan); \
|
|
206
|
+
msd_add_run(out, lo + o_mask, n_mask); \
|
|
207
|
+
} \
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
DEFINE_MSD_REFINE(int8_t, i8, 0)
|
|
211
|
+
DEFINE_MSD_REFINE(uint8_t, u8, 0)
|
|
212
|
+
DEFINE_MSD_REFINE(int16_t, i16, 0)
|
|
213
|
+
DEFINE_MSD_REFINE(uint16_t, u16, 0)
|
|
214
|
+
DEFINE_MSD_REFINE(int32_t, i32, 0)
|
|
215
|
+
DEFINE_MSD_REFINE(uint32_t, u32, 0)
|
|
216
|
+
DEFINE_MSD_REFINE(int64_t, i64, 0)
|
|
217
|
+
DEFINE_MSD_REFINE(uint64_t, u64, 0)
|
|
218
|
+
DEFINE_MSD_REFINE(float32_t, f32, 1)
|
|
219
|
+
DEFINE_MSD_REFINE(double, f64, 1)
|
|
220
|
+
|
|
221
|
+
/* Whether every key has a typed pair kernel, so that MSD can sort them. */
|
|
222
|
+
static int
|
|
223
|
+
msd_is_sortable (struct cmp_base *base)
|
|
224
|
+
{
|
|
225
|
+
int j;
|
|
226
|
+
for (j=0; j<base->n; j++) {
|
|
227
|
+
switch ( base->ca[j]->data_type ) {
|
|
228
|
+
case CA_BOOLEAN: case CA_INT8: case CA_UINT8:
|
|
229
|
+
case CA_INT16: case CA_UINT16:
|
|
230
|
+
case CA_INT32: case CA_UINT32:
|
|
231
|
+
case CA_INT64: case CA_UINT64:
|
|
232
|
+
case CA_FLOAT32: case CA_FLOAT64:
|
|
233
|
+
break;
|
|
234
|
+
default:
|
|
235
|
+
return 0;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return 1;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
typedef struct {
|
|
242
|
+
struct cmp_base *base;
|
|
243
|
+
ca_size_t *perm; /* elements rows, the answer */
|
|
244
|
+
void *buf; /* elements pairs of 16 bytes */
|
|
245
|
+
msd_run_t *runs[2]; /* elements / 2 + 1 runs each */
|
|
246
|
+
ca_size_t elements;
|
|
247
|
+
} msd_sort_t;
|
|
248
|
+
|
|
249
|
+
static void
|
|
250
|
+
msd_sort (msd_sort_t *s)
|
|
251
|
+
{
|
|
252
|
+
struct cmp_base *base = s->base;
|
|
253
|
+
msd_runs_t a, b, *in = &a, *out = &b;
|
|
254
|
+
ca_size_t i;
|
|
255
|
+
int j;
|
|
256
|
+
|
|
257
|
+
for (i=0; i<s->elements; i++) {
|
|
258
|
+
s->perm[i] = i;
|
|
259
|
+
}
|
|
260
|
+
a.runs = s->runs[0]; a.count = 0;
|
|
261
|
+
b.runs = s->runs[1]; b.count = 0;
|
|
262
|
+
msd_add_run(in, 0, s->elements);
|
|
263
|
+
|
|
264
|
+
for (j=0; j<base->n && in->count > 0; j++) {
|
|
265
|
+
CArray *key = base->ca[j];
|
|
266
|
+
msd_runs_t *next = ( j < base->n - 1 ) ? out : NULL;
|
|
267
|
+
msd_runs_t *t;
|
|
268
|
+
if ( next ) next->count = 0;
|
|
269
|
+
switch ( key->data_type ) {
|
|
270
|
+
case CA_BOOLEAN:
|
|
271
|
+
case CA_INT8: msd_refine_i8 (key, s->perm, s->buf, in, next, base->masked_last); break;
|
|
272
|
+
case CA_UINT8: msd_refine_u8 (key, s->perm, s->buf, in, next, base->masked_last); break;
|
|
273
|
+
case CA_INT16: msd_refine_i16(key, s->perm, s->buf, in, next, base->masked_last); break;
|
|
274
|
+
case CA_UINT16: msd_refine_u16(key, s->perm, s->buf, in, next, base->masked_last); break;
|
|
275
|
+
case CA_INT32: msd_refine_i32(key, s->perm, s->buf, in, next, base->masked_last); break;
|
|
276
|
+
case CA_UINT32: msd_refine_u32(key, s->perm, s->buf, in, next, base->masked_last); break;
|
|
277
|
+
case CA_INT64: msd_refine_i64(key, s->perm, s->buf, in, next, base->masked_last); break;
|
|
278
|
+
case CA_UINT64: msd_refine_u64(key, s->perm, s->buf, in, next, base->masked_last); break;
|
|
279
|
+
case CA_FLOAT32: msd_refine_f32(key, s->perm, s->buf, in, next, base->masked_last); break;
|
|
280
|
+
case CA_FLOAT64: msd_refine_f64(key, s->perm, s->buf, in, next, base->masked_last); break;
|
|
281
|
+
default:
|
|
282
|
+
rb_raise(rb_eRuntimeError, "%s: [BUG] data type %d has no pair kernel",
|
|
283
|
+
ca_calling_method_name(), (int) key->data_type);
|
|
284
|
+
}
|
|
285
|
+
if ( ! next ) break;
|
|
286
|
+
t = in; in = out; out = t;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/* ----------------------------------------------------------------- */
|
|
291
|
+
|
|
106
292
|
typedef struct {
|
|
107
293
|
struct sort_addr_key *data;
|
|
108
294
|
struct sort_addr_key *aux; /* NULL: no object key, libc sorts */
|
|
109
295
|
ca_size_t elements;
|
|
296
|
+
msd_sort_t *msd; /* non-NULL: every key is numeric, sort by MSD */
|
|
110
297
|
} sort_addr_run_t;
|
|
111
298
|
|
|
112
299
|
static VALUE
|
|
113
300
|
sort_addr_run (VALUE arg)
|
|
114
301
|
{
|
|
115
302
|
sort_addr_run_t *r = (sort_addr_run_t *) arg;
|
|
303
|
+
if ( r->msd ) {
|
|
304
|
+
msd_sort(r->msd);
|
|
305
|
+
return Qnil;
|
|
306
|
+
}
|
|
116
307
|
if ( r->aux ) {
|
|
117
308
|
sort_addr_merge(r->data, r->aux, r->elements,
|
|
118
309
|
(int (*)(const void*,const void*)) sort_addr_cmp);
|
|
@@ -145,8 +336,9 @@ rb_ca_s_sort_addr_kw (int argc, VALUE *argv, VALUE ropt, VALUE self)
|
|
|
145
336
|
CArray *co;
|
|
146
337
|
struct cmp_base base_s, *base = &base_s;
|
|
147
338
|
struct sort_addr_key *data;
|
|
148
|
-
volatile VALUE hlist = 0, hdata = 0, haux = 0, hmodes = 0;
|
|
339
|
+
volatile VALUE hlist = 0, hdata = 0, haux = 0, hmodes = 0, hruns0 = 0, hruns1 = 0;
|
|
149
340
|
sort_addr_run_t run;
|
|
341
|
+
msd_sort_t msd;
|
|
150
342
|
char *modes;
|
|
151
343
|
int has_object = 0;
|
|
152
344
|
ca_size_t elements;
|
|
@@ -203,26 +395,42 @@ rb_ca_s_sort_addr_kw (int argc, VALUE *argv, VALUE ropt, VALUE self)
|
|
|
203
395
|
}
|
|
204
396
|
modes[argc] = '\0';
|
|
205
397
|
|
|
206
|
-
data = ALLOCV_N(struct sort_addr_key, hdata, elements);
|
|
207
|
-
for (i=0; i<elements; i++) {
|
|
208
|
-
data[i].i = i;
|
|
209
|
-
data[i].base = base;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
run.data = data;
|
|
213
|
-
run.aux = has_object ? ALLOCV_N(struct sort_addr_key, haux, elements) : NULL;
|
|
214
|
-
run.elements = elements;
|
|
215
|
-
ca_attach_window(argc, base->ca, modes, sort_addr_run, (VALUE) &run);
|
|
216
|
-
|
|
217
398
|
out = rb_ca_template_with_type(argv[0], INT2NUM(CA_SIZE), INT2NUM(0));
|
|
218
399
|
TypedData_Get_Struct(out, CArray, &carray_data_type, co);
|
|
219
400
|
q = (ca_size_t *) co->ptr;
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
401
|
+
|
|
402
|
+
run.data = NULL;
|
|
403
|
+
run.aux = NULL;
|
|
404
|
+
run.elements = elements;
|
|
405
|
+
run.msd = NULL;
|
|
406
|
+
|
|
407
|
+
if ( msd_is_sortable(base) ) {
|
|
408
|
+
ca_size_t nruns = elements / 2 + 1;
|
|
409
|
+
msd.base = base;
|
|
410
|
+
msd.perm = q;
|
|
411
|
+
msd.buf = ALLOCV(hdata, (size_t) elements * sizeof(ca_pair_f64));
|
|
412
|
+
msd.runs[0] = ALLOCV_N(msd_run_t, hruns0, nruns);
|
|
413
|
+
msd.runs[1] = ( argc > 1 ) ? ALLOCV_N(msd_run_t, hruns1, nruns) : NULL;
|
|
414
|
+
msd.elements = elements;
|
|
415
|
+
run.msd = &msd;
|
|
416
|
+
ca_attach_window(argc, base->ca, modes, sort_addr_run, (VALUE) &run);
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
data = ALLOCV_N(struct sort_addr_key, hdata, elements);
|
|
420
|
+
for (i=0; i<elements; i++) {
|
|
421
|
+
data[i].i = i;
|
|
422
|
+
data[i].base = base;
|
|
423
|
+
}
|
|
424
|
+
run.data = data;
|
|
425
|
+
run.aux = has_object ? ALLOCV_N(struct sort_addr_key, haux, elements) : NULL;
|
|
426
|
+
ca_attach_window(argc, base->ca, modes, sort_addr_run, (VALUE) &run);
|
|
427
|
+
for (i=0; i<elements; i++) {
|
|
428
|
+
q[i] = data[i].i;
|
|
429
|
+
}
|
|
224
430
|
}
|
|
225
431
|
|
|
432
|
+
ALLOCV_END(hruns1);
|
|
433
|
+
ALLOCV_END(hruns0);
|
|
226
434
|
ALLOCV_END(haux);
|
|
227
435
|
ALLOCV_END(hdata);
|
|
228
436
|
ALLOCV_END(hmodes);
|
data/ext/carray_sort_kernel.c
CHANGED
|
@@ -453,16 +453,15 @@ DEFINE_PARTITION_NAN(double, f64);
|
|
|
453
453
|
preserved), so argsort on NaN-containing input keeps NaN indices
|
|
454
454
|
in ascending order.
|
|
455
455
|
|
|
456
|
-
Implementation: 2-pass stable filter
|
|
457
|
-
|
|
458
|
-
|
|
456
|
+
Implementation: 2-pass stable filter through scratch, n pairs that
|
|
457
|
+
the caller provides. The caller takes it before it opens its walks:
|
|
458
|
+
an allocation here, inside the walk, would raise past their finish. */
|
|
459
459
|
#define DEFINE_PARTITION_NAN_PAIR(TYPE, SUFFIX) \
|
|
460
460
|
ca_size_t \
|
|
461
|
-
ca_partition_nan_pair_##SUFFIX (ca_pair_##SUFFIX *a, ca_size_t n
|
|
461
|
+
ca_partition_nan_pair_##SUFFIX (ca_pair_##SUFFIX *a, ca_size_t n, \
|
|
462
|
+
ca_pair_##SUFFIX *scratch) \
|
|
462
463
|
{ \
|
|
463
464
|
if ( n == 0 ) return 0; \
|
|
464
|
-
ca_pair_##SUFFIX *scratch = \
|
|
465
|
-
(ca_pair_##SUFFIX *) xmalloc((size_t) n * sizeof(ca_pair_##SUFFIX)); \
|
|
466
465
|
ca_size_t fin = 0, nan_pos = 0; \
|
|
467
466
|
for ( ca_size_t k = 0; k < n; k++ ) { \
|
|
468
467
|
if ( !isnan(a[k].v) ) scratch[fin++] = a[k]; \
|
|
@@ -472,7 +471,6 @@ ca_partition_nan_pair_##SUFFIX (ca_pair_##SUFFIX *a, ca_size_t n) \
|
|
|
472
471
|
if ( isnan(a[k].v) ) scratch[finite_count + nan_pos++] = a[k]; \
|
|
473
472
|
} \
|
|
474
473
|
memcpy(a, scratch, (size_t) n * sizeof(ca_pair_##SUFFIX)); \
|
|
475
|
-
xfree(scratch); \
|
|
476
474
|
return finite_count; \
|
|
477
475
|
} \
|
|
478
476
|
struct ca_partition_nan_pair_##SUFFIX##_eat_semicolon
|