Skip to content
Snippets Groups Projects
Commit 1bd36647 authored by Olivier BICHLER's avatar Olivier BICHLER
Browse files

Merge branch 'view' into 'dev'

New proposal for handling tensor views

See merge request eclipse/aidge/aidge_core!69
parents 9f7c89aa deea0be0
No related branches found
No related tags found
No related merge requests found
# Version 0.1.0 (January 23, 2024)
Initial release
......@@ -67,19 +67,13 @@ private:
class TensorImpl {
public:
TensorImpl() = delete;
TensorImpl(const char *backend, DeviceIdx_t device = 0) : mBackend(backend), mDevice(device){};
TensorImpl(const char *backend, DeviceIdx_t device, NbElts_t length) : mBackend(backend), mDevice(device), mNbElts(length) {};
/**
* Return the (backend, device) pair for this implementation.
*/
std::pair<std::string, DeviceIdx_t> device() const { return std::make_pair(mBackend, mDevice); }
/**
* Set the device ID for current backend.
* @param device New device ID on current backend.
*/
virtual void setDevice(DeviceIdx_t device) = 0;
/**
* Copy data from the same device.
* @param src Pointer on current implementation device.
......@@ -93,30 +87,34 @@ public:
* @param srcDt Source data type.
* @param src Pointer on current implementation device.
* @param length Number of elements to copy.
* @param offset Destination offset (in number of elements).
*/
virtual void copyCast(const void *src, NbElts_t length, const DataType srcDt) = 0;
virtual void copyCast(const void *src, const DataType srcDt, NbElts_t length, NbElts_t offset = 0) = 0;
/**
* Copy data from an other device on the same backend.
* @param device (backend, device) pair to copy from. The backend must match current implementation backend.
* @param src Pointer on current implementation backend.
* @param length Number of elements to copy.
* @param offset Destination offset (in number of elements).
*/
virtual void copyFromDevice(const void *src, NbElts_t length, const std::pair<std::string, DeviceIdx_t>& device) = 0;
virtual void copyFromDevice(const void *src, const std::pair<std::string, DeviceIdx_t>& device, NbElts_t length, NbElts_t offset = 0) = 0;
/**
* Copy data from host.
* @param src Host pointer to copy from.
* @param length Number of elements to copy.
* @param offset Destination offset (in number of elements).
*/
virtual void copyFromHost(const void *src, NbElts_t length) = 0;
virtual void copyFromHost(const void *src, NbElts_t length, NbElts_t offset = 0) = 0;
/**
* Copy data to host.
* @param src Host pointer to copy to.
* @param length Number of elements to copy.
* @param offset Source offset (in number of elements).
*/
virtual void copyToHost(void *dst, NbElts_t length) const = 0;
virtual void copyToHost(void *dst, NbElts_t length, NbElts_t offset = 0) const = 0;
/**
* Return the raw device pointer.
......@@ -146,8 +144,22 @@ public:
AIDGE_THROW_OR_ABORT(std::runtime_error, "Cannot set raw pointer for backend %s", mBackend);
};
virtual std::size_t size() const = 0; // Storage size
virtual std::size_t scalarSize() const = 0; // Size of one scalar (in bytes)
/**
* Set the size, in number of elements, that must be stored.
*/
void resize(NbElts_t length) {
mNbElts = length;
}
/**
* Return the number of elements stored.
*/
inline std::size_t size() const noexcept { return mNbElts; }
/**
* Return the size (in bytes) of one element (scalar).
*/
virtual std::size_t scalarSize() const noexcept = 0;
constexpr const char *backend() const { return mBackend; }
virtual ~TensorImpl() = default;
virtual bool operator==(const TensorImpl &othImpl) const = 0;
......@@ -156,12 +168,16 @@ public:
* Copy from another backend.
* @param srcImpl Source TensorImpl to copy from.
* @param length Number of elements of size scalarSize() to copy
* @param srcOffset Source offset (in number of elements).
* @param dstOffset Destination offset (in number of elements).
*/
void copyFrom(const TensorImpl& srcImpl, NbElts_t length);
void copyFrom(const TensorImpl& srcImpl, NbElts_t length, NbElts_t srcOffset = 0, NbElts_t dstOffset = 0);
protected:
const char *mBackend;
DeviceIdx_t mDevice;
const DeviceIdx_t mDevice;
/// Number of elements (to be) stored
NbElts_t mNbElts;
};
} // namespace Aidge
......
This diff is collapsed.
......@@ -30,7 +30,7 @@ void addCtor(py::class_<Tensor,
Data,
Registrable<Tensor,
std::tuple<std::string, DataType>,
std::unique_ptr<TensorImpl>(const Tensor&)>>& mTensor){
std::shared_ptr<TensorImpl>(DeviceIdx_t device, NbElts_t length)>>& mTensor){
mTensor.def(py::init([]( py::array_t<T, py::array::c_style | py::array::forcecast> b) {
/* Request a buffer descriptor from Python */
py::buffer_info info = b.request();
......@@ -58,16 +58,16 @@ void addCtor(py::class_<Tensor,
void init_Tensor(py::module& m){
py::class_<Registrable<Tensor,
std::tuple<std::string, DataType>,
std::unique_ptr<TensorImpl>(const Tensor&)>,
std::shared_ptr<TensorImpl>(DeviceIdx_t device, NbElts_t length)>,
std::shared_ptr<Registrable<Tensor,
std::tuple<std::string, DataType>,
std::unique_ptr<TensorImpl>(const Tensor&)>>>(m,"TensorRegistrable");
std::shared_ptr<TensorImpl>(DeviceIdx_t device, NbElts_t length)>>>(m,"TensorRegistrable");
py::class_<Tensor, std::shared_ptr<Tensor>,
Data,
Registrable<Tensor,
std::tuple<std::string, DataType>,
std::unique_ptr<TensorImpl>(const Tensor&)>> pyClassTensor
std::shared_ptr<TensorImpl>(DeviceIdx_t device, NbElts_t length)>> pyClassTensor
(m,"Tensor", py::multiple_inheritance(), py::buffer_protocol());
pyClassTensor.def(py::init<>())
......@@ -76,7 +76,7 @@ void init_Tensor(py::module& m){
.def("dims", (const std::vector<DimSize_t>& (Tensor::*)()const) &Tensor::dims)
.def("dtype", &Tensor::dataType)
.def("size", &Tensor::size)
.def("resize", (void (Tensor::*)(const std::vector<DimSize_t>&)) &Tensor::resize)
.def("resize", (void (Tensor::*)(const std::vector<DimSize_t>&, std::vector<DimSize_t>)) &Tensor::resize)
.def("has_impl", &Tensor::hasImpl)
.def("get_coord", &Tensor::getCoord)
.def("get_idx", &Tensor::getIdx)
......@@ -118,7 +118,7 @@ void init_Tensor(py::module& m){
}
})
.def_buffer([](Tensor& b) -> py::buffer_info {
const std::unique_ptr<TensorImpl>& tensorImpl = b.getImpl();
const std::shared_ptr<TensorImpl>& tensorImpl = b.getImpl();
std::vector<size_t> dims;
std::vector<size_t> strides;
......
......@@ -14,23 +14,23 @@
#include "aidge/utils/Types.h"
#include "aidge/utils/ErrorHandling.hpp"
void Aidge::TensorImpl::copyFrom(const TensorImpl& srcImpl, NbElts_t length) {
if (&srcImpl == this) {
void Aidge::TensorImpl::copyFrom(const TensorImpl& srcImpl, NbElts_t length, NbElts_t srcOffset, NbElts_t dstOffset) {
if (&srcImpl == this && srcOffset == dstOffset) {
return;
}
if (srcImpl.device() != device()) {
if (srcImpl.backend() == backend()) {
// Same backend, but different device
copyFromDevice(srcImpl.rawPtr(), length, srcImpl.device());
copyFromDevice(srcImpl.rawPtr(srcOffset), srcImpl.device(), length, dstOffset);
}
else if (srcImpl.hostPtr() != nullptr) {
// Different backend, but input is valid on host
copyFromHost(srcImpl.hostPtr(), length);
copyFromHost(srcImpl.hostPtr(srcOffset), length, dstOffset);
}
else if (hostPtr() != nullptr) {
// Different backend, but dst is valid on host
srcImpl.copyToHost(hostPtr(), length);
srcImpl.copyToHost(hostPtr(srcOffset), length, dstOffset);
}
else {
// No direct link possible from src to dst device
......@@ -40,12 +40,12 @@ void Aidge::TensorImpl::copyFrom(const TensorImpl& srcImpl, NbElts_t length) {
// - There is currently no concrete use case
// - Just providing a pointer would be unsafe (risk of buffer overflow...)
auto tmpHostBuffer = std::unique_ptr<char[]>(new char[scalarSize() * length]);
srcImpl.copyToHost(tmpHostBuffer.get(), length);
copyFromHost(tmpHostBuffer.get(), length);
srcImpl.copyToHost(tmpHostBuffer.get(), length, srcOffset);
copyFromHost(tmpHostBuffer.get(), length, dstOffset);
}
}
else {
// Same device: simple copy on device
copy(srcImpl.rawPtr(), length);
copy(srcImpl.rawPtr(srcOffset), length, dstOffset);
}
}
......@@ -13,11 +13,72 @@
#include "aidge/utils/Types.h"
#include "aidge/utils/ErrorHandling.hpp"
Aidge::Tensor Aidge::Tensor::extract(const std::vector<std::size_t>& coordIdx) const {
AIDGE_ASSERT(isContiguous(), "Tensor must be contiguous");
AIDGE_ASSERT(coordIdx.size() <= mDims.size(), "Number of coordinates is higher than number of dimensions");
Tensor subTensor(mDataType);
subTensor.resize(std::vector<size_t>(mDims.begin() + coordIdx.size(), mDims.end()),
std::vector<size_t>(mStrides.begin() + coordIdx.size(), mStrides.end()));
subTensor.setBackend(mImpl->backend(), mImpl->device().second);
subTensor.setImpl(mImpl, mImplOffset + getStorageIdx(coordIdx));
return subTensor;
}
Aidge::Tensor Aidge::Tensor::extract(const std::vector<std::size_t>& coordIdx, const std::vector<std::size_t>& dims) const {
AIDGE_ASSERT(isContiguous(), "Tensor must be contiguous");
AIDGE_ASSERT(coordIdx.size() == mDims.size(), "Coordinates does not match number of dimensions");
Tensor subTensor(mDataType);
subTensor.resize(dims, mStrides);
subTensor.setBackend(mImpl->backend(), mImpl->device().second);
subTensor.setImpl(mImpl, mImplOffset + getStorageIdx(coordIdx));
return subTensor;
}
void Aidge::Tensor::makeContiguous() {
if (!mImpl || isContiguous()) {
return;
}
// Block so that mImpl ref count is 1 for resize()
{
// Create a new storage that will be contiguous
std::shared_ptr<TensorImpl> newImpl = Registrar<Tensor>::create({mImpl->backend(), mDataType})(mImpl->device().second, mSize);
// Copy elements from old to new storage
size_t idx = 0;
while (idx < mSize) {
const size_t storageIdx = getStorageIdx(getCoord(idx));
// Determine the size of the contiguous chunk
size_t copySize = 1;
while (idx + copySize < mSize &&
getStorageIdx(getCoord(idx + copySize)) == storageIdx + copySize)
{
++copySize;
}
// Perform a single copy for the contiguous chunk
newImpl->copy(mImpl->rawPtr(mImplOffset + storageIdx), copySize, idx);
// Move to the next index after the contiguous chunk
idx += copySize;
}
// Replace old storage by new, contiguous, storage
setImpl(newImpl);
}
// Resize tensor without strides => tensor is now contiguous
resize(mDims);
}
void Aidge::Tensor::copyCast(const Tensor& src) {
if (&src == this) {
return;
}
AIDGE_ASSERT(src.isContiguous(), "cannot copy-cast non-contiguous tensor");
// Current Tensor has necessarily a data type, but may not have backend
if (!getImpl()) {
// If no backend was set for the current tensor, use the same as src
......@@ -27,7 +88,7 @@ void Aidge::Tensor::copyCast(const Tensor& src) {
resize(src.dims());
AIDGE_ASSERT(src.getImpl()->device() == getImpl()->device(), "cannot copy-cast from a different backend/device");
getImpl()->copyCast(src.getImpl()->rawPtr(), src.size(), src.dataType());
getImpl()->copyCast(src.getImpl()->rawPtr(src.mImplOffset), src.dataType(), src.size(), mImplOffset);
}
void Aidge::Tensor::copyFrom(const Tensor& src) {
......@@ -35,6 +96,8 @@ void Aidge::Tensor::copyFrom(const Tensor& src) {
return;
}
AIDGE_ASSERT(src.isContiguous(), "cannot copy from non-contiguous tensor");
// Current Tensor has necessarily a data type, but may not have backend
if (!getImpl()) {
// If no backend was set for the current tensor, use the same as src
......@@ -44,7 +107,7 @@ void Aidge::Tensor::copyFrom(const Tensor& src) {
resize(src.dims());
AIDGE_ASSERT(src.dataType() == dataType(), "cannot copy from a different data type");
getImpl()->copyFrom(*(src.getImpl()), src.size());
getImpl()->copyFrom(*(src.getImpl()), src.size(), src.mImplOffset, mImplOffset);
}
void Aidge::Tensor::copyCastFrom(const Tensor& src, std::shared_ptr<Tensor>& movedSrcPtr) {
......@@ -52,6 +115,8 @@ void Aidge::Tensor::copyCastFrom(const Tensor& src, std::shared_ptr<Tensor>& mov
return;
}
AIDGE_ASSERT(src.isContiguous(), "cannot copy-cast from non-contiguous tensor");
// Current Tensor has necessarily a data type, but may not have backend
if (!getImpl()) {
// If no backend was set for the current tensor, use the same as src
......@@ -65,12 +130,35 @@ void Aidge::Tensor::copyCastFrom(const Tensor& src, std::shared_ptr<Tensor>& mov
const auto device = getImpl()->device();
const Tensor& movedSrc = src.refFrom(movedSrcPtr, device.first, device.second);
// Second, copy-cast data (necessary)
getImpl()->copyCast(movedSrc.getImpl()->rawPtr(), movedSrc.size(), movedSrc.dataType());
getImpl()->copyCast(movedSrc.getImpl()->rawPtr(movedSrc.mImplOffset), movedSrc.dataType(), movedSrc.size(), mImplOffset);
}
else {
// Directly copy, no conversion necessary
// Avoid making a double copy if both data type and device are the same
getImpl()->copyFrom(*(src.getImpl()), src.size());
getImpl()->copyFrom(*(src.getImpl()), src.size(), src.mImplOffset, mImplOffset);
}
}
Aidge::Tensor& Aidge::Tensor::refContiguous(std::shared_ptr<Tensor>& fallback) {
// Scott Meyers' solution to avoid code duplication
return const_cast<Tensor&>(static_cast<const Tensor&>(*this).refContiguous(fallback));
}
const Aidge::Tensor& Aidge::Tensor::refContiguous(std::shared_ptr<Tensor>& fallback) const {
AIDGE_ASSERT(getImpl(), "no backend was set for tensor, cannot refCast() it");
if (isContiguous()) {
return *this;
}
else {
if (this != fallback.get()) {
// Shallow copy to fallback
*fallback = *this;
}
// Make fallback contiguous
fallback->makeContiguous();
return *fallback;
}
}
......@@ -91,6 +179,8 @@ const Aidge::Tensor& Aidge::Tensor::refCast(std::shared_ptr<Tensor>& fallback, c
fallback->setDataType(dt);
}
else {
AIDGE_ASSERT(isContiguous(), "cannot refCast non-contiguous tensor");
if (!fallback) {
fallback = std::make_shared<Tensor>(dt);
}
......@@ -101,7 +191,7 @@ const Aidge::Tensor& Aidge::Tensor::refCast(std::shared_ptr<Tensor>& fallback, c
const auto device = getImpl()->device();
fallback->setBackend(device.first, device.second, false); // don't keep previous data (no copy)
fallback->resize(dims());
fallback->getImpl()->copyCast(getImpl()->rawPtr(), size(), dataType());
fallback->getImpl()->copyCast(getImpl()->rawPtr(mImplOffset), dataType(), size(), fallback->mImplOffset);
}
return *fallback;
}
......@@ -124,6 +214,8 @@ const Aidge::Tensor& Aidge::Tensor::refFrom(std::shared_ptr<Tensor>& fallback, c
fallback->setBackend(backend, device);
}
else {
AIDGE_ASSERT(isContiguous(), "cannot refFrom non-contiguous tensor");
if (!fallback) {
fallback = std::make_shared<Tensor>(dataType());
}
......@@ -133,8 +225,34 @@ const Aidge::Tensor& Aidge::Tensor::refFrom(std::shared_ptr<Tensor>& fallback, c
fallback->setBackend(backend, device, false); // don't keep previous data (no copy)
fallback->resize(dims());
fallback->getImpl()->copyFrom(*getImpl(), size());
fallback->getImpl()->copyFrom(*getImpl(), size(), mImplOffset, fallback->mImplOffset);
}
return *fallback;
}
}
Aidge::Tensor& Aidge::Tensor::ref(std::shared_ptr<Tensor>& fallback, const Aidge::DataType& dt, const std::string &backend, DeviceIdx_t device) {
// Scott Meyers' solution to avoid code duplication
return const_cast<Tensor&>(static_cast<const Tensor&>(*this).ref(fallback, dt, backend, device));
}
const Aidge::Tensor& Aidge::Tensor::ref(std::shared_ptr<Tensor>& fallback, const Aidge::DataType& dt, const std::string &backend, DeviceIdx_t device) const {
AIDGE_ASSERT(getImpl(), "no backend was set for tensor, cannot ref() it");
if (dt == dataType() && std::make_pair(backend, device) == getImpl()->device()) {
return *this;
}
else {
// Change fallback type, backend & device, without any data copy
if (!fallback) {
fallback = std::make_shared<Tensor>(dt);
}
else {
fallback->setDataType(dt, false); // don't keep previous data (no copy)
}
fallback->setBackend(backend, device, false); // don't keep previous data (no copy)
fallback->resize(dims());
return *fallback;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment