Notice: If you are member of any public project or group, please make sure that your GitLab username is not the same as the LRZ identifier/Kennung (see https://gitlab.lrz.de/profile/account). Please change your username if necessary. For more information see the section "Public projects / Öffentliche Projekte" at https://doku.lrz.de/display/PUBLIC/GitLab . Thank you!

...
 
Commits (2)
......@@ -30,6 +30,12 @@ set(MODULE_SOURCES
add_library(${ELSA_MODULE_TARGET_NAME} ${MODULE_HEADERS} ${MODULE_SOURCES})
add_library(elsa::${ELSA_MODULE_NAME} ALIAS ${ELSA_MODULE_TARGET_NAME})
# use OpenMP is available
find_package(OpenMP REQUIRED)
if(OpenMP_CXX_FOUND)
target_link_libraries(${ELSA_MODULE_TARGET_NAME} PRIVATE OpenMP::OpenMP_CXX)
endif()
target_include_directories(${ELSA_MODULE_TARGET_NAME}
PUBLIC
$<INSTALL_INTERFACE:include/${ELSA_MODULE_NAME}>
......
......@@ -28,7 +28,7 @@ namespace elsa {
template <typename data_t>
DataContainer<data_t>::DataContainer(const DataContainer<data_t> &other)
: _dataDescriptor{other._dataDescriptor->clone()},
_dataHandler{other._dataHandler->clone()}
_dataHandler{other._dataHandler}
{}
template <typename data_t>
......@@ -36,7 +36,7 @@ namespace elsa {
{
if (this != &other) {
_dataDescriptor = other._dataDescriptor->clone();
_dataHandler = other._dataHandler->clone();
_dataHandler = other._dataHandler;
}
return *this;
......@@ -85,6 +85,7 @@ namespace elsa {
template<typename data_t>
data_t &DataContainer<data_t>::operator[](index_t index)
{
detach();
return (*_dataHandler)[index];
}
......@@ -97,6 +98,7 @@ namespace elsa {
template<typename data_t>
data_t &DataContainer<data_t>::operator()(IndexVector_t coordinate)
{
detach();
return (*_dataHandler)[_dataDescriptor->getIndexFromCoordinate(coordinate)];
}
......@@ -166,6 +168,7 @@ namespace elsa {
template <typename data_t>
DataContainer<data_t>& DataContainer<data_t>::operator+=(const DataContainer<data_t>& dc)
{
detach();
*_dataHandler += *dc._dataHandler;
return *this;
}
......@@ -173,6 +176,7 @@ namespace elsa {
template <typename data_t>
DataContainer<data_t>& DataContainer<data_t>::operator-=(const DataContainer<data_t>& dc)
{
detach();
*_dataHandler -= *dc._dataHandler;
return *this;
}
......@@ -180,6 +184,7 @@ namespace elsa {
template <typename data_t>
DataContainer<data_t>& DataContainer<data_t>::operator*=(const DataContainer<data_t>& dc)
{
detach();
*_dataHandler *= *dc._dataHandler;
return *this;
}
......@@ -187,6 +192,7 @@ namespace elsa {
template <typename data_t>
DataContainer<data_t>& DataContainer<data_t>::operator/=(const DataContainer<data_t>& dc)
{
detach();
*_dataHandler /= *dc._dataHandler;
return *this;
}
......@@ -195,6 +201,7 @@ namespace elsa {
template <typename data_t>
DataContainer<data_t>& DataContainer<data_t>::operator+=(data_t scalar)
{
detach();
*_dataHandler += scalar;
return *this;
}
......@@ -202,6 +209,7 @@ namespace elsa {
template <typename data_t>
DataContainer<data_t>& DataContainer<data_t>::operator-=(data_t scalar)
{
detach();
*_dataHandler -= scalar;
return *this;
}
......@@ -209,6 +217,7 @@ namespace elsa {
template <typename data_t>
DataContainer<data_t>& DataContainer<data_t>::operator*=(data_t scalar)
{
detach();
*_dataHandler *= scalar;
return *this;
}
......@@ -216,6 +225,7 @@ namespace elsa {
template <typename data_t>
DataContainer<data_t>& DataContainer<data_t>::operator/=(data_t scalar)
{
detach();
*_dataHandler /= scalar;
return *this;
}
......@@ -224,6 +234,7 @@ namespace elsa {
template <typename data_t>
DataContainer<data_t>& DataContainer<data_t>::operator=(data_t scalar)
{
detach();
*_dataHandler = scalar;
return *this;
}
......@@ -269,6 +280,17 @@ namespace elsa {
return !(*this == other);
}
template <typename data_t>
void DataContainer<data_t>::detach()
{
if (_dataHandler.use_count() != 1) {
#pragma omp barrier
#pragma omp single
_dataHandler = _dataHandler->clone();
}
return;
}
// ------------------------------------------
// explicit template instantiation
template class DataContainer<float>;
......
......@@ -23,6 +23,9 @@ namespace elsa
* This class provides a container for a signal that is stored in memory. This signal can
* be n-dimensional, and will be stored in memory in a linearized fashion. The information
* on how this linearization is performed is provided by an associated DataDescriptor.
*
* The class implements copy-on-write. Therefore any non-const functions should call the
* detach() function first to trigger the copy-on-write mechanism.
*/
template <typename data_t = real_t>
class DataContainer {
......@@ -88,7 +91,6 @@ namespace elsa
*/
DataContainer<data_t>& operator=(DataContainer<data_t>&& other);
/// return the current DataDescriptor
const DataDescriptor& getDataDescriptor() const;
......@@ -191,12 +193,11 @@ namespace elsa
/// comparison with another DataContainer
bool operator!=(const DataContainer<data_t>& other) const;
private:
/// the current DataDescriptor
std::unique_ptr<DataDescriptor> _dataDescriptor;
/// the current DataHandler
std::unique_ptr<DataHandler<data_t>> _dataHandler;
std::shared_ptr<DataHandler<data_t>> _dataHandler;
/// factory method to create DataHandlers based on handlerType with perfect forwarding of constructor arguments
template <typename ... Args>
......@@ -204,6 +205,9 @@ namespace elsa
/// private constructor accepting a DataDescriptor and a DataHandler
explicit DataContainer(const DataDescriptor& dataDescriptor, std::unique_ptr<DataHandler<data_t>> dataHandler);
/// creates the deep copy for the copy-on-write mechanism
void detach();
};
......
......@@ -371,6 +371,112 @@ SCENARIO("Testing the arithmetic operations with DataContainer arguments") {
REQUIRE(resultDivScalar[i] == dc[i] / scalar);
}
}
}
SCENARIO("Testing the copy-on-write mechanism") {
GIVEN("two random DataContainers and a copy constructed one") {
IndexVector_t numCoeff(3);
numCoeff << 52, 7, 29;
DataDescriptor desc(numCoeff);
DataContainer dc(desc);
DataContainer dc2(desc);
Eigen::VectorXf randVec = Eigen::VectorXf::Random(dc.getSize());
Eigen::VectorXf randVec2 = Eigen::VectorXf::Random(dc.getSize());
for (index_t i = 0; i < dc.getSize(); ++i) {
dc[i] = randVec(i);
dc2[i] = randVec2(i);
}
DataContainer dc3(dc);
WHEN("copy-on-write is not invoked yet") {
dc + dc3;
dc.sum();
dc.square();
dc.log();
dc.dot(dc2);
dc.l1Norm();
THEN("the data should still be the same") {
REQUIRE(dc3 == dc);
}
}
WHEN("manipulating one DataContainer invokes copy-on-write") {
dc3 += 2;
THEN("DataContainer are different") {
REQUIRE(dc3 != dc);
}
}
WHEN("manipulating one DataContainer invokes copy-on-write") {
dc3 += dc2;
THEN("DataContainer are different") {
REQUIRE(dc3 != dc);
}
}
WHEN("manipulating one DataContainer invokes copy-on-write") {
dc3 -= 2;
THEN("DataContainer are different") {
REQUIRE(dc3 != dc);
}
}
WHEN("manipulating one DataContainer invokes copy-on-write") {
dc3 -= dc2;
THEN("DataContainer are different") {
REQUIRE(dc3 != dc);
}
}
WHEN("manipulating one DataContainer invokes copy-on-write") {
dc3 /= 2;
THEN("DataContainers are different") {
REQUIRE(dc3 != dc);
}
}
WHEN("manipulating one DataContainer invokes copy-on-write") {
dc3 /= dc2;
THEN("DataContainers are different") {
REQUIRE(dc3 != dc);
}
}
WHEN("manipulating one DataContainer invokes copy-on-write") {
dc3 *= 2;
THEN("DataContainers are different") {
REQUIRE(dc3 != dc);
}
}
WHEN("manipulating one DataContainer invokes copy-on-write") {
dc3 *= 2;
THEN("DataContainers are different") {
REQUIRE(dc3 != dc);
}
}
WHEN("accessing a non-const reference through the [] operator invokes copy-on-write") {
dc[0] += 2;
THEN("DataContainers are different") {
REQUIRE(dc3 != dc);
}
}
}
}
\ No newline at end of file
......@@ -465,7 +465,7 @@ SCENARIO("Calls to functions of super class") {
THEN("Results do not change (may still be slightly different due to summation being performed in a different order)") {
op.apply(volume, sino);
opClone->apply(volume, sinoClone);
REQUIRE(sino == sinoClone);
REQUIRE((sino-sinoClone).squaredL2Norm() == Approx(0.0).margin(1e-5));
op.applyAdjoint(sino, volume);
opClone->applyAdjoint(sino, volumeClone);
......