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

[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 \
......
This diff is collapsed.
/*******************************************************************************
* 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.
* @see aml_layout_order()
**/
int aml_layout_row_order(const struct aml_layout_data *data);
/**
* Operator for retrieving dimensions size of a layout with row major order.
* Does not check data is not NULL. If data is NULL or not the good
* pointer type, the behaviour is undefined.
* Arguments are supposed to be checked in aml_layout_dims().
* @see aml_layout_dims()
**/
int aml_layout_row_dims(const struct aml_layout_data *data, size_t *dims);
/**
* Operator for reshaping dense layouts with row 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_row_reshape(const struct aml_layout_data *data,
struct aml_layout **output,
const size_t ndims,
const size_t *dims);
/**
* Operator for slicing dense layouts with row 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_row_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 row major order,
* without the overhead of user defined order, i.e using the internal
* library 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_row_slice_native(const struct aml_layout_data *data,
struct aml_layout **output,
const size_t *offsets,
const size_t *dims,
const size_t *strides);
/**
* Pre-existing operators for dense layout
* with AML_LAYOUT_ORDER_COLUMN_MAJOR order.
**/
extern struct aml_layout_ops aml_layout_column_ops;
/**
* Pre-existing operators for dense layout
* with AML_LAYOUT_ORDER_ROW_MAJOR order.
**/
extern struct aml_layout_ops aml_layout_row_ops;
#endif
/*************************************************************************
* 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_NATIVE_H
#define AML_AREA_LAYOUT_NATIVE_H
/**
* @defgroup aml_layout_native "AML Layout Internal API"
* @brief Layout API for internal management of layouts.
*
* #include <aml/layout/native.h>
* @{
**/
/**
* 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 layout[in]: An initialized layout.
* @param coords[in]: The 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 *aml_layout_deref_native(const struct aml_layout *layout,
const size_t *coords);
/**
* Return the layout dimensions in the order they are actually stored
* in the library.
* @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_native(const struct aml_layout *layout,
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 offsets[in]: The index of the first element of the slice
* in each dimension.
* @param dims[in]: The number of elements of the slice along each
* dimension .
* @param strides[in]: The displacement (in number of elements) between
* elements of the slice.
* @return AML_SUCCESS on success, else an AML error code (<0).
**/
int aml_layout_slice_native(const struct aml_layout *layout,
struct aml_layout **reshaped_layout,
const size_t *offsets,
const size_t *dims,
const size_t *strides);
#endif
/*******************************************************************************
* 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_RESHAPE_H
#define AML_AREA_LAYOUT_RESHAPE_H
/**
* @defgroup aml_layout_reshape "AML Layout Reshape"
* @brief Default aml layout.
*
* Layout for reshaping dense layouts when reshape method
* on dense layouts fails.
*
* #include <aml/layout/reshape.h>
* @see aml_layout
* @{
**/
/**
* Structure of reshape layout.
**/
struct aml_layout_data_reshape {
struct aml_layout *target;
size_t ndims;
size_t target_ndims;
size_t *dims;
size_t *coffsets;
size_t *target_dims;
};
int aml_layout_reshape_create(struct aml_layout **layout,
struct aml_layout *target,
const int order,
const size_t ndims,
const size_t *dims);
void aml_layout_reshape_destroy(struct aml_layout **l);
extern struct aml_layout_ops aml_layout_reshape_row_ops;
extern struct aml_layout_ops aml_layout_reshape_column_ops;
#endif
......@@ -6,6 +6,11 @@ AREA_SOURCES = \
area/area.c \
area/linux.c
LAYOUT_SOURCES = \
layout/layout.c \
layout/dense.c \
layout/reshape.c
DMA_SOURCES = \
dma/dma.c \
dma/dma_linux_par.c \
......@@ -31,6 +36,7 @@ LIB_SOURCES = \
$(DMA_SOURCES) \
$(SCRATCH_SOURCES) \
$(TILING_SOURCES) \
$(LAYOUT_SOURCES) \
$(UTILS_SOURCES) \
aml.c
......
......@@ -106,10 +106,10 @@ void *aml_area_linux_mmap(const struct aml_area_data *area_data,
return out;
}
int aml_area_linux_munmap(
__attribute__ ((unused)) const struct aml_area_data *area_data,
int aml_area_linux_munmap(const struct aml_area_data *area_data,
void *ptr, const size_t size)
{
(void)area_data;
int err = munmap(ptr, size);
if (err == -1)
......
This diff is collapsed.
/*******************************************************************************
* 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
*******************************************************************************/
#include "aml.h"
static int
aml_check_layout_coords(const struct aml_layout *layout,
int (*get_dims)(const struct aml_layout_data *,
size_t *), const size_t *coords)
{
size_t ndims = layout->ops->ndims(layout->data);
size_t dims[ndims];
int err = AML_SUCCESS;
err = get_dims(layout->data, dims);
if (err != AML_SUCCESS)
return err;
while (ndims--)
if (coords[ndims] >= dims[ndims])
return -AML_EINVAL;
return AML_SUCCESS;
}
void *aml_layout_deref(const struct aml_layout *layout, const size_t *coords)
{
assert(layout != NULL &&
layout->ops != NULL &&
layout->ops->deref != NULL);
return layout->ops->deref(layout->data, coords);
}
void *aml_layout_deref_safe(const struct aml_layout *layout,
const size_t *coords)
{
assert(layout != NULL &&
layout->ops != NULL &&
layout->ops->deref != NULL &&
layout->ops->ndims != NULL &&
layout->ops->dims != NULL);
assert(aml_check_layout_coords(layout,
layout->ops->dims,
coords) == AML_SUCCESS);
return layout->ops->deref(layout->data, coords);
}
void *aml_layout_deref_native(const struct aml_layout *layout,
const size_t *coords)
{
assert(layout != NULL &&
layout->ops != NULL &&
layout->ops->deref_native != NULL &&
layout->ops->ndims != NULL &&
layout->ops->dims_native != NULL);
return layout->ops->deref_native(layout->data, coords);
}
int aml_layout_order(const struct aml_layout *layout)
{
assert(layout != NULL &&
layout->ops != NULL &&
layout->ops->order != NULL);
return layout->ops->order(layout->data);
}
int aml_layout_dims(const struct aml_layout *layout, size_t *dims)
{
assert(layout != NULL &&
layout->ops != NULL &&
layout->ops->dims != NULL);
return layout->ops->dims(layout->data, dims);
}
int aml_layout_dims_native(const struct aml_layout *layout, size_t *dims)
{
assert(layout != NULL &&
layout->ops != NULL &&
layout->ops->dims_native != NULL);
return layout->ops->dims_native(layout->data, dims);
}
size_t aml_layout_ndims(const struct aml_layout *layout)
{
assert(layout != NULL &&
layout->ops != NULL &&
layout->ops->ndims != NULL);
return layout->ops->ndims(layout->data);
}
size_t aml_layout_element_size(const struct aml_layout *layout)
{
assert(layout != NULL &&
layout->ops != NULL &&
layout->ops->element_size != NULL);
return layout->ops->element_size(layout->data);
}
static int aml_layout_check_elements(const struct aml_layout *layout,
const size_t ndims,
const size_t *dims)
{
size_t layout_ndims;
size_t n = 0, m = 0;
assert(layout->ops->ndims != NULL &&
layout->ops->dims != NULL);
layout_ndims = layout->ops->ndims(layout->data);
size_t layout_dims[layout_ndims];
assert(layout->ops->dims(layout->data, layout_dims) == AML_SUCCESS);
for (size_t i = 0; i < ndims; i++)
n *= dims[i];
for (size_t i = 0; i < layout_ndims; i++)
m *= layout_dims[i];
if (m != n)
return -AML_EINVAL;
return AML_SUCCESS;
}
int aml_layout_reshape(const struct aml_layout *layout,
struct aml_layout **output,
const size_t ndims,
const size_t *dims)
{
assert(ndims != 0 &&
output != NULL &&
layout != NULL &&
layout->ops != NULL);
if (layout->ops->reshape == NULL)
return -AML_ENOTSUP;
assert(aml_layout_check_elements(layout, ndims, dims) == AML_SUCCESS);
struct aml_layout *result = NULL;
int err = layout->ops->reshape(layout->data, &result, ndims, dims);
if (err == AML_SUCCESS)
*output = result;
return err;
}
/**
* This function will collect the layout dimensions and check that
* the slice queried will fit into the layout.
**/
static int
aml_check_layout_slice(const struct aml_layout *layout,
int (*get_dims)(const struct aml_layout_data *,
size_t *),
const size_t *dims,
const size_t *offsets,
const size_t *strides)
{
assert(layout->ops->ndims != NULL &&
layout->ops->dims != NULL);
int err = AML_SUCCESS;
size_t ndims = layout->ops->ndims(layout->data);
size_t n_elements;
size_t layout_dims[ndims];
err = get_dims(layout->data, layout_dims);
if (err != AML_SUCCESS)
return err;
for (size_t i = 0; i < ndims; i++) {
n_elements = offsets[i] + (dims[i]-1) * strides[i];
if (n_elements > layout_dims[i])
return -AML_EINVAL;
}
return AML_SUCCESS;
}
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)
{
assert(layout != NULL &&
layout->ops != NULL);
if (layout->ops->slice == NULL)
return -AML_ENOTSUP;
size_t ndims = aml_layout_ndims(layout);
struct aml_layout *result;
int err;
size_t _offsets[ndims];
size_t _strides[ndims];
if (offsets)
memcpy(_offsets, offsets, ndims * sizeof(*offsets));
else
for (size_t i = 0; i < ndims; i++)
_offsets[i] = 0;
if (strides)
memcpy(_strides, strides, ndims * sizeof(*strides));
else
for (size_t i = 0; i < ndims; i++)
_strides[i] = 1;
assert(aml_check_layout_slice(layout,
layout->ops->dims,
dims,
_offsets,
_strides) == AML_SUCCESS);
err = layout->ops->slice(layout->data,
&result,
dims,
_offsets,