136 lines
3.8 KiB
C++
136 lines
3.8 KiB
C++
|
|
#ifndef LAPACK_CPP_MEMORY_BLOCK_HPP
|
|
#define LAPACK_CPP_MEMORY_BLOCK_HPP
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <type_traits>
|
|
|
|
#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 <typename T, typename idx_t, bool aligned = true>
|
|
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<double> A(10);
|
|
* double* data = A.ptr();
|
|
* data[0] = 1.0; // This is allowed
|
|
*/
|
|
template <typename T, typename idx_t = lapack_idx_t, bool aligned = true>
|
|
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<T, idx_t, aligned>(m) * n) : (calc_ld<T, idx_t, aligned>(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<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<T, idx_t, aligned>(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<T, idx_t, aligned>(calc_ld<T, idx_t, aligned>(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 |