Add sort channel module
Sorts the N input values and writes them in the N outputs in order.
This commit is contained in:
parent
a4ce0e2a1e
commit
70b29b6459
@ -29,6 +29,7 @@ add_library(emu STATIC
|
||||
loom.c
|
||||
metadata.c
|
||||
mux.c
|
||||
sort.c
|
||||
path.c
|
||||
proc.c
|
||||
pv/pcf.c
|
||||
|
134
src/emu/sort.c
Normal file
134
src/emu/sort.c
Normal file
@ -0,0 +1,134 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
//#define ENABLE_DEBUG
|
||||
|
||||
#include "sort.h"
|
||||
|
||||
static int
|
||||
cmp_int64(const void *a, const void *b)
|
||||
{
|
||||
int64_t aa = *(const int64_t *) a;
|
||||
int64_t bb = *(const int64_t *) b;
|
||||
|
||||
if (aa < bb)
|
||||
return -1;
|
||||
else if (aa > bb)
|
||||
return +1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Called when an input channel changes its value */
|
||||
static int
|
||||
sort_cb_input(struct chan *in_chan, void *ptr)
|
||||
{
|
||||
struct sort_input *input = ptr;
|
||||
struct sort *sort = input->sort;
|
||||
struct value cur;
|
||||
|
||||
if (chan_read(in_chan, &cur) != 0) {
|
||||
err("chan_read() failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t vcur = 0;
|
||||
if (cur.type == VALUE_INT64)
|
||||
vcur = cur.i;
|
||||
|
||||
int64_t index = input->index;
|
||||
int64_t last = sort->values[index];
|
||||
|
||||
/* Nothing to do if no change */
|
||||
if (last == vcur)
|
||||
return 0;
|
||||
|
||||
dbg("sort input %s changed", in_chan->name);
|
||||
|
||||
/* Otherwise recompute the outputs */
|
||||
sort->values[index] = vcur;
|
||||
memcpy(sort->sorted, sort->values, sort->n * sizeof(int64_t));
|
||||
qsort(sort->sorted, sort->n, sizeof(int64_t), cmp_int64);
|
||||
|
||||
for (int64_t i = 0; i < sort->n; i++) {
|
||||
struct value val = value_int64(sort->sorted[i]);
|
||||
if (chan_set(sort->outputs[i], val) != 0) {
|
||||
err("chan_set failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sort_init(struct sort *sort, struct bay *bay, int64_t n)
|
||||
{
|
||||
memset(sort, 0, sizeof(struct sort));
|
||||
sort->bay = bay;
|
||||
sort->n = n;
|
||||
sort->inputs = calloc(n, sizeof(struct sort_input));
|
||||
if (sort->inputs == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
sort->outputs = calloc(n, sizeof(struct chan *));
|
||||
if (sort->outputs == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
sort->values = calloc(n, sizeof(int64_t));
|
||||
if (sort->values == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
sort->sorted = calloc(n, sizeof(int64_t));
|
||||
if (sort->sorted == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sort_set_input(struct sort *sort, int64_t index, struct chan *chan)
|
||||
{
|
||||
struct sort_input *input = &sort->inputs[index];
|
||||
|
||||
if (input->chan != NULL) {
|
||||
err("input %d already has a channel", index);
|
||||
return -1;
|
||||
}
|
||||
|
||||
input->chan = chan;
|
||||
input->index = index;
|
||||
input->sort = sort;
|
||||
|
||||
if (bay_add_cb(sort->bay, BAY_CB_DIRTY, chan, sort_cb_input, input, 1) == NULL) {
|
||||
err("bay_add_cb failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sort_set_output(struct sort *sort, int64_t index, struct chan *chan)
|
||||
{
|
||||
if (sort->outputs[index] != NULL) {
|
||||
err("output %d already has a channel", index);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sort->outputs[index] = chan;
|
||||
|
||||
/* The sort module may write multiple times to the same channel if we
|
||||
* update more than one input. */
|
||||
chan_prop_set(chan, CHAN_DIRTY_WRITE, 1);
|
||||
|
||||
/* No duplicate checks are done by sort module, so we simply allow them */
|
||||
chan_prop_set(chan, CHAN_DUPLICATES, 1);
|
||||
|
||||
return 0;
|
||||
}
|
32
src/emu/sort.h
Normal file
32
src/emu/sort.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef SORT_H
|
||||
#define SORT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "bay.h"
|
||||
|
||||
struct sort;
|
||||
|
||||
struct sort_input {
|
||||
int64_t index;
|
||||
struct chan *chan;
|
||||
struct sort *sort;
|
||||
};
|
||||
|
||||
struct sort {
|
||||
int64_t n;
|
||||
struct sort_input *inputs;
|
||||
struct chan **outputs;
|
||||
int64_t *values;
|
||||
int64_t *sorted;
|
||||
struct bay *bay;
|
||||
};
|
||||
|
||||
USE_RET int sort_init(struct sort *sort, struct bay *bay, int64_t n);
|
||||
USE_RET int sort_set_input(struct sort *sort, int64_t index, struct chan *input);
|
||||
USE_RET int sort_set_output(struct sort *sort, int64_t index, struct chan *output);
|
||||
USE_RET int sort_register(struct sort *sort, struct bay *bay);
|
||||
|
||||
#endif /* SORT_H */
|
@ -15,3 +15,4 @@ unit_test(thread.c)
|
||||
unit_test(value.c)
|
||||
unit_test(version.c)
|
||||
unit_test(path.c)
|
||||
unit_test(sort.c)
|
||||
|
86
test/unit/sort.c
Normal file
86
test/unit/sort.c
Normal file
@ -0,0 +1,86 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "emu/sort.h"
|
||||
#include "common.h"
|
||||
#include "unittest.h"
|
||||
|
||||
#define N 10
|
||||
|
||||
static void
|
||||
check_output(struct chan *chan, struct value expected)
|
||||
{
|
||||
struct value out_value = value_null();
|
||||
if (chan_read(chan, &out_value) != 0)
|
||||
die("chan_read() failed for channel %s", chan->name);
|
||||
|
||||
char buf1[128];
|
||||
if (!value_is_equal(&out_value, &expected)) {
|
||||
char buf2[128];
|
||||
die("unexpected value found %s in output (expected %s)\n",
|
||||
value_str(out_value, buf1),
|
||||
value_str(expected, buf2));
|
||||
}
|
||||
|
||||
err("output ok: chan=%s val=%s", chan->name, value_str(out_value, buf1));
|
||||
}
|
||||
|
||||
static void
|
||||
test_sort(void)
|
||||
{
|
||||
struct bay bay;
|
||||
bay_init(&bay);
|
||||
|
||||
struct chan inputs[N];
|
||||
struct chan outputs[N];
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
chan_init(&inputs[i], CHAN_SINGLE, "input.%d", i);
|
||||
chan_init(&outputs[i], CHAN_SINGLE, "output.%d", i);
|
||||
}
|
||||
|
||||
/* Register all channels in the bay */
|
||||
for (int i = 0; i < N; i++) {
|
||||
OK(bay_register(&bay, &inputs[i]));
|
||||
OK(bay_register(&bay, &outputs[i]));
|
||||
}
|
||||
|
||||
/* Setup channel values */
|
||||
for (int i = 0; i < N; i++) {
|
||||
OK(chan_set(&inputs[i], value_int64(0)));
|
||||
}
|
||||
|
||||
OK(bay_propagate(&bay));
|
||||
|
||||
struct sort sort;
|
||||
OK(sort_init(&sort, &bay, N));
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
OK(sort_set_input(&sort, i, &inputs[i]));
|
||||
OK(sort_set_output(&sort, i, &outputs[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
OK(chan_set(&inputs[i], value_int64(1)));
|
||||
}
|
||||
|
||||
OK(bay_propagate(&bay));
|
||||
|
||||
/* Check the outputs */
|
||||
for (int i = 0; i < N - 2; i++) {
|
||||
check_output(&outputs[i], value_int64(0));
|
||||
}
|
||||
for (int i = N - 2; i < N; i++) {
|
||||
check_output(&outputs[i], value_int64(1));
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
test_sort();
|
||||
|
||||
err("OK\n");
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user