Skip to content

Tidy MemoryRegion and introduce ReadOnly, WriteOnly, ReadWrite memory access qualifier

Issue/feature addressed

  • Added ReadOnly, WriteOnly, ReadWrite memory access qualifier to the GetPtr function
  • The copyRegion, copyArray, copyVector functions are now basically helper functions wrapped around the unified copyFrom function. Added HostToHost, HostToDevice, DeviceToHost, DeviceToDevice template parameter option to the copyFrom function to allow user to fully control memory copy to MemoryRegion. Default is set to HostToDevice to maintain backward compatibility.
  • Added the DeviceOnly, HostOnly, HostDevice template parameter to the initialize function to allow user to fully control MemoryRegion initialization.
  • Unify all MemoryRegionHost/MemoryRegionDevice constructor into a single one, taking one data pointer and a size parameter.
  • Unify all creation method in MemoryRegion into a fromData creator, one data pointer and a size parameter. The fromVector and fromArray are now simply "helper" functions that call fromData. Note, the creation of a MemoryRegion from a Array<OneD, Array<OneD, TDataIn> is not efficient, but the use of Array<OneD, Array<OneD, TDataIn> should be eventually deprecated.

TODO (Future MR?):

  • Add unit test for MemoryRegion

Proposed solution

  • If GetPtr is called with the ReadOnly memory access qualifier, then a const ptr is returned. Memory copy between host/device or device/host will occur, if necessary.
  • If GetPtr is called with the WriteOnly memory access qualifier, then a non-const ptr is returned. No-memory copy between host/device or device/host will occur.
  • If GetPtr is called with the ReadWrite memory access qualifier, then a non-const ptr is returned. Memory copy between host/device or device/host will occur, if necessary.

Implementation

Note: Most of the proposed implementation relies on pre-existing infrastructure. It combines the previous GetPtr and GetConstPtr metafunctions and add a new WriteOnly memory access.

  1. Introduce ReadOnly, WriteOnly, ReadWrite memory access qualifier within the MemoryQualifier namespace

class ReadOnly
{
};
class WriteOnly
{
};
class ReadWrite
{
};
  1. Introduce const_if metafunction. This metafunction allows the definition of a single GetPtr metafunction returning either a const TData * or a TData * data type depending of the template type parameter. The implementation is based on a possible implementation of the C++ native std::enable_if metafunction
template <bool B, class T = void> struct const_if
{
    typedef T type;
};

template <class T> struct const_if<true, T>
{
    typedef const T type;
};
  1. All const and non-const host/device pointers are now accessed using a single templated GetPtr metafunction:
    template <typename MemSpace,
              typename AccessQualifier = MemoryQualifier::ReadWrite>
    typename const_if<
        std::is_same<AccessQualifier, MemoryQualifier::ReadOnly>::value,
        TData>::type *
    GetPtr()

...

            if constexpr (std::is_same<AccessQualifier,
                                       MemoryQualifier::ReadOnly>::value)
            {
                return m_storage->GetHostConstPtr();
            }
            else if constexpr (std::is_same<AccessQualifier,
                                            MemoryQualifier::WriteOnly>::value)
            {
                return m_storage->GetHostPtr(true);
            }
            else if constexpr (std::is_same<AccessQualifier,
                                            MemoryQualifier::ReadWrite>::value)
            {
                return m_storage->GetHostPtr();
            }

...

                if constexpr (std::is_same<AccessQualifier,
                                           MemoryQualifier::ReadOnly>::value)
                {
                    return ret.GetDeviceConstPtr();
                }
                else if constexpr (std::is_same<
                                       AccessQualifier,
                                       MemoryQualifier::WriteOnly>::value)
                {
                    return ret.GetDevicePtr(true);
                }
                else if constexpr (std::is_same<
                                       AccessQualifier,
                                       MemoryQualifier::ReadWrite>::value)
                {
                    return ret.GetDevicePtr();
                }

...

Note: ReadWrite is the default access qualifier (this can help avoiding breaking to much legacy code, notably the unit tests)

  1. Within the GetHostPtr and GetDevicePtr functions (in MemoryRegionHost/MemoryRegionDevice), prevent unnecessary device/host or host/device data copy if the write_only flag is enable and set m_host_valid/m_device_valid flag to true.
    TData *GetHostPtr(bool write_only) override
    {
        if (write_only)
        {
            this->m_host_valid = true;
        }
        else
        {
            DeviceToHost(); // Move to host if necessary
        }

        m_device_valid = false;

        return this->m_host;
    }
    TData *GetDevicePtr(bool write_only = false)
    {
        if (write_only)
        {
            m_device_valid = true;
        }
        else
        {
            HostToDevice(); // Move to device if necessary
        }

        this->m_host_valid = false;

        return m_device;
    }
  1. To allow user to proper control memory initialization, MemoryWrite template parameter with DeviceOnly, HostOnly, HostDevice options has been added to the initialize function. The MemoryWrite template parameter is ignored for pure host (CPU) code.

Note: The default value is set to DeviceOnly to preserve backward compatibility.

    template <typename MemWrite = MemoryWrite::DeviceOnly>
    void initialize(TData val, size_t count = 0, size_t offset = 0)
    {

    ...

    }
  1. Added a MemoryCopy template parameter with option HostToHost, HostToDevice, DeviceToHost, DeviceToDevice to the copyFrom function to allow user to fully control memory copy to MemoryRegion.

Note: Default is set to HostToDevice to preserve backward compatibility.

    template <typename MemSpace, typename TDataIn = TData,
              typename MemCopy = MemoryCopy::HostToDevice>
    void copyFrom(const TDataIn *src, const size_t size,
                  const size_t offset = 0)
    {

    ...

    }

Example:

One example of application of the WriteOnly memory access is the BwdTrans operator. For the BwdTrans operator, the out field does not need to by copied from host to device. This can now be achieved the following way

    void apply(Field<TData, FieldState::Coeff> &in,
               Field<TData, FieldState::Phys> &out) override
    {
        // Initialize pointers.
        auto *inPtr = in.template GetPtr<MemSpace, MemoryQualifier::ReadOnly>();
        auto *outPtr =
            out.template GetPtr<MemSpace, MemoryQualifier::WriteOnly>();

...

Tests

Suggested reviewers

Notes

Please add any other information that could be useful for reviewers.

Checklist

  • Functions and classes, or changes to them, are documented.
  • [ ] User guide/documentation is updated.
  • [ ] Changelog is updated.
  • [ ] Suitable tests added for new functionality.
  • Contributed code is correctly formatted. (See the contributing guidelines).
  • [ ] License added to any new files.
  • No extraneous files have been added (e.g. compiler output or test data files).
Edited by Jacques Xing

Merge request reports

Loading