#ifndef LAPACK_CPP_MEMORY_BLOCK_HPP #define LAPACK_CPP_MEMORY_BLOCK_HPP #include #include #include #include #include "lapack_cpp/base/types.hpp" namespace lapack_cpp { /** * Calculate the leading dimension of a matrix, given the number of rows. * Normally, the leading dimension can just be equal to to the number of rows, * but for performance reasons, we want to make sure that the leading dimension * is a multiple of 64 bytes. That way, if the first column is aligned, they all * are. * * @tparam T * @param m * @return int */ template inline constexpr idx_t calc_ld(const idx_t m) { if (aligned) return ((m - 1 + (64 / sizeof(T))) / (64 / sizeof(T))) * (64 / sizeof(T)); else return m; } /** * A class representing a contiguous block of memory of fixed size. * * This class owns the data. It is responsible for allocating and deallocating * the memory. * * A special note about const. Declaring a Memory block as const does not make * the data const. For example: * const MemoryBlock A(10); * double* data = A.ptr(); * data[0] = 1.0; // This is allowed */ template class MemoryBlock { public: // Constructor for a block of size n MemoryBlock(idx_t n) : n_(n), data_(aligned ? (T*)aligned_alloc(64, n_ * sizeof(T)) : (T*)malloc(n_ * sizeof(T))) { assert(n >= 0); #ifndef NDEBUG // When debugging, fill the memory with NaNs // This makes it easier to spot uninitialized memory std::fill(data_, data_ + n_, NAN); #endif } // Constructor for a block of sufficient size to store a matrix of size m x // n MemoryBlock(idx_t m, idx_t n, Layout layout = Layout::ColMajor) : n_(layout == Layout::ColMajor ? (calc_ld(m) * n) : (calc_ld(n) * m)), data_(aligned ? (T*)aligned_alloc(64, n_ * sizeof(T)) : (T*)malloc(n_ * sizeof(T))) { assert(m >= 0); assert(n >= 0); #ifndef NDEBUG // When debugging, fill the memory with NaNs // This makes it easier to spot uninitialized memory std::fill(data_, data_ + n_, NAN); #endif } // Non-owning constructor MemoryBlock(idx_t n, T* data) : n_(n), data_(data), owns_data_(false) { assert(n >= 0); } // Destructor ~MemoryBlock() { if (owns_data_) free((std::remove_const_t*)data_); } // Copy constructor MemoryBlock(const MemoryBlock& other) : n_(other.n_), data_(other.data_), owns_data_(false) {} // Assignment operator void operator=(const MemoryBlock& other) { assert(other.size() == this->size()); std::copy(other.data_, other.data_ + n_, data_); } // Memory block that remains after creating a vector of size n MemoryBlock remainder(idx_t n) const { idx_t size = calc_ld(n); assert(size <= n_); return MemoryBlock(n_ - n, data_ + n); } // Memory block that remains after creating a vector of size n MemoryBlock remainder(idx_t m, idx_t n) const { idx_t size = calc_ld(calc_ld(m) * n); assert(size <= n_); return MemoryBlock(n_ - size, data_ + size); } // Returns the size of the memory block inline idx_t size() const { return n_; } /** * Return a pointer to the data of the vector. */ inline T* ptr() const { return data_; } private: const idx_t n_; T* data_; const bool owns_data_ = true; }; } // namespace lapack_cpp #endif