Commit 6556c6b1 authored by Jens Petit's avatar Jens Petit

DataHandlerGPU: Assignment operation of DataContainer (#21)

parent 57aadf80
......@@ -47,13 +47,12 @@ namespace elsa
if (this != &other) {
_dataDescriptor = other._dataDescriptor->clone();
if (_dataHandler) {
if (_dataHandler && canAssign(other._dataHandlerType)) {
*_dataHandler = *other._dataHandler;
} else {
_dataHandler = other._dataHandler->clone();
_dataHandlerType = other._dataHandlerType;
}
// TODO: Check what to do with handler type if CPU copy assign to GPU type
}
return *this;
......@@ -75,14 +74,13 @@ namespace elsa
{
_dataDescriptor = std::move(other._dataDescriptor);
if (_dataHandler) {
if (_dataHandler && canAssign(other._dataHandlerType)) {
*_dataHandler = std::move(*other._dataHandler);
} else {
_dataHandler = std::move(other._dataHandler);
_dataHandlerType = std::move(other._dataHandlerType);
}
// TODO: Check what to do with handler type if CPU move assign to GPU type
// leave other in a valid state
other._dataDescriptor = nullptr;
other._dataHandler = nullptr;
......@@ -428,6 +426,71 @@ namespace elsa
return _dataHandlerType;
}
template <typename data_t>
DataContainer<data_t> DataContainer<data_t>::loadToCPU()
{
if (_dataHandlerType == DataHandlerType::CPU
|| _dataHandlerType == DataHandlerType::MAP_CPU) {
throw std::logic_error(
"DataContainer: cannot load data to CPU with already CPU based container");
}
DataContainer<data_t> dcCPU(*_dataDescriptor, DataHandlerType::CPU);
for (index_t i = 0; i < getSize(); i++) {
dcCPU[i] = this->operator[](i);
}
return dcCPU;
}
template <typename data_t>
DataContainer<data_t> DataContainer<data_t>::loadToGPU()
{
if (_dataHandlerType == DataHandlerType::GPU
|| _dataHandlerType == DataHandlerType::MAP_GPU) {
throw std::logic_error(
"DataContainer: cannot load data to GPU with already GPU based container");
}
DataContainer<data_t> dcGPU(*_dataDescriptor, DataHandlerType::GPU);
for (index_t i = 0; i < getSize(); i++) {
dcGPU[i] = this->operator[](i);
}
return dcGPU;
}
template <typename data_t>
bool DataContainer<data_t>::canAssign(DataHandlerType handlerType)
{
if (_dataHandlerType == DataHandlerType::CPU
|| _dataHandlerType == DataHandlerType::MAP_CPU) {
switch (handlerType) {
case DataHandlerType::CPU:
return true;
break;
case DataHandlerType::MAP_CPU:
return true;
break;
default:
return false;
}
} else {
switch (handlerType) {
case DataHandlerType::GPU:
return true;
break;
case DataHandlerType::MAP_GPU:
return true;
break;
default:
return false;
}
}
}
// ------------------------------------------
// explicit template instantiation
template class DataContainer<float>;
......
......@@ -72,6 +72,10 @@ namespace elsa
* \brief copy assignment for DataContainer
*
* \param[in] other DataContainer to copy
*
* Note that a copy assignment with a DataContainer on a different device (CPU vs GPU) will
* result in an "infectious" copy which means that afterwards the current container will use
* the same device as "other".
*/
DataContainer<data_t>& operator=(const DataContainer<data_t>& other);
......@@ -94,6 +98,10 @@ namespace elsa
* The moved-from objects remains in a valid state. However, as preconditions are not
* fulfilled for any member functions, the object should not be used. After move- or copy-
* assignment, this is possible again.
*
* Note that a copy assignment with a DataContainer on a different device (CPU vs GPU) will
* result in an "infectious" copy which means that afterwards the current container will use
* the same device as "other".
*/
DataContainer<data_t>& operator=(DataContainer<data_t>&& other);
......@@ -371,6 +379,26 @@ namespace elsa
template <bool GPU, class Operand, std::enable_if_t<isDataContainer<Operand>, int>>
friend constexpr auto evaluateOrReturn(Operand const& operand);
/**
* \brief Factory function which returns GPU based DataContainers
*
* \return the GPU based DataContainer
*
* Note that if this function is called on a container which is already GPU based, it will
* throw an exception.
*/
DataContainer loadToGPU();
/**
* \brief Factory function which returns CPU based DataContainers
*
* \return the CPU based DataContainer
*
* Note that if this function is called on a container which is already CPU based, it will
* throw an exception.
*/
DataContainer loadToCPU();
private:
/// the current DataDescriptor
std::unique_ptr<DataDescriptor> _dataDescriptor;
......@@ -391,6 +419,20 @@ namespace elsa
explicit DataContainer(const DataDescriptor& dataDescriptor,
std::unique_ptr<DataHandler<data_t>> dataHandler,
DataHandlerType dataType = defaultHandlerType);
/**
* \brief Helper function to indicate if a regular assignment or a clone should be performed
*
* \param[in] handlerType the member variable of the other container in
* copy-/move-assignment
*
* \return true if a regular assignment of the pointed to DataHandlers should be done
*
* An assignment operation with a DataContainer which does not use the same device (CPU /
* GPU) has to be handled differently. This helper function indicates if a regular
* assignment should be performed or not.
*/
bool canAssign(DataHandlerType handlerType);
};
/// User-defined template argument deduction guide for the expression based constructor
......
......@@ -587,6 +587,92 @@ TEMPLATE_PRODUCT_TEST_CASE("Scenario: Testing creation of Maps through DataConta
}
}
#ifdef ELSA_CUDA_VECTOR
TEMPLATE_TEST_CASE("Scenario: Testing loading data to GPU and vice versa.", "", float, double,
std::complex<float>, std::complex<double>, index_t)
{
GIVEN("A CPU DataContainer with random data")
{
IndexVector_t numCoeff(3);
numCoeff << 52, 7, 29;
DataDescriptor desc(numCoeff);
DataContainer<TestType> dcCPU(desc, DataHandlerType::CPU);
DataContainer<TestType> dcGPU(desc, DataHandlerType::GPU);
Eigen::Matrix<TestType, Eigen::Dynamic, 1> randVec{dcCPU.getSize()};
randVec.setRandom();
for (index_t i = 0; i < dcCPU.getSize(); ++i) {
dcCPU[i] = randVec(i);
dcGPU[i] = randVec(i);
}
WHEN("Trying to call loadToCPU on CPU container")
{
THEN("Throws") { REQUIRE_THROWS(dcCPU.loadToCPU()); }
}
WHEN("Trying to call loadToGPU on GPU container")
{
THEN("Throws") { REQUIRE_THROWS(dcGPU.loadToGPU()); }
}
WHEN("Loading to GPU from CPU")
{
DataContainer dcGPU2 = dcCPU.loadToGPU();
REQUIRE(dcGPU2.getDataHandlerType() == DataHandlerType::GPU);
THEN("all elements have to be the same")
{
for (index_t i = 0; i < dcCPU.getSize(); ++i) {
REQUIRE(dcGPU2[i] == dcGPU[i]);
}
}
}
WHEN("Loading to CPU from GPU")
{
DataContainer dcCPU2 = dcGPU.loadToCPU();
REQUIRE(dcCPU2.getDataHandlerType() == DataHandlerType::CPU);
THEN("all elements have to be the same")
{
for (index_t i = 0; i < dcCPU.getSize(); ++i) {
REQUIRE(dcCPU2[i] == dcGPU[i]);
}
}
}
WHEN("copy-assigning a GPU to a CPU container")
{
dcCPU = dcGPU;
THEN("it should be a GPU container")
{
REQUIRE(dcCPU.getDataHandlerType() == DataHandlerType::GPU);
}
AND_THEN("they should be equal") { REQUIRE(dcCPU == dcGPU); }
}
WHEN("copy-assigning a CPU to a GPU container")
{
dcGPU = dcCPU;
THEN("it should be a GPU container")
{
REQUIRE(dcGPU.getDataHandlerType() == DataHandlerType::CPU);
}
AND_THEN("they should be equal") { REQUIRE(dcCPU == dcGPU); }
}
}
}
#endif
SCENARIO("Testing iterators for DataContainer")
{
GIVEN("A 1D container")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment