Commit 95807e1b authored by Nicolas Denoyelle's avatar Nicolas Denoyelle Committed by Swann Perarnau
Browse files

[feature] add data layout support

Add new building block describing how data
is organized in memory.
parent 0e89b24a
......@@ -28,7 +28,7 @@ make:generic:
- /^wip.*/
- /^WIP.*/
variables:
CFLAGS: "-std=c99 -pedantic -Wall -Wextra -Werror -Wno-unused-but-set-parameter"
CFLAGS: "-std=c99 -pedantic -Wall -Wextra -Werror -Wno-pointer-arith -Wno-unused-but-set-parameter"
script:
- ./autogen.sh
- mkdir build
......@@ -48,7 +48,7 @@ make:out-of-tree:
- /^wip.*/
- /^WIP.*/
variables:
CFLAGS: "-std=c99 -pedantic -Wall -Wextra -Werror -Wno-unused-but-set-parameter"
CFLAGS: "-std=c99 -pedantic -Wall -Wextra -Werror -Wno-pointer-arith -Wno-unused-but-set-parameter"
script:
- ./autogen.sh
- mkdir out
......
......@@ -4,3 +4,4 @@ Brian Suchy <briansuchy2022@u.northwestern.edu>
Valentin Reis <fre@freux.fr>
Nicolas Denoyelle <ndenoyelle@anl.gov>
Clement Foyer <cfoyer@cray.com>
Brice Videau <bvideau@anl.gov>
......@@ -792,6 +792,7 @@ WARN_LOGFILE =
INPUT = ../include \
../include/aml/area \
../include/aml/layout \
../include/aml/tiling \
../include/aml/dma \
../include/aml/scratch \
......
......@@ -34,7 +34,8 @@ As of now, AML implements the following abstractions:
:align: right
* :doc:`Areas <pages/areas>`, a set of addressable physical memories,
* :doc:`Tilings <pages/tilings>`, a description of tiling data structures,
* :doc:`Layout <pages/layout>`, a description of data structures organization,
* :doc:`Tilings <pages/tilings>`, (soon to be replaced),
* :doc:`DMAs <pages/dmas>`, an engine to asynchronously move data structures between areas,
* :doc:`Scratchpads <pages/scratchs>`, a stage-in, stage-out abstraction for prefetching.
......@@ -132,5 +133,6 @@ for more info.
pages/areas
pages/tilings
pages/layout
pages/dmas
pages/scratchs
Layout: Description of Data Organization
========================================
.. doxygengroup:: aml_layout
Implementations
---------------
.. toctree::
layout_dense
layout_native
AML Dense Layouts
=================
.. doxygengroup:: aml_layout_dense
AML Layouts Native Ops
======================
.. doxygengroup:: aml_layout_native
......@@ -4,6 +4,12 @@ include_aml_areadir=$(includedir)/aml/area
include_aml_area_HEADERS = \
aml/area/linux.h
include_aml_layoutdir=$(includedir)/aml/layout
include_aml_layout_HEADERS = \
aml/layout/native.h \
aml/layout/dense.h \
aml/layout/reshape.h
include_aml_dmadir=$(includedir)/aml/dma
include_aml_dma_HEADERS = \
aml/dma/linux-seq.h \
......
......@@ -6,7 +6,7 @@
* For more info, see https://xgitlab.cels.anl.gov/argo/aml
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
******************************************************************************/
/**
* \file aml.h
......@@ -585,6 +585,365 @@ int aml_dma_cancel(struct aml_dma *dma, struct aml_dma_request *req);
////////////////////////////////////////////////////////////////////////////////
/**
* @}
* @defgroup aml_layout "AML Layout"
* @brief Low level description of data orrganization at the byte granularity.
*
* Layout describes how contiguous element of a flat memory address space are
* organized into a multidimensional array of elements of a fixed size.
* The abstraction provide functions to build layouts, access elements,
* reshape a layout, or subset a layout.
*
* Layouts are characterized by:
* * A pointer to the data it describes
* * A set of dimensions on which data spans.
* * A stride in between elements of a dimension.
* * A pitch indicating the space between contiguous elements of a dimension.
*
* The figure below describes a 2D layout with a sub-layout
* (obtained with aml_layout_slice()) operation. The sub-layout has a stride
* of 1 element along the second dimension. The slice has an offset of 1 element
* along the same dimension, and its pitch is the pitch of the original
* layout. Calling aml_layout_deref() on this sublayout with appropriate
* coordinates will return a pointer to elements noted (coor_x, coord_y).
* @see aml_layout_slice()
*
* @image html layout.png "2D layout with a 2D slice." width=400cm
*
* Access to specific elements of a layout can be done with
* the aml_layout_deref() function. Access to an element is always done
* relatively to the dimensions order set by at creation time.
* However, internally, the library will store dimensions from the last
* dimension to the first dimension such that elements along the first dimension
* are contiguous in memory. This order is defined called with the value
* AML_LAYOUT_ORDER_FORTRAN. Therefore, AML provides access to elements
* without the overhead of user order choice through function suffixed
* with "native".
* @see aml_layout_deref()
* @see aml_layout_deref_native()
* @see aml_layout_dims_native()
* @see aml_layout_slice_native()
*
* The layout abstraction also provides a function to reshape data
* with a different set of dimensions. A reshaped layout will access
* the same data but with different coordinates as pictured in the
* figure below.
* @see aml_layout_reshape()
*
* @image html reshape.png "2D layout turned into a 3D layout." width=700cm
*
* @see aml_layout_dense
* @see aml_layout_pad
* @{
**/
////////////////////////////////////////////////////////////////////////////////
struct aml_layout_ops;
struct aml_layout_data;
/** Structure definition of AML layouts **/
struct aml_layout {
/** Layout functions implementation **/
struct aml_layout_ops *ops;
/** Implementation specific data of a layout**/
struct aml_layout_data *data;
};
/** List of operators implemented by layouts. **/
struct aml_layout_ops {
/**
* Layout must provide a way to access a specific element
* according to the provided dimensions.
* Coordinates bounds checking is done in the generic API.
* Coordinates provided by the user will match the order
* Of the dimensions provided by the user in the constructor.
* However, dimensions are always stored internally in the
* AML_LAYOUT_ORDER_FORTRAN order.
* @param data[in]: The non-NULL handle to layout internal data.
* @param coords[in]: The non-NULL coordinates on which to access data.
* Coordinates are checked to be valid in aml_layout_deref().
* @return A pointer to the dereferenced element on success.
* @return NULL on failure with aml_errno set to the error reason.
**/
void *(*deref)(const struct aml_layout_data *data,
const size_t *coords);
/**
* Function for derefencing elements of a layout inside the library.
* Layout assumes data is always stored in AML_LAYOUT_ORDER_FORTRAN
* order. Coordinates provided by the library will match the same
* order, i.e last dimension first.
* @param data[in]: The non-NULL handle to layout internal data.
* @param coords[in]: The non-NULL coordinates on which to access data.
* The first coordinate should be the last dimensions and so on to the
* last, coordinate, last dimension.
* @return A pointer to the dereferenced element on success.
* @return NULL on failure with aml_errno set to the error reason.
**/
void *(*deref_native)(const struct aml_layout_data *data,
const size_t *coords);
/**
* Get the order in which dimensions of the layout are
* supposed to be accessed by the user.
* @param data[in]: The non-NULL handle to layout internal data.
* @return Order value. It is a bitmask with order bit set (or not set).
* Output value can be further checked against order AML_LAYOUT_ORDER
* flags by using the macro AML_LAYOUT_ORDER() on output value.
* @see AML_LAYOUT_ORDER()
**/
int (*order)(const struct aml_layout_data *data);
/**
* Return the layout dimensions in the user order.
* @param data[in]: The non-NULL handle to layout internal data.
* @param dims[out]: The non-NULL array of dimensions to fill. It is
* supposed to be large enough to contain ndims() elements.
* @return AML_SUCCESS on success, else an AML error code.
**/
int (*dims)(const struct aml_layout_data *data, size_t *dims);
/**
* Return the layout dimensions in the order they are actually stored
* in the library.
* @param data[in]: The non-NULL handle to layout internal data.
* @param dims[out]: The non-NULL array of dimensions to fill. It is
* supposed to be large enough to contain ndims() elements.
* @return AML_SUCCESS on success, else an AML error code.
**/
int (*dims_native)(const struct aml_layout_data *data,
size_t *dims);
/**
* Return the number of dimensions in a layout.
* @param data[in]: The non-NULL handle to layout internal data.
* @return The number of dimensions in the layout.
**/
size_t (*ndims)(const struct aml_layout_data *data);
/**
* Return the size of layout elements.
* @param data[in]: The non-NULL handle to layout internal data.
* @return The size of elements stored with this layout.
**/
size_t (*element_size)(const struct aml_layout_data *data);
/**
* Reshape the layout with different dimensions.
* Layout dimensions are checked in aml_layout_reshape() to store
* the exact same number of elements.
* @param data[in]: The non-NULL handle to layout internal data.
* @param output[out]: A non NULL pointer to a layout where to allocate
* a new layout resulting from the reshape operation.
* @param ndims[in]: The number of dimensions of the new layout.
* @param dims[in]: The number of elements along each dimension of
* the new layout.
* @return AML_SUCCESS on success, else an AML error code (<0).
**/
int (*reshape)(const struct aml_layout_data *data,
struct aml_layout **output,
const size_t ndims,
const size_t *dims);
/**
* Return a layout that is a subset of another layout.
* Slice arguments compatibility with the original layout are
* checked in aml_layout_slice().
* @param data[in]: The non-NULL handle to layout internal data.
* @param output[out]: A non NULL pointer to a layout where to allocate
* a new layout resulting from the slice operation.
* @param dims[in]: The number of elements of the slice along each
* dimension .
* @param offsets[in]: The index of the first element of the slice
* in each dimension.
* @param strides[in]: The displacement (in number of elements) between
* elements of the slice.
* @return A newly allocated layout with the queried subset of the
* original layout on succes.
* @return NULL on error with aml_errno set to the failure reason.
**/
int (*slice)(const struct aml_layout_data *data,
struct aml_layout **output,
const size_t *dims,
const size_t *offsets,
const size_t *strides);
/**
* Return a layout that is a subset of another layout, assuming
* dimensions are stored with AML_LAYOUT_ORDER_FORTRAN.
* Slice arguments compatibility with the original layout are
* checked in aml_layout_slice().
* @param data[in]: The non-NULL handle to layout internal data.
* @param output[out]: A non NULL pointer to a layout where to allocate
* a new layout resulting from the slice operation.
* @param dims[in]: The number of elements of the slice along each
* dimension .
* @param offsets[in]: The index of the first element of the slice
* in each dimension.
* @param strides[in]: The displacement (in number of elements) between
* elements of the slice.
* @return A newly allocated layout with the queried subset of the
* original layout on succes.
* @return NULL on error with aml_errno set to the failure reason.
**/
int (*slice_native)(const struct aml_layout_data *data,
struct aml_layout **output,
const size_t *dims,
const size_t *offsets,
const size_t *strides);
};
/**
* Tag specifying user storage of dimensions inside a layout.
* Layout order is the first bit in an integer bitmask.
* @see AML_LAYOUT_ORDER()
* This tag will store dimensions in the order provided by the user,
* i.e elements of the last dimension will be contiguous in memory.
**/
#define AML_LAYOUT_ORDER_C (0<<0)
/**
* Tag specifying user storage of dimensions inside a layout.
* Layout order is the first bit in an integer bitmask.
* @see AML_LAYOUT_ORDER()
* This tag will store dimensions in the reversed order provided
* by the user, i.e elements of the first dimension will be contiguous
* in memory. This storage is the actual storage used by the library
* inside the structure.
**/
#define AML_LAYOUT_ORDER_FORTRAN (1<<0)
/**
* This is equivalent to AML_LAYOUT_ORDER_C.
* @see AML_LAYOUT_ORDER_C
**/
#define AML_LAYOUT_ORDER_COLUMN_MAJOR (0<<0)
/**
* This is equivalent to AML_LAYOUT_ORDER_FORTRAN.
* @see AML_LAYOUT_ORDER_FORTRAN
**/
#define AML_LAYOUT_ORDER_ROW_MAJOR (1<<0)
/**
* Get the order bit of an integer bitmask.
* The value can be further checked for equality
* with AML_LAYOUT_ORDER_* values.
* @param x: An integer with the first bit set
* to the order value.
* @return An integer containing only the bit order.
**/
#define AML_LAYOUT_ORDER(x) ((x) & (1<<0))
/**
* Dereference an element of a layout by its coordinates.
* @param layout[in]: An initialized layout.
* @param coords[in]: The coordinates on which to access data.
* @return A pointer to the dereferenced element on success.
* @return NULL on failure with aml_errno set to the error reason:
* * AML_EINVAL if coordinate are out of bound
* * See specific implementation of layout for further information
* on possible error codes.
**/
void *aml_layout_deref(const struct aml_layout *layout,
const size_t *coords);
/**
* Equivalent to aml_layout_deref() but with bound checking
* on coordinates.
* @see aml_layout_deref()
**/
void *aml_layout_deref_safe(const struct aml_layout *layout,
const size_t *coords);
/**
* Get the order in which dimensions of the layout are supposed to be
* accessed by the user.
* @param layout[in]: An initialized layout.
* @return The order (>0) on success, an AML error (<0) on failure.
* @return On success, a bitmask with order bit set (or not set).
* Output value can be further checked against order AML_LAYOUT_ORDER
* flags by using the macro AML_LAYOUT_ORDER() on output value.
* @see AML_LAYOUT_ORDER()
**/
int aml_layout_order(const struct aml_layout *layout);
/**
* Return the layout dimensions in the user order.
* @param layout[in]: An initialized layout.
* @param dims[out]: The non-NULL array of dimensions to fill. It is
* supposed to be large enough to contain ndims() elements.
* @return AML_SUCCESS on success, else an AML error code.
**/
int aml_layout_dims(const struct aml_layout *layout, size_t *dims);
/**
* Return the number of dimensions in a layout.
* @param layout[in]: An initialized layout.
* @return The number of dimensions in the layout.
**/
size_t aml_layout_ndims(const struct aml_layout *layout);
/**
* @brief Return the size of layout elements.
* @param layout[in]: An initialized layout.
* @return The size of elements stored with this layout.
**/
size_t aml_layout_element_size(const struct aml_layout *layout);
/**
* @brief Reshape the layout with different dimensions.
* This function checks that the number of elements of
* the reshaped layout matches the number of elements
* in the original layout. Additional constraint may apply
* depending on the layout implementation.
* @param layout[in]: An initialized layout.
* @param reshaped_layout[out]: A newly allocated layout
* with the queried shape on succes.
* @param ndims[in]: The number of dimensions of the new layout.
* @param dims[in]: The number of elements along each dimension of
* the new layout.
* @return AML_SUCCESS on success.
* @return AML_EINVAL if reshape dimensions are not compatible
* with original layout dimensions.
* @return AML_ENOMEM if AML failed to allocate the new structure.
* @return Another aml_error code. Refer to the layout
* implementation of reshape function.
**/
int aml_layout_reshape(const struct aml_layout *layout,
struct aml_layout **reshaped_layout,
const size_t ndims,
const size_t *dims);
/**
* Return a layout that is a subset of another layout.
* The number of elements to subset along each dimension
* must be compatible with offsets and strides.
* This function checks that the amount of elements along
* each dimensions of the slice actually fits in the original
* layout.
* @param layout[in]: An initialized layout.
* @param reshaped_layout[out]: a pointer where to store a
* newly allocated layout with the queried subset of the
* original layout on succes.
* @param dims[in]: The number of elements of the slice along each
* dimension .
* @param offsets[in]: The index of the first element of the slice
* in each dimension. If NULL, offset is set to 0.
* @param strides[in]: The displacement (in number of elements) between
* elements of the slice. If NULL, stride is set to 1.
* @return AML_SUCCESS on success, else an AML error code (<0).
**/
int aml_layout_slice(const struct aml_layout *layout,
struct aml_layout **reshaped_layout,
const size_t *dims,
const size_t *offsets,
const size_t *strides);
////////////////////////////////////////////////////////////////////////////////
/**
* @}
* @defgroup aml_scratch "AML Scratchpad"
......
/*******************************************************************************
* Copyright 2019 UChicago Argonne, LLC.
* (c.f. AUTHORS, LICENSE)
*
* This file is part of the AML project.
* For more info, see https://xgitlab.cels.anl.gov/argo/aml
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
#ifndef AML_AREA_LAYOUT_DENSE_H
#define AML_AREA_LAYOUT_DENSE_H
/**
* @defgroup aml_layout_dense "AML Layout Dense"
* @brief Default aml layout.
*
* Dense layouts describe how a multi-dimensional dense data structure
* is collapsed into a linear (and contiguous) virtual address range.
* Dimensions of a layout may contain a stride (space between elements)
* on the virtual address space, and a pitch (distance between contiguous
* elements of the same dimension).
*
* #include <aml/layout/dense.h>
* @see aml_layout
* @{
**/
/**
* Structure of a dense layout.
**/
struct aml_layout_dense {
/**
* dimensions, in element size, of the data structure,
* by order of appearance in memory.
**/
size_t *dims;
/**
* Offset between elements of the same dimension.
* Offset in number of elements.
**/
size_t *stride;
/**
* distances between two elements of the next dimension
* (or total dimension of the layout in this dimension).
**/
size_t *pitch;
/**
* cumulative distances between two elements in the same
* dimension (pitch[0] is the element size in bytes).
**/
size_t *cpitch;
/** base pointer of the address range **/
void *ptr;
/** number of dimensions **/
size_t ndims;
};
/**
* Dense layout constructor.
* @param layout[out]: A pointer where to store a newly allocated layout.
* @param ptr[in]: The pointer to the data structure described by this layout.
* @param order[in]: The order in which dimensions are organized.
* Can be AML_LAYOUT_ORDER_COLUMN_MAJOR or AML_LAYOUT_ORDER_ROW_MAJOR.
* @param element_size[in]: The size of each element in layout.
* @param ndims[in]: The number of dimensions of the layout.
* @param dims[in]: The number of elements along each dimension of the layout.
* @param stride[in]: The space between elements (in number of elements),
* along each dimension. If NULL then the stride is set to one for each
* dimension.
* @param pitch[in]: The space between consecutive elements of the same
* dimension. If NULL, pitch is set to the number of elements in each dimension.
* @return -AML_ENOMEM if layout allocation failed.
* @return -AML_EINVAL if layout is NULL.
* @return AML_SUCCESS if creation succeeded.
* @see aml_layout_dense
**/
int aml_layout_dense_create(struct aml_layout **layout,
void *ptr,
const int order,
const size_t element_size,
const size_t ndims,
const size_t *dims,
const size_t *stride,
const size_t *pitch);
/**
* Function to free a dense layout.
* @param layout[inout]: The layout to deallocate. NULL on return.
**/
void aml_layout_dense_destroy(struct aml_layout **layout);
/**
* Deref operator for dense layout in AML_ORDER_COLUMN_MAJOR.
* Also used as the deref operator for this type of layout.
* Does not check its argument. If data is NULL, or coords are out
* of bounds, the behaviour of aml_layout_column_deref() is undefined.
* @see aml_layout_deref()
* @see aml_layout_deref_native()
**/
void *aml_layout_column_deref(const struct aml_layout_data *data,
const size_t *coords);
/**
* Layout operator for retrieving order of dimension storage.
* This function shall not fail.
* @see aml_layout_order()
**/
int aml_layout_column_order(const struct aml_layout_data *data);
/**
* Operator for retrieving the number of dimensions in the layout.
* Does not check data is not NULL. If data is NULL or not the good
* pointer type, the behaviour is undefined.
* @see aml_layout_ndims()
**/
size_t aml_layout_dense_ndims(const struct aml_layout_data *data);
/**
* Layout operator for retrieving layout elements size.
* Does not check data is not NULL. If data is NULL or not the good
* @see aml_layout_element_size()
**/
size_t aml_layout_dense_element_size(const struct aml_layout_data *data);
/**
* Operator for reshaping dense layouts with column major order.
* Does not check if the number of elements match.
* This should be done in aml_layout_reshape().
* @return -AML_EINVAL if merge then split of dimensions
* cannot be done appropriatly.
* @return -AML_ENOMEM if the resulting layout cannot be allocated.
* @return AML_SUCCESS on successful reshape.
* @see aml_layout_reshape()
**/
int aml_layout_column_reshape(const struct aml_layout_data *data,
struct aml_layout **output,
size_t ndims,
const size_t *dims);
/**
* Operator for slicing dense layouts with column major order.
* Does not check if slice elements are out of bound.
* This should be done in aml_layout_slice().
* @return -AML_ENOMEM if the resulting layout cannot be allocated.
* @return AML_SUCCESS on successful slicing.
* @see aml_layout_slice()
**/
int aml_layout_column_slice(const struct aml_layout_data *data,
struct aml_layout **output,
const size_t *offsets,
const size_t *dims,
const size_t *strides);
/**
* Operator for slicing dense layouts with column major order.
* Does not check if slice elements are out of bound.
* This should be done in aml_layout_slice().
* @return -AML_ENOMEM if the resulting layout cannot be allocated.
* @return AML_SUCCESS on successful slicing.
* @see aml_layout_deref()
**/
void *aml_layout_row_deref(const struct aml_layout_data *data,
const size_t *coords);
/**
* Operator for retrieving layout order of a row major layout.
* This function shall not fail.