-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #95 from lf-lang/input-iterator
C support for multiport iterator for sparse inputs
- Loading branch information
Showing
10 changed files
with
370 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/** | ||
* @file | ||
* @author Edward A. Lee (eal@berkeley.edu) | ||
* | ||
* @section LICENSE | ||
* Copyright (c) 2022, The University of California at Berkeley. | ||
* | ||
* Redistribution and use in source and binary forms, with or without modification, | ||
* are permitted provided that the following conditions are met: | ||
* | ||
* 1. Redistributions of source code must retain the above copyright notice, | ||
* this list of conditions and the following disclaimer. | ||
* | ||
* 2. Redistributions in binary form must reproduce the above copyright notice, | ||
* this list of conditions and the following disclaimer in the documentation | ||
* and/or other materials provided with the distribution. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY | ||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | ||
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF | ||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
* | ||
* @section DESCRIPTION | ||
* | ||
* Header file for macros, functions, and structs for optimized sparse I/O | ||
* through multiports. | ||
* | ||
*/ | ||
#include "port.h" | ||
#include <stdio.h> | ||
|
||
/** | ||
* A vector of pointers to the size fields of instances of | ||
* lf_sparse_io_record_t so that these can be set to 0 between iterations. | ||
* The start field of this struct will be NULL initially, so calling | ||
* vector_new(_lf_sparse_io_record_sizes) will be necessary to use this. | ||
*/ | ||
struct vector_t _lf_sparse_io_record_sizes; | ||
|
||
/** | ||
* Compare two non-negative integers pointed to. Return -1 if a < b, 0 if a == b, | ||
* and 1 if a > b. | ||
* @param a Pointer to the first integer. | ||
* @param b Pointer to the second integer. | ||
*/ | ||
int compare_sizes(const void* a, const void* b) { | ||
if (*(size_t*)a < *(size_t*)b) { | ||
return -1; | ||
} else if (*(size_t*)a > *(size_t*)b) { | ||
return 1; | ||
} else { | ||
return 0; | ||
} | ||
} | ||
|
||
/** | ||
* Given an array of pointers to port structs, return an iterator | ||
* that can be used to iterate over the present channels. | ||
* @param port An array of pointers to port structs. | ||
* @param width The width of the multiport (or a negative number if not | ||
* a multiport). | ||
*/ | ||
lf_multiport_iterator_t _lf_multiport_iterator_impl(lf_port_base_t** port, int width) { | ||
// NOTE: Synchronization is not required because all writers must have | ||
// completed by the time this is invoked. | ||
struct lf_multiport_iterator_t result = (lf_multiport_iterator_t) { | ||
.next = -1, | ||
.idx = -1, // Indicate that lf_multiport_next() has not been called. | ||
.port = port, | ||
.width = width | ||
}; | ||
if (width <= 0) return result; | ||
if (port[0]->sparse_record && port[0]->sparse_record->size >= 0) { | ||
// Sparse record is enabled and ready to use. | ||
if (port[0]->sparse_record->size > 0) { | ||
// Need to sort it first (if the length is greater than 1). | ||
if (port[0]->sparse_record->size > 1) { | ||
qsort( | ||
&port[0]->sparse_record->present_channels[0], | ||
(size_t)port[0]->sparse_record->size, | ||
sizeof(size_t), | ||
&compare_sizes | ||
); | ||
} | ||
result.next = port[0]->sparse_record->present_channels[0]; | ||
} | ||
return result; | ||
} | ||
// Fallback is to iterate over all port structs representing channels. | ||
int start = 0; | ||
while(start < width) { | ||
if (port[start]->is_present) { | ||
result.next = start; | ||
return result; | ||
} | ||
start++; | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* Return the channel number of the next present input on the multiport | ||
* or -1 if there are no more present channels. | ||
* @param iterator The iterator. | ||
*/ | ||
int lf_multiport_next(lf_multiport_iterator_t* iterator) { | ||
// If the iterator has not been used, return next. | ||
if (iterator->idx < 0) { | ||
iterator->idx = 0; | ||
return iterator->next; | ||
} | ||
// If the iterator is already exhausted, return. | ||
if (iterator->next < 0 || iterator->width <= 0) { | ||
return -1; | ||
} | ||
struct lf_sparse_io_record_t* sparse_record | ||
= iterator->port[iterator->idx]->sparse_record; | ||
if (sparse_record && sparse_record->size >= 0) { | ||
// Sparse record is enabled and ready to use. | ||
iterator->idx++; | ||
if (iterator->idx >= sparse_record->size) { | ||
// No more present channels. | ||
iterator->next = -1; | ||
} else { | ||
iterator->next = sparse_record->present_channels[iterator->idx]; | ||
} | ||
return iterator->next; | ||
} else { | ||
// Fall back to iterate over all port structs representing channels. | ||
int start = iterator->next + 1; | ||
while(start < iterator->width) { | ||
if (iterator->port[start]->is_present) { | ||
iterator->next = start; | ||
return iterator->next; | ||
} | ||
start++; | ||
} | ||
// No more present channels found. | ||
iterator->next = -1; | ||
return iterator->next; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
/** | ||
* @file | ||
* @author Edward A. Lee (eal@berkeley.edu) | ||
* | ||
* @section LICENSE | ||
* Copyright (c) 2022, The University of California at Berkeley. | ||
* | ||
* Redistribution and use in source and binary forms, with or without modification, | ||
* are permitted provided that the following conditions are met: | ||
* | ||
* 1. Redistributions of source code must retain the above copyright notice, | ||
* this list of conditions and the following disclaimer. | ||
* | ||
* 2. Redistributions in binary form must reproduce the above copyright notice, | ||
* this list of conditions and the following disclaimer in the documentation | ||
* and/or other materials provided with the distribution. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY | ||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | ||
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF | ||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
* | ||
* @section DESCRIPTION | ||
* | ||
* This header file is for macros, functions, and structs for optimized sparse | ||
* input through multiports. When reading from a wide input multiport, | ||
* before this optimization, it was necessary for a reactor to test each | ||
* channel for presence each time a reaction was triggered by the multiport. | ||
* If few of the input channels are present, this can be very inefficient. | ||
* To more efficiently handle this situation, reactor authors should | ||
* annotate the input port with the "@sparse" annotation and use | ||
* lf_multiport_iterator() to read from an input multiport. For example: | ||
* | ||
* ``` | ||
* @sparse | ||
* input[100] in:int; | ||
* reaction(in) {= | ||
* struct lf_multiport_iterator_t i = lf_multiport_iterator(in); | ||
* int channel = lf_multiport_next(&i); | ||
* while(channel >= 0) { | ||
* printf("Received %d on channel %d.\n", in[channel]->value, channel); | ||
* channel = lf_multiport_next(&i); | ||
* } | ||
* =} | ||
* ``` | ||
* | ||
* The `lf_multiport_iterator()` function constructs an iterator (which is | ||
* a struct) that can be passed to the `lf_multiport_next()` function to | ||
* obtain the first channel number with a present input. Subsequent calls | ||
* to `lf_multiport_next()` return the next channel index that is present | ||
* until there are no more, at which point they return -1. | ||
* | ||
* The way this works is that for each input multiport p1 that is marked | ||
* @sparse, a struct s of type `lf_sparse_io_record_t` will | ||
* be dynamically allocated and a pointer to this struct will be put on the | ||
* self struct in a field named "portname__sparse". Each port channel struct | ||
* within the multiport will be given a pointer to s. | ||
*/ | ||
|
||
#ifndef PORT_H | ||
#define PORT_H | ||
|
||
#include <stdlib.h> | ||
#include <stdbool.h> | ||
#include "utils/vector.h" | ||
|
||
/** Threshold for width of multiport s.t. sparse reading is supported. */ | ||
#define LF_SPARSE_WIDTH_THRESHOLD 10 | ||
|
||
/** | ||
* Divide LF_SPARSE_WIDTH_THRESHOLD by this number to get the capacity of a | ||
* sparse input record for a multiport. | ||
*/ | ||
#define LF_SPARSE_CAPACITY_DIVIDER 10 | ||
|
||
/** | ||
* A record of the subset of channels of a multiport that have present inputs. | ||
*/ | ||
typedef struct lf_sparse_io_record_t { | ||
int size; // -1 if overflowed. 0 if empty. | ||
size_t capacity; // Max number of writes to be considered sparse. | ||
size_t present_channels[]; // Array of channel indices that are present. | ||
} lf_sparse_io_record_t; | ||
|
||
/** | ||
* Port structs are customized types because their payloads are type | ||
* specific. This struct represents their common features. Given any | ||
* pointer to a port struct, it can be cast to lf_port_base_t and then | ||
* these common fields can be accessed. | ||
*/ | ||
typedef struct lf_port_base_t { | ||
bool is_present; | ||
lf_sparse_io_record_t* sparse_record; // NULL if there is no sparse record. | ||
int destination_channel; // -1 if there is no destination. | ||
} lf_port_base_t; | ||
|
||
/** | ||
* An iterator over a record of the subset of channels of a multiport that | ||
* have present inputs. To use this, create an iterator using the function | ||
* lf_multiport_iterator(). That function returns a struct that can be | ||
* passed to the lf_multiport_next() function to obtain the next channel | ||
* number of a present input (or -1 if there is no next present input). | ||
*/ | ||
typedef struct lf_multiport_iterator_t { | ||
int next; | ||
int idx; // Index in the record of next or -1 if lf_multiport_next has not been called. | ||
lf_port_base_t** port; | ||
int width; | ||
} lf_multiport_iterator_t; | ||
|
||
|
||
/** | ||
* A vector of pointers to the size fields of instances of | ||
* lf_sparse_io_record_t so that these can be set to 0 between iterations. | ||
* The start field of this struct will be NULL initially, so calling | ||
* vector_new(_lf_sparse_io_record_sizes) will be necessary to use this. | ||
*/ | ||
extern struct vector_t _lf_sparse_io_record_sizes; | ||
|
||
/** | ||
* Given an array of pointers to port structs, return an iterator | ||
* that can be used to iterate over the present channels. | ||
* @param port An array of pointers to port structs. | ||
* @param width The width of the multiport (or a negative number if not | ||
* a multiport). | ||
*/ | ||
lf_multiport_iterator_t _lf_multiport_iterator_impl(lf_port_base_t** port, int width); | ||
|
||
/** | ||
* Macro for creating an iterator over an input multiport. | ||
* The argument is the port name. This returns an instance of | ||
* lf_multiport_iterator_t on the stack, a pointer to which should be | ||
* passed to lf_multiport_iterator_next() to advance. | ||
*/ | ||
#define lf_multiport_iterator(in) (_lf_multiport_iterator_impl( \ | ||
(lf_port_base_t**)self->_lf_ ## in, \ | ||
self->_lf_ ## in ## _width)) | ||
|
||
/** | ||
* Return the channel number of the next present input on the multiport | ||
* or -1 if there are no more present channels. | ||
* @param iterator The iterator. | ||
*/ | ||
int lf_multiport_next(lf_multiport_iterator_t* iterator); | ||
|
||
#endif /* PORT_H */ | ||
/** @} */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.